@akanjs/cli 3.1.0-rc.0 → 3.1.0-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/.build-stamp +1 -1
- package/{CodeAgent-q7a4srt6.js → CodeAgent-q50k4s45.js} +2 -2
- package/{CodeTui-ak23dxc9.js → CodeTui-62g35ztk.js} +1 -1
- package/TunnelShare-f1egrktx.js +13 -0
- package/{agent.command-f3gsc1gd.js → agent.command-adfd5299.js} +5 -5
- package/akanCode.js +2 -2
- package/{application.command-3v2d53gd.js → application.command-hj3gv1rx.js} +52 -41
- package/{applicationBuildRunner-1yaqsrdq.js → applicationBuildRunner-by10r9gf.js} +16 -30
- package/buildBatch.proc.js +22 -13
- package/{cloud.command-kk2rwkvw.js → cloud.command-k2fhaape.js} +60 -63
- package/cloud.script-z8p5dz7e.js +23 -0
- package/{code.command-30sca09d.js → code.command-kt5zgcc2.js} +6 -6
- package/{context.command-k7eqacq6.js → context.command-8fgn42jw.js} +16 -13
- package/context.runner-hnh0rg7m.js +24 -0
- package/{guideline.command-dtczrqpf.js → guideline.command-3yxxr2mm.js} +2 -2
- package/guidelines/conventions/conventions.instruction.md +30 -15
- package/guidelines/cssRule/cssRule.instruction.md +12 -6
- package/guidelines/mcpRule/mcpRule.instruction.md +32 -11
- package/guidelines/runtimeRule/runtimeRule.instruction.md +24 -15
- package/guidelines/transportRule/transportRule.instruction.md +28 -5
- package/incrementalBuilder.proc.js +9 -6
- package/{index-kjvrq3nc.js → index-0909wckt.js} +361 -282
- package/{index-p9ystjrp.js → index-20cebtth.js} +92 -52
- package/{index-55mynnqs.js → index-2etqq75t.js} +455 -206
- package/{index-6jjtnhkw.js → index-31ce9vhg.js} +51 -9
- package/{index-y8q31011.js → index-3reaaa1h.js} +42 -153
- package/{index-ww9bhp67.js → index-4pf8g7ff.js} +1 -1
- package/{index-qt22xstj.js → index-58vvz603.js} +5 -3
- package/index-6726qr1m.js +668 -0
- package/index-8935h3n1.js +33 -0
- package/{index-nz15k0b6.js → index-8zdwpxef.js} +1 -1
- package/{index-c2yrsxdh.js → index-azh29dc8.js} +96 -320
- package/{index-c1x0be5j.js → index-bea2gwc6.js} +2 -2
- package/{index-v29746vb.js → index-e9prnmyt.js} +3 -3
- package/{index-tn4rpqp1.js → index-fjsv10hd.js} +73 -49
- package/index-g0vwzy9m.js +241 -0
- package/{index-c76k723e.js → index-h85cxfkg.js} +2 -2
- package/{index-rwq3ekqc.js → index-hnby7p9n.js} +7 -7
- package/{index-2etcv0sc.js → index-jwqtchha.js} +1 -1
- package/index-kc8z134d.js +248 -0
- package/index-nq045px5.js +1124 -0
- package/{index-fm46xrf1.js → index-nq2ja5e0.js} +3 -3
- package/{index-0bm04n5g.js → index-pyxn1b53.js} +10 -8
- package/{index-05g3jayg.js → index-rhbabmja.js} +1 -1
- package/{index-2mx692qq.js → index-rk9sdys3.js} +2 -2
- package/{index-7bymavqk.js → index-sby8fe1p.js} +1 -1
- package/{index-ctanya41.js → index-sqfdmb5b.js} +4 -4
- package/{index-2t4tqd1c.js → index-t7nws9mx.js} +3 -3
- package/{index-jyv387qw.js → index-tv8fjaba.js} +4 -4
- package/{index-gnyf1fqy.js → index-tz5evhdr.js} +2 -2
- package/{index-aprt7pkz.js → index-xpdgxsj5.js} +229 -1327
- package/{index-e0cvze1s.js → index-z507nzhg.js} +4 -4
- package/index.js +22 -22
- package/{library.command-gaga5bmq.js → library.command-8tw59xzj.js} +3 -3
- package/{localRegistry.command-9e84e32q.js → localRegistry.command-tf3dmznd.js} +12 -9
- package/{module.command-7vpf6db4.js → module.command-9vm8hffy.js} +5 -5
- package/{package.command-twmvvf69.js → package.command-erbxe2tz.js} +3 -3
- package/package.json +2 -2
- package/{page.command-5asq7fdf.js → page.command-jpavcj0p.js} +3 -3
- package/{pipeline.command-d0v38572.js → pipeline.command-b8rzvjvv.js} +220 -219
- package/{primitive.command-dyrs2fm8.js → primitive.command-sm32chzk.js} +6 -6
- package/{projectTypecheck-cvtkrzp2.js → projectTypecheck-jamtx95x.js} +1 -1
- package/{quality.command-6emsqpbg.js → quality.command-pkd17n6y.js} +2 -2
- package/{repair.command-8t6aegv5.js → repair.command-8y4mq6hs.js} +5 -5
- package/{scalar.command-bnggy1x7.js → scalar.command-q8khmyx5.js} +4 -4
- package/{subspace.command-f02j5tge.js → subspace.command-51n0ykbx.js} +421 -362
- package/templates/app/env/env.client.type.ts +2 -3
- package/templates/app/page/styles.css.template +8 -0
- package/{tunnel.command-9x8nfn75.js → tunnel.command-7820vtxq.js} +7 -4
- package/{workflow.command-jx0wag5r.js → workflow.command-n5sv4wqz.js} +10 -10
- package/{workspace.command-cmj6mqyk.js → workspace.command-zdy60jh8.js} +33 -30
- package/TunnelShare-21mm6dr5.js +0 -9
- package/cloud.script-r7eyfey4.js +0 -20
- package/context.runner-hqd1ppqe.js +0 -21
- package/index-6w0rbr9d.js +0 -68
- package/index-r2tpeb3n.js +0 -392
|
@@ -4,94 +4,336 @@ import {
|
|
|
4
4
|
FileSys2
|
|
5
5
|
} from "./index-52w0fvh8.js";
|
|
6
6
|
|
|
7
|
-
// pkgs/@akanjs/devkit/
|
|
7
|
+
// pkgs/@akanjs/devkit/typecheck/projectTypecheck.ts
|
|
8
|
+
import { mkdir, rm } from "fs/promises";
|
|
8
9
|
import path from "path";
|
|
9
|
-
|
|
10
|
+
|
|
11
|
+
class ProjectTypecheck2 {
|
|
12
|
+
projectDir;
|
|
13
|
+
workspaceRoot;
|
|
14
|
+
env;
|
|
15
|
+
static #testIncludes = ["**/*.test.ts", "**/*.test.tsx", "**/*.spec.ts", "**/*.spec.tsx"];
|
|
16
|
+
static #excludes = [".akan/**", "node_modules/**", "local/**"];
|
|
17
|
+
constructor(projectDir, workspaceRoot, env) {
|
|
18
|
+
this.projectDir = projectDir;
|
|
19
|
+
this.workspaceRoot = workspaceRoot;
|
|
20
|
+
this.env = env;
|
|
21
|
+
}
|
|
22
|
+
get typecheckDir() {
|
|
23
|
+
return path.join(this.projectDir, ".akan", "typecheck");
|
|
24
|
+
}
|
|
25
|
+
async run(entries, { clean = false, incremental = true, tests = false } = {}) {
|
|
26
|
+
const name = tests ? "tsconfig.tests" : "tsconfig";
|
|
27
|
+
await mkdir(this.typecheckDir, { recursive: true });
|
|
28
|
+
const tsconfig = {
|
|
29
|
+
extends: "../../tsconfig.json",
|
|
30
|
+
compilerOptions: { noEmit: true, incremental, tsBuildInfoFile: `./${name}.tsbuildinfo` },
|
|
31
|
+
include: [...entries, ...tests ? ProjectTypecheck2.#testIncludes.map((glob) => `../../${glob}`) : []],
|
|
32
|
+
...tests ? { exclude: ProjectTypecheck2.#excludes.map((glob) => `../../${glob}`) } : {},
|
|
33
|
+
references: []
|
|
34
|
+
};
|
|
35
|
+
const tsconfigPath = path.join(this.typecheckDir, `${name}.json`);
|
|
36
|
+
await Bun.write(tsconfigPath, `${JSON.stringify(tsconfig, null, 2)}
|
|
37
|
+
`);
|
|
38
|
+
if (clean)
|
|
39
|
+
await rm(path.join(this.typecheckDir, `${name}.tsbuildinfo`), { force: true });
|
|
40
|
+
await this.#check(tsconfigPath);
|
|
41
|
+
return tsconfigPath;
|
|
42
|
+
}
|
|
43
|
+
async#check(tsconfigPath) {
|
|
44
|
+
const proc = Bun.spawn([process.execPath, await ProjectTypecheck2.workerEntry(this.workspaceRoot)], {
|
|
45
|
+
cwd: this.workspaceRoot,
|
|
46
|
+
env: { ...this.env, AKAN_COMMAND_TYPE: "typecheck", AKAN_TYPECHECK_TSCONFIG: tsconfigPath },
|
|
47
|
+
stdout: "pipe",
|
|
48
|
+
stderr: "pipe"
|
|
49
|
+
});
|
|
50
|
+
const [stdout, stderr, exitCode] = await Promise.all([
|
|
51
|
+
new Response(proc.stdout).text(),
|
|
52
|
+
new Response(proc.stderr).text(),
|
|
53
|
+
proc.exited
|
|
54
|
+
]);
|
|
55
|
+
if (exitCode !== 0)
|
|
56
|
+
throw new Error((stderr || stdout).trim() || `Typecheck failed with exit code ${exitCode}`);
|
|
57
|
+
}
|
|
58
|
+
static async workerEntry(workspaceRoot) {
|
|
59
|
+
const candidates = [
|
|
60
|
+
path.join(workspaceRoot, "pkgs/@akanjs/devkit/typecheck/typecheck.proc.ts"),
|
|
61
|
+
path.join(workspaceRoot, "node_modules/@akanjs/devkit/typecheck/typecheck.proc.ts"),
|
|
62
|
+
path.join(import.meta.dir, "typecheck.proc.js"),
|
|
63
|
+
path.join(import.meta.dir, "typecheck.proc.ts"),
|
|
64
|
+
path.join(import.meta.dir, "..", "typecheck.proc.js")
|
|
65
|
+
];
|
|
66
|
+
for (const candidate of candidates)
|
|
67
|
+
if (await Bun.file(candidate).exists())
|
|
68
|
+
return candidate;
|
|
69
|
+
throw new Error(`[devkit] typecheck worker entry not found; looked in: ${candidates.join(", ")}`);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// pkgs/@akanjs/devkit/transforms/serverEnvProfile.ts
|
|
74
|
+
import { existsSync, realpathSync } from "fs";
|
|
75
|
+
import path2 from "path";
|
|
76
|
+
import { Logger, toError } from "akanjs/common";
|
|
10
77
|
|
|
11
78
|
class ServerEnvProfile {
|
|
12
79
|
static namePattern = /^[a-z][a-zA-Z0-9-]*$/;
|
|
80
|
+
static envFilePattern = /[\\/]env[\\/]env\.server\.[A-Za-z0-9-]+\.ts$/;
|
|
13
81
|
static fileName(environment, profile) {
|
|
14
82
|
return `env.server.${environment}.${profile}.ts`;
|
|
15
83
|
}
|
|
16
84
|
static profileFileOf(file, profile) {
|
|
17
85
|
return file.replace(/\.ts$/, `.${profile}.ts`);
|
|
18
86
|
}
|
|
19
|
-
static
|
|
20
|
-
if (
|
|
21
|
-
return
|
|
22
|
-
|
|
23
|
-
return
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
const result = ServerEnvProfile.strip(child, at ? `${at}.${key}` : key, underSecret || secretKey.test(key));
|
|
27
|
-
stripped.push(...result.stripped);
|
|
28
|
-
return result.value;
|
|
29
|
-
};
|
|
30
|
-
if (Array.isArray(value))
|
|
31
|
-
return { value: value.map((child, idx) => strip(String(idx), child)), stripped };
|
|
32
|
-
const kept = Object.fromEntries(Object.entries(value).map(([key, child]) => [key, strip(key, child)]).filter(([, child]) => child !== undefined));
|
|
33
|
-
return { value: kept, stripped };
|
|
34
|
-
}
|
|
35
|
-
static secretValues(value, at = "", underSecret = false) {
|
|
36
|
-
if (typeof value === "string") {
|
|
37
|
-
const named = secretKey.test(at.split(".").at(-1) ?? "");
|
|
38
|
-
const distinct = value.length >= 16 && !/^https?:\/\//.test(value) && !/^[a-z]+(?:[._-][a-z]+)*$/.test(value);
|
|
39
|
-
return underSecret && value.length >= 8 && (named || distinct) ? [{ path: at, value }] : [];
|
|
87
|
+
static #url(value) {
|
|
88
|
+
if (!/^[a-z][a-z0-9+.-]*:\/\//i.test(value))
|
|
89
|
+
return null;
|
|
90
|
+
try {
|
|
91
|
+
return new URL(value);
|
|
92
|
+
} catch {
|
|
93
|
+
return null;
|
|
40
94
|
}
|
|
41
|
-
if (!value || typeof value !== "object")
|
|
42
|
-
return [];
|
|
43
|
-
return Object.entries(value).flatMap(([key, child]) => ServerEnvProfile.secretValues(child, at ? `${at}.${key}` : key, underSecret || secretKey.test(key)));
|
|
44
95
|
}
|
|
45
|
-
static
|
|
96
|
+
static #carriesCredential(url) {
|
|
97
|
+
return !!url.password || [...url.searchParams.keys()].some((name) => Logger.isSecretKey(name));
|
|
98
|
+
}
|
|
99
|
+
static #isCredentialUrl(value) {
|
|
100
|
+
const url = ServerEnvProfile.#url(value);
|
|
101
|
+
return !!url && ServerEnvProfile.#carriesCredential(url);
|
|
102
|
+
}
|
|
103
|
+
static #isSecret(value, underSecret) {
|
|
104
|
+
return underSecret && !!value.trim() || ServerEnvProfile.#isCredentialUrl(value);
|
|
105
|
+
}
|
|
106
|
+
static #fold(value, fold, at = "", underSecret = false) {
|
|
46
107
|
if (typeof value === "string")
|
|
47
|
-
return
|
|
108
|
+
return fold.text(value, at, underSecret);
|
|
48
109
|
if (!value || typeof value !== "object")
|
|
49
|
-
return
|
|
50
|
-
return Object.entries(value).
|
|
110
|
+
return fold.other(value);
|
|
111
|
+
return fold.node(value, Object.entries(value).map(([key, child]) => [
|
|
112
|
+
key,
|
|
113
|
+
ServerEnvProfile.#fold(child, fold, at ? `${at}.${key}` : key, underSecret || Logger.isSecretKey(key))
|
|
114
|
+
]));
|
|
115
|
+
}
|
|
116
|
+
static #collect(text) {
|
|
117
|
+
return { text, other: () => [], node: (_, children) => children.flatMap(([, found]) => found) };
|
|
118
|
+
}
|
|
119
|
+
static strip(value) {
|
|
120
|
+
return ServerEnvProfile.#fold(value, {
|
|
121
|
+
text: (text, at, underSecret) => ServerEnvProfile.#isSecret(text, underSecret) ? { value: undefined, stripped: [at] } : { value: text, stripped: [] },
|
|
122
|
+
other: (other) => ({ value: other, stripped: [] }),
|
|
123
|
+
node: (node, children) => {
|
|
124
|
+
const stripped = children.flatMap(([, result]) => result.stripped);
|
|
125
|
+
if (Array.isArray(node))
|
|
126
|
+
return {
|
|
127
|
+
value: children.flatMap(([, result]) => result.value === undefined || result.stripped.length && typeof result.value === "object" && !Object.keys(result.value ?? {}).length ? [] : [result.value]),
|
|
128
|
+
stripped
|
|
129
|
+
};
|
|
130
|
+
return {
|
|
131
|
+
value: Object.fromEntries(children.flatMap(([key, result]) => result.value === undefined ? [] : [[key, result.value]])),
|
|
132
|
+
stripped
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
});
|
|
51
136
|
}
|
|
52
|
-
static
|
|
137
|
+
static secretValues(value) {
|
|
138
|
+
return ServerEnvProfile.#fold(value, ServerEnvProfile.#collect((text, at, underSecret) => {
|
|
139
|
+
const url = ServerEnvProfile.#url(text);
|
|
140
|
+
if (url)
|
|
141
|
+
return ServerEnvProfile.#carriesCredential(url) || underSecret && (/webhook/i.test(at) || ServerEnvProfile.distinctive(text)) ? [...new Set([text, url.password])].filter((part) => part.length >= 8).map((part) => ({ path: at, value: part })) : [];
|
|
142
|
+
const named = Logger.isSecretKey(at.split(".").at(-1) ?? "");
|
|
143
|
+
return underSecret && text.length >= 8 && (named || ServerEnvProfile.distinctive(text)) ? [{ path: at, value: text }] : [];
|
|
144
|
+
}));
|
|
145
|
+
}
|
|
146
|
+
static distinctive(value) {
|
|
147
|
+
const url = ServerEnvProfile.#url(value);
|
|
148
|
+
if (url)
|
|
149
|
+
return ServerEnvProfile.#carriesCredential(url) || [...url.pathname.split("/"), ...url.searchParams.values()].some((part) => ServerEnvProfile.distinctive(part));
|
|
150
|
+
return value.length >= 16 && !/^https?:\/\//.test(value) && !/^[a-z]+(?:[._-][a-z]+)*$/.test(value);
|
|
151
|
+
}
|
|
152
|
+
static secretPaths(value) {
|
|
153
|
+
return ServerEnvProfile.#fold(value, ServerEnvProfile.#collect((text, at, underSecret) => ServerEnvProfile.#isSecret(text, underSecret) ? [at] : []));
|
|
154
|
+
}
|
|
155
|
+
static async evaluate(file, cwd, load) {
|
|
156
|
+
return (await ServerEnvProfile.#evaluated(file, cwd, load)).value;
|
|
157
|
+
}
|
|
158
|
+
static async#evaluated(file, cwd, load) {
|
|
159
|
+
const pattern = (regex, flags = "") => `new RegExp(${JSON.stringify(regex.source)}, "${flags}")`;
|
|
160
|
+
const literal = /(["'`])((?:\\[\s\S]|(?!\1)[^\\])*)\1/;
|
|
161
|
+
const frame = /^\s*at (?:[^(]*\()?(.+):(\d+):(\d+)\)?\s*$/;
|
|
53
162
|
const script = [
|
|
54
|
-
"const
|
|
55
|
-
'
|
|
56
|
-
"
|
|
163
|
+
"const waiting = new Map();",
|
|
164
|
+
'process.on("message", ({ load, error, loader, contents }) => {',
|
|
165
|
+
" const reply = waiting.get(load);",
|
|
166
|
+
" waiting.delete(load);",
|
|
167
|
+
" if (error === undefined) reply?.resolve({ loader, contents });",
|
|
168
|
+
" else reply?.reject(new Error(error));",
|
|
169
|
+
"});",
|
|
170
|
+
"if (process.env.AKAN_PROFILE_ENV_LOAD)",
|
|
171
|
+
" Bun.plugin({",
|
|
172
|
+
' name: "akan-server-env",',
|
|
173
|
+
" setup(build) {",
|
|
174
|
+
" build.onLoad({ filter: new RegExp(process.env.AKAN_PROFILE_ENV_LOAD) }, (args) =>",
|
|
175
|
+
" new Promise((resolve, reject) => {",
|
|
176
|
+
" waiting.set(args.path, { resolve, reject });",
|
|
177
|
+
" process.send({ load: args.path });",
|
|
178
|
+
" }));",
|
|
179
|
+
" },",
|
|
180
|
+
" });",
|
|
181
|
+
`const literal = ${pattern(literal, "g")};`,
|
|
182
|
+
`const interpolation = ${pattern(/\$\{[^}]*\}/)};`,
|
|
183
|
+
`const frameOf = ${pattern(frame)};`,
|
|
184
|
+
`const evaluating = ${pattern(/\s*\(evaluating '[\s\S]*'\)\s*$/)};`,
|
|
185
|
+
"const literalsOf = async (file) =>",
|
|
186
|
+
' [...(await Bun.file(file).text().catch(() => "")).matchAll(literal)].flatMap((match) => match[2].split(interpolation));',
|
|
187
|
+
"const describe = async (error) => {",
|
|
188
|
+
" const first = Array.isArray(error?.errors) && error.errors.length ? error.errors[0] : error;",
|
|
189
|
+
' const frame = String(first?.stack ?? "").split("\\n").map((line) => frameOf.exec(line)).find(Boolean);',
|
|
190
|
+
" const at = first?.position?.file ? [first.position.file, first.position.line, first.position.column] : frame ? frame.slice(1, 4) : [];",
|
|
191
|
+
" const files = [process.env.AKAN_PROFILE_ENV_FILE, at[0]].filter(Boolean);",
|
|
192
|
+
" const literals = (await Promise.all(files.map(literalsOf))).flat().filter((part) => part.length >= 4);",
|
|
193
|
+
' let message = String(first?.message ?? first).replace(evaluating, "");',
|
|
194
|
+
" for (const part of literals.sort((a, b) => b.length - a.length))",
|
|
195
|
+
' message = message.split(part).join("[redacted]");',
|
|
196
|
+
' const name = typeof first?.name === "string" && /^[A-Za-z]+$/.test(first.name) ? first.name : "Error";',
|
|
197
|
+
' return name + ": " + message.slice(0, 200) + (at.length ? " (at " + at.join(":") + ")" : "");',
|
|
198
|
+
"};",
|
|
199
|
+
"try {",
|
|
200
|
+
" const { env } = await import(process.env.AKAN_PROFILE_ENV_FILE);",
|
|
201
|
+
" const lossy = [];",
|
|
202
|
+
" const walk = (value, at) => {",
|
|
203
|
+
' if (typeof value === "function" || typeof value === "symbol" || typeof value === "bigint" || (typeof value === "number" && !Number.isFinite(value))) return lossy.push(at);',
|
|
204
|
+
' if (!value || typeof value !== "object") return;',
|
|
205
|
+
" const proto = Object.getPrototypeOf(value);",
|
|
206
|
+
" if (!Array.isArray(value) && proto !== Object.prototype && proto !== null) return lossy.push(at);",
|
|
207
|
+
' for (const [key, child] of Object.entries(value)) walk(child, at ? at + "." + key : key);',
|
|
208
|
+
" };",
|
|
209
|
+
' walk(env, "");',
|
|
210
|
+
' const plain = (_, value) => (typeof value === "function" || typeof value === "symbol" ? undefined : typeof value === "bigint" ? String(value) : value);',
|
|
211
|
+
' process.send({ env: JSON.stringify(env ?? null, plain) ?? "null", lossy }, undefined, undefined, () => process.disconnect());',
|
|
212
|
+
"} catch (error) {",
|
|
213
|
+
" process.exitCode = 1;",
|
|
214
|
+
" process.send({ failure: await describe(error) }, undefined, undefined, () => process.disconnect());",
|
|
215
|
+
"}"
|
|
57
216
|
].join(`
|
|
58
217
|
`);
|
|
218
|
+
let sent;
|
|
219
|
+
let failure;
|
|
220
|
+
let loadError;
|
|
59
221
|
const child = Bun.spawn([process.execPath, "--eval", script], {
|
|
60
222
|
cwd,
|
|
61
|
-
env: {
|
|
62
|
-
|
|
63
|
-
|
|
223
|
+
env: {
|
|
224
|
+
PATH: process.env.PATH ?? "",
|
|
225
|
+
HOME: process.env.HOME ?? "",
|
|
226
|
+
AKAN_PROFILE_ENV_FILE: file,
|
|
227
|
+
...load ? { AKAN_PROFILE_ENV_LOAD: ServerEnvProfile.envFilePattern.source } : {}
|
|
228
|
+
},
|
|
229
|
+
stdout: "ignore",
|
|
230
|
+
stderr: "ignore",
|
|
231
|
+
serialization: "json",
|
|
232
|
+
ipc: (message, subprocess) => {
|
|
233
|
+
if ("env" in message) {
|
|
234
|
+
sent = message;
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
if ("failure" in message) {
|
|
238
|
+
failure = message.failure;
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
241
|
+
if (!load)
|
|
242
|
+
return;
|
|
243
|
+
const requested = message.load;
|
|
244
|
+
load(requested).then(({ loader, contents }) => ({ load: requested, loader, contents }), (error) => {
|
|
245
|
+
loadError ??= error;
|
|
246
|
+
return { load: requested, error: toError(error).message };
|
|
247
|
+
}).then((reply) => subprocess.send(reply)).catch(() => {
|
|
248
|
+
return;
|
|
249
|
+
});
|
|
250
|
+
}
|
|
64
251
|
});
|
|
65
|
-
const
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
252
|
+
const exitCode = await child.exited;
|
|
253
|
+
if (exitCode !== 0 || !sent) {
|
|
254
|
+
if (loadError)
|
|
255
|
+
throw loadError;
|
|
256
|
+
throw new Error(`${path2.basename(file)} could not be evaluated to check it for secrets: ${failure ?? `its process exited with code ${exitCode} and sent nothing`}`);
|
|
257
|
+
}
|
|
258
|
+
return { value: JSON.parse(sent.env), lossy: sent.lossy };
|
|
259
|
+
}
|
|
260
|
+
static async sourceInPlaceOf(profileFile, replacedFile, profile) {
|
|
261
|
+
const contents = await Bun.file(profileFile).text();
|
|
262
|
+
const dir = path2.dirname(profileFile);
|
|
263
|
+
const replaced = ServerEnvProfile.#real(replacedFile);
|
|
264
|
+
const selfImports = new Bun.Transpiler({ loader: "ts" }).scanImports(contents).flatMap(({ path: specifier }) => {
|
|
265
|
+
try {
|
|
266
|
+
return ServerEnvProfile.#real(Bun.resolveSync(specifier, dir)) === replaced ? [specifier] : [];
|
|
267
|
+
} catch {
|
|
268
|
+
return [];
|
|
269
|
+
}
|
|
270
|
+
});
|
|
271
|
+
if (selfImports.length)
|
|
272
|
+
throw new Error(`${path2.basename(profileFile)} imports ${selfImports.join(", ")}, the file a "${profile}" build bundles it in place of, so it would import itself. Write the values it needs into it instead.`);
|
|
273
|
+
return contents;
|
|
274
|
+
}
|
|
275
|
+
static #real(file) {
|
|
276
|
+
return existsSync(file) ? realpathSync.native(file) : path2.resolve(file);
|
|
277
|
+
}
|
|
278
|
+
static async libEnvSource(file, profile, chain = []) {
|
|
279
|
+
if (chain.includes(file))
|
|
280
|
+
throw new Error(`${[...chain, file].join(" -> ")}: lib env files reach each other, which a "${profile}" build cannot swap.`);
|
|
281
|
+
const profileFile = ServerEnvProfile.profileFileOf(file, profile);
|
|
282
|
+
const libRoot = path2.dirname(path2.dirname(file));
|
|
283
|
+
if (await Bun.file(profileFile).exists()) {
|
|
284
|
+
const contents = await ServerEnvProfile.sourceInPlaceOf(profileFile, file, profile);
|
|
285
|
+
const reached = [...chain, file];
|
|
286
|
+
const value = await ServerEnvProfile.evaluate(profileFile, libRoot, (envFile) => ServerEnvProfile.#underProfile(envFile, profile, new Set, reached));
|
|
287
|
+
const secrets = ServerEnvProfile.secretPaths(value);
|
|
288
|
+
if (secrets.length)
|
|
289
|
+
throw new Error(`${profileFile} holds secret-named values, which the "${profile}" bundle carries to every computer it runs on: ${secrets.join(", ")}.`);
|
|
290
|
+
return { loader: "ts", contents, profileFile: true, stripped: [] };
|
|
291
|
+
}
|
|
292
|
+
const { exports } = new Bun.Transpiler({ loader: "ts" }).scan(await Bun.file(file).text());
|
|
293
|
+
if (exports.some((name) => name !== "env"))
|
|
294
|
+
throw new Error(`${file} exports ${exports.join(", ")}: a "${profile}" build carries only a lib env's \`env\`, so give the lib ${path2.basename(profileFile)}.`);
|
|
295
|
+
const evaluated = await ServerEnvProfile.#evaluated(file, libRoot);
|
|
296
|
+
if (evaluated.lossy.length)
|
|
297
|
+
throw new Error(`${file} holds ${evaluated.lossy.join(", ")}, which a "${profile}" build cannot carry: it bundles a lib env's defaults as JSON, which keeps no function, Map, Set, Date or class instance. Give the lib ${path2.basename(profileFile)}.`);
|
|
298
|
+
const { value, stripped } = ServerEnvProfile.strip(evaluated.value);
|
|
299
|
+
return {
|
|
300
|
+
loader: "js",
|
|
301
|
+
contents: `export const env = ${JSON.stringify(value ?? {})};
|
|
302
|
+
`,
|
|
303
|
+
profileFile: false,
|
|
304
|
+
stripped
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
static async#underProfile(envFile, profile, appEnvDirs, chain) {
|
|
308
|
+
const name = path2.basename(envFile, ".ts").slice("env.server.".length);
|
|
309
|
+
if (appEnvDirs.has(path2.dirname(envFile)) || name === "type" || name === "example")
|
|
310
|
+
return { loader: "ts", contents: await Bun.file(envFile).text() };
|
|
311
|
+
const { loader, contents } = await ServerEnvProfile.libEnvSource(envFile, profile, chain);
|
|
312
|
+
return { loader, contents };
|
|
74
313
|
}
|
|
75
314
|
static async assertShippable({ envDir, environment, profile }) {
|
|
76
315
|
if (!ServerEnvProfile.namePattern.test(profile))
|
|
77
316
|
throw new Error(`The server env profile "${profile}" must be a lowercase-first name of letters, digits and dashes.`);
|
|
78
317
|
const name = ServerEnvProfile.fileName(environment, profile);
|
|
79
|
-
const file =
|
|
318
|
+
const file = path2.join(envDir, name);
|
|
80
319
|
if (!await Bun.file(file).exists())
|
|
81
320
|
throw new Error(`The server env profile "${profile}" bundles env/${name} in place of env/env.server.${environment}.ts, and there is no such file.`);
|
|
82
|
-
|
|
321
|
+
await ServerEnvProfile.sourceInPlaceOf(file, path2.join(envDir, `env.server.${environment}.ts`), profile);
|
|
322
|
+
const appEnvDirs = new Set([path2.resolve(envDir), realpathSync.native(envDir)]);
|
|
323
|
+
const value = await ServerEnvProfile.evaluate(file, path2.dirname(envDir), (envFile) => ServerEnvProfile.#underProfile(envFile, profile, appEnvDirs, []));
|
|
324
|
+
const secrets = ServerEnvProfile.secretPaths(value);
|
|
83
325
|
if (secrets.length)
|
|
84
326
|
throw new Error(`env/${name} holds secret-named values, which a profile's bundle carries to every computer it runs on: ${secrets.join(", ")}. Leave them out, or read them from the process env of the server that needs them.`);
|
|
85
327
|
}
|
|
86
328
|
}
|
|
87
329
|
|
|
88
330
|
// pkgs/@akanjs/devkit/artifact/implicitRootLayout.ts
|
|
89
|
-
import { readdir, rm } from "fs/promises";
|
|
90
|
-
import
|
|
331
|
+
import { readdir, rm as rm2 } from "fs/promises";
|
|
332
|
+
import path4 from "path";
|
|
91
333
|
|
|
92
334
|
// pkgs/@akanjs/devkit/transforms/asyncDefaultExportDetector.ts
|
|
93
335
|
import fs from "fs";
|
|
94
|
-
import
|
|
336
|
+
import path3 from "path";
|
|
95
337
|
|
|
96
338
|
class AsyncDefaultExportDetector {
|
|
97
339
|
static #typescriptLoad;
|
|
@@ -102,7 +344,7 @@ class AsyncDefaultExportDetector {
|
|
|
102
344
|
static async detect(moduleAbsPath) {
|
|
103
345
|
try {
|
|
104
346
|
const typescript = await AsyncDefaultExportDetector.#loadTypescript();
|
|
105
|
-
const source = fs.readFileSync(
|
|
347
|
+
const source = fs.readFileSync(path3.resolve(moduleAbsPath), "utf8");
|
|
106
348
|
const sourceFile = typescript.createSourceFile(moduleAbsPath, source, typescript.ScriptTarget.Latest, true, /\.[tj]sx$/.test(moduleAbsPath) ? typescript.ScriptKind.TSX : typescript.ScriptKind.TS);
|
|
107
349
|
return new AsyncDefaultExportDetector(typescript).detectInSourceFile(sourceFile);
|
|
108
350
|
} catch {
|
|
@@ -166,17 +408,17 @@ class AsyncDefaultExportDetector {
|
|
|
166
408
|
|
|
167
409
|
// pkgs/@akanjs/devkit/artifact/implicitRootLayout.ts
|
|
168
410
|
var LAYOUT_KEY_RE = /^\.\/(.+\/)?_layout\.(tsx|ts|jsx|js)$/;
|
|
169
|
-
var IMPLICIT_LAYOUT_DIR =
|
|
170
|
-
var IMPLICIT_DICT_DIR =
|
|
171
|
-
var IMPLICIT_OVERRIDES_DIR =
|
|
411
|
+
var IMPLICIT_LAYOUT_DIR = path4.join(".akan", "generated", "root-layouts");
|
|
412
|
+
var IMPLICIT_DICT_DIR = path4.join(".akan", "generated", "dict");
|
|
413
|
+
var IMPLICIT_OVERRIDES_DIR = path4.join(".akan", "generated", "overrides");
|
|
172
414
|
var OVERRIDES_KEY_RE = /^\.\/(.+\/)?_overrides\.(tsx|ts|jsx|js)$/;
|
|
173
415
|
var importSpecifier = (fromAbsPath, toAbsPath) => {
|
|
174
|
-
const rel =
|
|
416
|
+
const rel = path4.relative(path4.dirname(fromAbsPath), toAbsPath).split(path4.sep).join("/");
|
|
175
417
|
return rel.startsWith(".") ? rel : `./${rel}`;
|
|
176
418
|
};
|
|
177
419
|
async function writeGeneratedOverridesLayoutFile(opts) {
|
|
178
420
|
const filename = `${opts.key.replace(/^\.\//, "").replace(/[^a-zA-Z0-9]+/g, "_")}.tsx`;
|
|
179
|
-
const absPath =
|
|
421
|
+
const absPath = path4.join(path4.resolve(opts.appCwdPath), IMPLICIT_OVERRIDES_DIR, filename);
|
|
180
422
|
const source = `"use client";
|
|
181
423
|
import { UiOverrideProvider } from "akanjs/ui";
|
|
182
424
|
import { createElement, type ReactNode } from "react";
|
|
@@ -192,7 +434,7 @@ export default function AkanUiOverridesLayout({ children }: { children?: ReactNo
|
|
|
192
434
|
async function pruneGeneratedFiles(dirAbsPath, keepAbsPaths) {
|
|
193
435
|
const keep = new Set(keepAbsPaths);
|
|
194
436
|
const names = await readdir(dirAbsPath).catch(() => []);
|
|
195
|
-
await Promise.all(names.map((name) =>
|
|
437
|
+
await Promise.all(names.map((name) => path4.join(dirAbsPath, name)).filter((absPath) => !keep.has(absPath)).map((absPath) => rm2(absPath, { force: true })));
|
|
196
438
|
}
|
|
197
439
|
function getRootBoundarySegments(key) {
|
|
198
440
|
const match = LAYOUT_KEY_RE.exec(key);
|
|
@@ -208,7 +450,7 @@ function implicitRootLayoutKey(segments) {
|
|
|
208
450
|
}
|
|
209
451
|
function implicitRootLayoutAbsPath(appCwdPath, segments) {
|
|
210
452
|
const filename = segments.length ? `${segments.join("__")}__root_layout.tsx` : "__root_layout.tsx";
|
|
211
|
-
return
|
|
453
|
+
return path4.join(path4.resolve(appCwdPath), IMPLICIT_LAYOUT_DIR, filename);
|
|
212
454
|
}
|
|
213
455
|
function isRootBoundarySegments(segments, basePaths) {
|
|
214
456
|
const firstVisibleIndex = segments.findIndex((segment) => !/^\(.+\)$/.test(segment));
|
|
@@ -231,7 +473,7 @@ function findRootBoundaries(pageKeys, appCwdPath, basePaths) {
|
|
|
231
473
|
const id = segments.join("/");
|
|
232
474
|
boundaries.set(id, {
|
|
233
475
|
sourceKey: key,
|
|
234
|
-
sourceAbsPath:
|
|
476
|
+
sourceAbsPath: path4.resolve(appCwdPath, "page", key.replace(/^\.\//, "")),
|
|
235
477
|
segments
|
|
236
478
|
});
|
|
237
479
|
}
|
|
@@ -247,16 +489,16 @@ function findExplicitRootLayoutAbsPath(pageKeys, appCwdPath) {
|
|
|
247
489
|
const segments = getRootBoundarySegments(key);
|
|
248
490
|
return segments !== null && segments.length === 0;
|
|
249
491
|
});
|
|
250
|
-
return rootLayoutKey ?
|
|
492
|
+
return rootLayoutKey ? path4.resolve(appCwdPath, "page", rootLayoutKey.replace(/^\.\//, "")) : null;
|
|
251
493
|
}
|
|
252
494
|
async function assertEnvClientConvention(appCwdPath, appName) {
|
|
253
|
-
const envPath =
|
|
495
|
+
const envPath = path4.join(appCwdPath, "env", "env.client.ts");
|
|
254
496
|
if (!await Bun.file(envPath).exists()) {
|
|
255
497
|
throw new Error(`[route-convention] app "${appName}" must provide env/env.client.ts exporting "env" for generated System.Provider`);
|
|
256
498
|
}
|
|
257
499
|
}
|
|
258
500
|
async function writeGeneratedDictionaryMacroFile(appCwdPath, appName) {
|
|
259
|
-
const absPath =
|
|
501
|
+
const absPath = path4.join(path4.resolve(appCwdPath), IMPLICIT_DICT_DIR, "useDict.ts");
|
|
260
502
|
await Bun.write(absPath, `import { getAllDictionary } from "@apps/${appName}/lib/dict" with { type: "macro" };
|
|
261
503
|
|
|
262
504
|
export const allDictionary = getAllDictionary();
|
|
@@ -275,13 +517,13 @@ async function writeGeneratedRootLayoutFile(opts) {
|
|
|
275
517
|
void st;
|
|
276
518
|
` : `import "@apps/${opts.appName}/client";
|
|
277
519
|
`;
|
|
278
|
-
const inheritedLabel = inheritedSourceAbsPath ?
|
|
520
|
+
const inheritedLabel = inheritedSourceAbsPath ? path4.relative(opts.appCwdPath, inheritedSourceAbsPath) : "";
|
|
279
521
|
const inheritedImport = inheritedSourceSpecifier ? `import * as inheritedModule from ${JSON.stringify(inheritedSourceSpecifier)};
|
|
280
522
|
const inheritedLayout = resolveRouteModule(inheritedModule as never, ${JSON.stringify(inheritedLabel)}).module as LayoutModule;
|
|
281
523
|
` : `const inheritedLayout: LayoutModule = {};
|
|
282
524
|
`;
|
|
283
525
|
const prefix = opts.boundary.segments.find((segment) => !/^\(.+\)$/.test(segment)) ?? null;
|
|
284
|
-
const userLabel = opts.boundary.sourceAbsPath ?
|
|
526
|
+
const userLabel = opts.boundary.sourceAbsPath ? path4.relative(opts.appCwdPath, opts.boundary.sourceAbsPath) : "";
|
|
285
527
|
const userImport = sourceSpecifier ? `import * as userModule from ${JSON.stringify(sourceSpecifier)};
|
|
286
528
|
const userLayout = resolveRouteModule(userModule as never, ${JSON.stringify(userLabel)}).module as LayoutModule;
|
|
287
529
|
const UserLayout = userLayout.default as (props: LayoutProps) => ReactNode | Promise<ReactNode>;
|
|
@@ -360,8 +602,8 @@ ${layoutExports}${layoutReturn};
|
|
|
360
602
|
return absPath;
|
|
361
603
|
}
|
|
362
604
|
async function resolveSsrPageEntries(opts) {
|
|
363
|
-
const absPageDir =
|
|
364
|
-
const hasSt = await Bun.file(
|
|
605
|
+
const absPageDir = path4.resolve(opts.appCwdPath, "page");
|
|
606
|
+
const hasSt = await Bun.file(path4.join(opts.appCwdPath, "lib", "st.ts")).exists();
|
|
365
607
|
const basePaths = opts.basePaths ?? [];
|
|
366
608
|
const rootSourceAbsPath = findExplicitRootLayoutAbsPath(opts.pageKeys, opts.appCwdPath);
|
|
367
609
|
const rootBoundaries = findRootBoundaries(opts.pageKeys, opts.appCwdPath, basePaths);
|
|
@@ -370,7 +612,7 @@ async function resolveSsrPageEntries(opts) {
|
|
|
370
612
|
return segments !== null && isRootBoundarySegments(segments, basePaths);
|
|
371
613
|
}));
|
|
372
614
|
const base = await Promise.all(opts.pageKeys.filter((key) => !rootLayoutKeys.has(key)).map(async (key) => {
|
|
373
|
-
const userAbsPath =
|
|
615
|
+
const userAbsPath = path4.resolve(absPageDir, key);
|
|
374
616
|
if (OVERRIDES_KEY_RE.test(key)) {
|
|
375
617
|
const moduleAbsPath = await writeGeneratedOverridesLayoutFile({
|
|
376
618
|
appCwdPath: opts.appCwdPath,
|
|
@@ -393,10 +635,10 @@ async function resolveSsrPageEntries(opts) {
|
|
|
393
635
|
}),
|
|
394
636
|
seedAbsPaths: [...new Set([boundary.sourceAbsPath, rootSourceAbsPath].filter((absPath) => absPath !== null))]
|
|
395
637
|
})));
|
|
396
|
-
const appCwdAbsPath =
|
|
638
|
+
const appCwdAbsPath = path4.resolve(opts.appCwdPath);
|
|
397
639
|
await Promise.all([
|
|
398
|
-
pruneGeneratedFiles(
|
|
399
|
-
pruneGeneratedFiles(
|
|
640
|
+
pruneGeneratedFiles(path4.join(appCwdAbsPath, IMPLICIT_OVERRIDES_DIR), base.filter(({ key }) => OVERRIDES_KEY_RE.test(key)).map(({ moduleAbsPath }) => moduleAbsPath)),
|
|
641
|
+
pruneGeneratedFiles(path4.join(appCwdAbsPath, IMPLICIT_LAYOUT_DIR), generated.map(({ moduleAbsPath }) => moduleAbsPath))
|
|
400
642
|
]);
|
|
401
643
|
return [...base, ...generated].sort((a, b) => a.key.localeCompare(b.key));
|
|
402
644
|
}
|
|
@@ -406,19 +648,19 @@ async function resolveSsrPageEntriesForApp(app, pageKeys) {
|
|
|
406
648
|
}
|
|
407
649
|
|
|
408
650
|
// pkgs/@akanjs/devkit/codegenLock.ts
|
|
409
|
-
import { mkdir, open, readFile, rm as
|
|
410
|
-
import
|
|
411
|
-
import { Logger } from "akanjs/common";
|
|
651
|
+
import { mkdir as mkdir2, open, readFile, rm as rm3, stat } from "fs/promises";
|
|
652
|
+
import path5 from "path";
|
|
653
|
+
import { Logger as Logger2 } from "akanjs/common";
|
|
412
654
|
|
|
413
655
|
class CodegenLock {
|
|
414
656
|
static fileName = "codegen.lock";
|
|
415
657
|
static waitTimeoutMs = 1e4;
|
|
416
658
|
static unknownHolderStaleMs = 60000;
|
|
417
659
|
static #pollMs = 25;
|
|
418
|
-
static #logger = new
|
|
660
|
+
static #logger = new Logger2("CodegenLock");
|
|
419
661
|
static #queue = Promise.resolve();
|
|
420
662
|
static pathIn(workspaceRoot) {
|
|
421
|
-
return
|
|
663
|
+
return path5.join(workspaceRoot, "local", ".akan", CodegenLock.fileName);
|
|
422
664
|
}
|
|
423
665
|
static async run(workspaceRoot, label, fn) {
|
|
424
666
|
const ahead = CodegenLock.#queue;
|
|
@@ -448,7 +690,7 @@ class CodegenLock {
|
|
|
448
690
|
}
|
|
449
691
|
static async#withFileLock(workspaceRoot, label, fn) {
|
|
450
692
|
const lockPath = CodegenLock.pathIn(workspaceRoot);
|
|
451
|
-
await
|
|
693
|
+
await mkdir2(path5.dirname(lockPath), { recursive: true }).catch(() => {
|
|
452
694
|
return;
|
|
453
695
|
});
|
|
454
696
|
const held = await CodegenLock.#acquire(lockPath, label);
|
|
@@ -456,7 +698,7 @@ class CodegenLock {
|
|
|
456
698
|
return await fn();
|
|
457
699
|
} finally {
|
|
458
700
|
if (held)
|
|
459
|
-
await
|
|
701
|
+
await rm3(lockPath, { force: true }).catch(() => {
|
|
460
702
|
return;
|
|
461
703
|
});
|
|
462
704
|
}
|
|
@@ -493,7 +735,7 @@ class CodegenLock {
|
|
|
493
735
|
return false;
|
|
494
736
|
} else if (Date.now() - info.mtimeMs < CodegenLock.unknownHolderStaleMs)
|
|
495
737
|
return false;
|
|
496
|
-
await
|
|
738
|
+
await rm3(lockPath, { force: true }).catch(() => {
|
|
497
739
|
return;
|
|
498
740
|
});
|
|
499
741
|
return true;
|
|
@@ -590,26 +832,27 @@ import {
|
|
|
590
832
|
fork,
|
|
591
833
|
spawn as spawn2
|
|
592
834
|
} from "child_process";
|
|
593
|
-
import { existsSync as
|
|
835
|
+
import { existsSync as existsSync3, readFileSync as readFileSync2 } from "fs";
|
|
594
836
|
import {
|
|
595
837
|
copyFile,
|
|
596
838
|
cp as cpEntry,
|
|
597
839
|
lstat,
|
|
598
|
-
mkdir as
|
|
840
|
+
mkdir as mkdir3,
|
|
599
841
|
readdir as readDirEntries,
|
|
600
842
|
realpath,
|
|
601
|
-
rm as
|
|
843
|
+
rm as rm4,
|
|
602
844
|
stat as stat2,
|
|
603
845
|
symlink
|
|
604
846
|
} from "fs/promises";
|
|
605
|
-
import
|
|
847
|
+
import path13 from "path";
|
|
606
848
|
import { pathToFileURL } from "url";
|
|
607
849
|
import {
|
|
608
850
|
capitalize,
|
|
609
851
|
getPageSourceFileViolation,
|
|
610
852
|
isRouteSourceFile,
|
|
611
|
-
Logger as
|
|
853
|
+
Logger as Logger3,
|
|
612
854
|
parseRouteModuleKey,
|
|
855
|
+
toError as toError2,
|
|
613
856
|
validatePageSourceFile,
|
|
614
857
|
validateSubRoutePageKey
|
|
615
858
|
} from "akanjs/common";
|
|
@@ -755,7 +998,7 @@ ${CLAUDE_COMMENT_RULE}
|
|
|
755
998
|
`;
|
|
756
999
|
|
|
757
1000
|
// pkgs/@akanjs/devkit/akanConfig/akanBin.ts
|
|
758
|
-
import
|
|
1001
|
+
import path6 from "path";
|
|
759
1002
|
|
|
760
1003
|
// pkgs/@akanjs/devkit/akanConfig/types.ts
|
|
761
1004
|
import { archs, binPlatforms, defaultAppPipeline } from "akanjs";
|
|
@@ -806,7 +1049,7 @@ class AkanBin {
|
|
|
806
1049
|
if (!archive)
|
|
807
1050
|
throw new Error(`${at}: file picks the executable out of an archive, and this source is none`);
|
|
808
1051
|
const parts = typeof file === "string" ? file.split(/[\\/]/).filter((part) => part && part !== ".") : [];
|
|
809
|
-
if (!parts.length ||
|
|
1052
|
+
if (!parts.length || path6.isAbsolute(file) || parts.includes(".."))
|
|
810
1053
|
throw new Error(`${at}: file must be a path inside the archive (got ${JSON.stringify(file)})`);
|
|
811
1054
|
return { ...resolved, file: parts.join("/") };
|
|
812
1055
|
}
|
|
@@ -822,12 +1065,12 @@ class AkanBin {
|
|
|
822
1065
|
throw new Error(`${at}: path must name a file`);
|
|
823
1066
|
if (sha256 !== undefined)
|
|
824
1067
|
throw new Error(`${at}: sha256 checks a download, and a path source downloads nothing`);
|
|
825
|
-
return { path:
|
|
1068
|
+
return { path: path6.resolve(baseDir, localPath) };
|
|
826
1069
|
}
|
|
827
1070
|
}
|
|
828
1071
|
// pkgs/@akanjs/devkit/akanConfig/akanConfig.ts
|
|
829
1072
|
import fs2 from "fs";
|
|
830
|
-
import
|
|
1073
|
+
import path7 from "path";
|
|
831
1074
|
import { fileURLToPath } from "url";
|
|
832
1075
|
import { DatabaseModes, normalizeRoutePrefix } from "akanjs/base";
|
|
833
1076
|
import { resolveAkanI18nConfig } from "akanjs/common";
|
|
@@ -915,6 +1158,9 @@ var dedupeDockerRuns = (runs) => {
|
|
|
915
1158
|
return [...byKey.values()];
|
|
916
1159
|
};
|
|
917
1160
|
|
|
1161
|
+
class LibConfigRefusal extends Error {
|
|
1162
|
+
}
|
|
1163
|
+
|
|
918
1164
|
class AkanAppConfig {
|
|
919
1165
|
app;
|
|
920
1166
|
rootPackageJson;
|
|
@@ -1025,6 +1271,8 @@ class AkanAppConfig {
|
|
|
1025
1271
|
const { updates } = merged;
|
|
1026
1272
|
if (updates && (!updates.url || !(updates.publicKey || updates.publicKeys?.length)))
|
|
1027
1273
|
throw new Error(`${where}.updates in ${configPath} has no url or publicKeys${named ? "; give them in native.updates or the target's own" : ""}.`);
|
|
1274
|
+
if (updates?.publicKey !== undefined && updates.publicKeys !== undefined)
|
|
1275
|
+
throw new Error(`${where}.updates in ${configPath} has both publicKey and publicKeys${named ? ", native.updates merged in: give one, in native.updates' own form when overriding its keys" : ": give one"}.`);
|
|
1028
1276
|
AkanAppConfig.#assertFiles(own ?? {}, where, configPath);
|
|
1029
1277
|
const {
|
|
1030
1278
|
basePath: _basePath,
|
|
@@ -1100,6 +1348,8 @@ class AkanAppConfig {
|
|
|
1100
1348
|
"AKAN_DATABASE_MODES",
|
|
1101
1349
|
"AKAN_SSR",
|
|
1102
1350
|
"AKAN_CSR",
|
|
1351
|
+
"AKAN_MCP",
|
|
1352
|
+
"AKAN_SHUTDOWN_TIMEOUT_MS",
|
|
1103
1353
|
"AKAN_MODULES"
|
|
1104
1354
|
]);
|
|
1105
1355
|
static #desktopServerEnv(env, where, configPath) {
|
|
@@ -1118,11 +1368,14 @@ class AkanAppConfig {
|
|
|
1118
1368
|
static #desktopServerWatchdog(watchdog, where, configPath) {
|
|
1119
1369
|
if (watchdog === undefined || watchdog === false)
|
|
1120
1370
|
return null;
|
|
1121
|
-
const
|
|
1122
|
-
const whole = (value) => Number.isInteger(value) && value > 0;
|
|
1123
|
-
if (
|
|
1371
|
+
const options = watchdog === true ? {} : watchdog;
|
|
1372
|
+
const whole = (value) => value === undefined || Number.isInteger(value) && value > 0;
|
|
1373
|
+
if (!AkanAppConfig.#isPlainObject(options) || Object.keys(options).some((key) => key !== "intervalMs" && key !== "failures") || !whole(options.intervalMs) || !whole(options.failures))
|
|
1124
1374
|
throw new Error(`${where}.watchdog in ${configPath} must be true or { intervalMs, failures }, whole numbers from 1.`);
|
|
1125
|
-
return {
|
|
1375
|
+
return {
|
|
1376
|
+
intervalMs: options.intervalMs ?? 1e4,
|
|
1377
|
+
failures: options.failures ?? 3
|
|
1378
|
+
};
|
|
1126
1379
|
}
|
|
1127
1380
|
static #platformIndexPath(section) {
|
|
1128
1381
|
const { indexPath: rawIndexPath, ...rest } = section;
|
|
@@ -1344,7 +1597,7 @@ CMD [${command.map((c) => `"${c}"`).join(",")}]`;
|
|
|
1344
1597
|
static async from(app, { bustImportCache = false } = {}) {
|
|
1345
1598
|
const [configImp, baseDevEnv, libs, rootPackageJson] = await Promise.all([
|
|
1346
1599
|
AkanAppConfig.importConfigModule(app.cwdPath, { bustImportCache }),
|
|
1347
|
-
WorkspaceExecutor.getBaseDevEnv(
|
|
1600
|
+
WorkspaceExecutor.getBaseDevEnv(path7.join(app.workspace.workspaceRoot, ".env")),
|
|
1348
1601
|
app.workspace.getLibs(),
|
|
1349
1602
|
app.workspace.getPackageJson()
|
|
1350
1603
|
]);
|
|
@@ -1361,6 +1614,8 @@ CMD [${command.map((c) => `"${c}"`).join(",")}]`;
|
|
|
1361
1614
|
}
|
|
1362
1615
|
static async#collectLibContributions(app, libs, bustImportCache) {
|
|
1363
1616
|
const libConfigs = await Promise.all(libs.map(async (libName) => LibExecutor.from(app, libName).getConfig({ refresh: bustImportCache }).catch((error) => {
|
|
1617
|
+
if (error instanceof LibConfigRefusal)
|
|
1618
|
+
throw error;
|
|
1364
1619
|
app.logger.warn(`Skipped libs/${libName}/akan.config.ts contributions: ${String(error)}`);
|
|
1365
1620
|
return null;
|
|
1366
1621
|
})));
|
|
@@ -1442,11 +1697,11 @@ var akanPackageJson = null;
|
|
|
1442
1697
|
function getAkanPackageJson() {
|
|
1443
1698
|
if (akanPackageJson)
|
|
1444
1699
|
return akanPackageJson;
|
|
1445
|
-
const sourceDir =
|
|
1700
|
+
const sourceDir = path7.dirname(fileURLToPath(import.meta.url));
|
|
1446
1701
|
const packageJsonPaths = [
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1702
|
+
path7.join(sourceDir, "../../../akanjs/package.json"),
|
|
1703
|
+
path7.join(process.cwd(), "pkgs/akanjs/package.json"),
|
|
1704
|
+
path7.join(path7.dirname(Bun.main), "node_modules/akanjs/package.json")
|
|
1450
1705
|
];
|
|
1451
1706
|
try {
|
|
1452
1707
|
packageJsonPaths.unshift(Bun.resolveSync("akanjs/package.json", sourceDir));
|
|
@@ -1501,29 +1756,29 @@ class AkanLibConfig {
|
|
|
1501
1756
|
const resolved = typeof configImp === "function" ? configImp(lib) : configImp;
|
|
1502
1757
|
const { plugins, ...config } = resolved ?? {};
|
|
1503
1758
|
if ("pipeline" in config)
|
|
1504
|
-
throw new
|
|
1759
|
+
throw new LibConfigRefusal(`libs/${lib.name}/akan.config.ts declares \`pipeline\`, which a lib does not take: pipelines belong to apps and the workspace root, and a lib's build-time work goes in an akan build plugin.`);
|
|
1505
1760
|
if ("subspace" in config)
|
|
1506
|
-
throw new
|
|
1761
|
+
throw new LibConfigRefusal(`libs/${lib.name}/akan.config.ts declares \`subspace\`, a workspace setting: it belongs in the workspace root akan.config.ts.`);
|
|
1507
1762
|
return new AkanLibConfig(lib, config, plugins ?? []);
|
|
1508
1763
|
}
|
|
1509
1764
|
}
|
|
1510
1765
|
//! need to refactor
|
|
1511
1766
|
var shiftBuildNum = async (app, delta) => {
|
|
1512
1767
|
const { buildNum } = (await AkanAppConfig.from(app)).native;
|
|
1513
|
-
const akanConfigPath =
|
|
1768
|
+
const akanConfigPath = path7.join(app.cwdPath, "akan.config.ts");
|
|
1514
1769
|
const akanConfig = fs2.readFileSync(akanConfigPath, "utf8");
|
|
1515
1770
|
fs2.writeFileSync(akanConfigPath, akanConfig.replace(`buildNum: ${buildNum}`, `buildNum: ${buildNum + delta}`));
|
|
1516
1771
|
};
|
|
1517
1772
|
var increaseBuildNum = async (app) => await shiftBuildNum(app, 1);
|
|
1518
1773
|
var decreaseBuildNum = async (app) => await shiftBuildNum(app, -1);
|
|
1519
1774
|
// pkgs/@akanjs/devkit/akanConfig/akanWorkspaceConfig.ts
|
|
1520
|
-
import
|
|
1775
|
+
import path8 from "path";
|
|
1521
1776
|
class AkanWorkspaceConfig {
|
|
1522
1777
|
static fileName = "akan.config.ts";
|
|
1523
1778
|
static #keys = ["subspace", "pipeline", "cloud"];
|
|
1524
1779
|
static #loopbackHosts = ["localhost", "127.0.0.1", "[::1]"];
|
|
1525
1780
|
static async from(workspace, { bustImportCache = false } = {}) {
|
|
1526
|
-
const file =
|
|
1781
|
+
const file = path8.join(workspace.workspaceRoot, AkanWorkspaceConfig.fileName);
|
|
1527
1782
|
if (!await FileSys2.fileExists(file))
|
|
1528
1783
|
return new AkanWorkspaceConfig(null, {});
|
|
1529
1784
|
const input = await AkanAppConfig.importConfigModule(workspace.workspaceRoot, { bustImportCache });
|
|
@@ -1568,13 +1823,13 @@ class AkanWorkspaceConfig {
|
|
|
1568
1823
|
}
|
|
1569
1824
|
}
|
|
1570
1825
|
// pkgs/@akanjs/devkit/jsonFormatter.ts
|
|
1571
|
-
import
|
|
1826
|
+
import path9 from "path";
|
|
1572
1827
|
|
|
1573
1828
|
class JsonFormatter {
|
|
1574
1829
|
static lineWidth = 120;
|
|
1575
1830
|
static stringify(value, filePath) {
|
|
1576
1831
|
const json = JSON.stringify(value, null, 2);
|
|
1577
|
-
if (
|
|
1832
|
+
if (path9.basename(filePath) === "package.json")
|
|
1578
1833
|
return `${json}
|
|
1579
1834
|
`;
|
|
1580
1835
|
return `${JsonFormatter.#print(JSON.parse(json), 0, { column: 0, trailing: 0 })}
|
|
@@ -1644,8 +1899,8 @@ ${" ".repeat(depth)}]`;
|
|
|
1644
1899
|
|
|
1645
1900
|
// pkgs/@akanjs/devkit/linter.ts
|
|
1646
1901
|
import { spawn } from "child_process";
|
|
1647
|
-
import { existsSync, readFileSync } from "fs";
|
|
1648
|
-
import
|
|
1902
|
+
import { existsSync as existsSync2, readFileSync } from "fs";
|
|
1903
|
+
import path10 from "path";
|
|
1649
1904
|
import chalk from "chalk";
|
|
1650
1905
|
var parseBiomeReport = (output) => {
|
|
1651
1906
|
for (let start = output.indexOf("{");start !== -1; start = output.indexOf("{", start + 1)) {
|
|
@@ -1711,14 +1966,14 @@ class Linter {
|
|
|
1711
1966
|
#biomeBin;
|
|
1712
1967
|
constructor(cwdPath) {
|
|
1713
1968
|
this.lintRoot = this.#findBiomeRootPath(cwdPath);
|
|
1714
|
-
this.configPath = Linter.#configPathIn(this.lintRoot) ??
|
|
1715
|
-
const localBiomeBin =
|
|
1716
|
-
this.#biomeBin =
|
|
1969
|
+
this.configPath = Linter.#configPathIn(this.lintRoot) ?? path10.join(this.lintRoot, "biome.json");
|
|
1970
|
+
const localBiomeBin = path10.join(this.lintRoot, "node_modules", ".bin", Linter.biomeBinName);
|
|
1971
|
+
this.#biomeBin = existsSync2(localBiomeBin) ? localBiomeBin : "biome";
|
|
1717
1972
|
}
|
|
1718
1973
|
static #configPathIn(dir) {
|
|
1719
1974
|
for (const fileName of BIOME_CONFIG_FILES) {
|
|
1720
|
-
const configPath =
|
|
1721
|
-
if (
|
|
1975
|
+
const configPath = path10.join(dir, fileName);
|
|
1976
|
+
if (existsSync2(configPath))
|
|
1722
1977
|
return configPath;
|
|
1723
1978
|
}
|
|
1724
1979
|
return null;
|
|
@@ -1726,19 +1981,19 @@ class Linter {
|
|
|
1726
1981
|
#findBiomeRootPath(dir) {
|
|
1727
1982
|
if (Linter.#configPathIn(dir))
|
|
1728
1983
|
return dir;
|
|
1729
|
-
const parentDir =
|
|
1984
|
+
const parentDir = path10.dirname(dir);
|
|
1730
1985
|
if (parentDir === dir)
|
|
1731
1986
|
throw new Error(`biome.json not found from ${dir}`);
|
|
1732
1987
|
return this.#findBiomeRootPath(parentDir);
|
|
1733
1988
|
}
|
|
1734
1989
|
#toBiomePath(filePath) {
|
|
1735
|
-
const relativePath =
|
|
1736
|
-
if (!relativePath.startsWith("..") && !
|
|
1990
|
+
const relativePath = path10.relative(this.lintRoot, filePath);
|
|
1991
|
+
if (!relativePath.startsWith("..") && !path10.isAbsolute(relativePath))
|
|
1737
1992
|
return relativePath;
|
|
1738
1993
|
return filePath;
|
|
1739
1994
|
}
|
|
1740
1995
|
#resolveFilePath(filePath) {
|
|
1741
|
-
return
|
|
1996
|
+
return path10.isAbsolute(filePath) ? filePath : path10.join(this.lintRoot, filePath);
|
|
1742
1997
|
}
|
|
1743
1998
|
async#runBiome(args, input) {
|
|
1744
1999
|
return await new Promise((resolve, reject) => {
|
|
@@ -1763,7 +2018,7 @@ class Linter {
|
|
|
1763
2018
|
const diagnosticPath = diagnostic.location?.path;
|
|
1764
2019
|
if (!diagnosticPath)
|
|
1765
2020
|
return fallbackFilePath;
|
|
1766
|
-
return
|
|
2021
|
+
return path10.isAbsolute(diagnosticPath) ? diagnosticPath : path10.join(this.lintRoot, diagnosticPath);
|
|
1767
2022
|
}
|
|
1768
2023
|
#createLintMessage(diagnostic) {
|
|
1769
2024
|
const start = diagnostic.location?.start;
|
|
@@ -1806,7 +2061,7 @@ class Linter {
|
|
|
1806
2061
|
};
|
|
1807
2062
|
}
|
|
1808
2063
|
async#checkFile(filePath, { write = false } = {}) {
|
|
1809
|
-
const originalContent =
|
|
2064
|
+
const originalContent = existsSync2(filePath) ? readFileSync(filePath, "utf8") : "";
|
|
1810
2065
|
const { stdout, stderr } = await this.#runBiome([
|
|
1811
2066
|
"check",
|
|
1812
2067
|
...write ? ["--write"] : [],
|
|
@@ -1820,7 +2075,7 @@ class Linter {
|
|
|
1820
2075
|
const report = parseBiomeReport(stdout || stderr);
|
|
1821
2076
|
const results = this.#toLintResults(report, filePath);
|
|
1822
2077
|
const { errors, warnings } = this.#splitMessages(results);
|
|
1823
|
-
const output = write &&
|
|
2078
|
+
const output = write && existsSync2(filePath) ? readFileSync(filePath, "utf8") : undefined;
|
|
1824
2079
|
return {
|
|
1825
2080
|
fixed: write && output !== originalContent,
|
|
1826
2081
|
output,
|
|
@@ -1836,7 +2091,7 @@ class Linter {
|
|
|
1836
2091
|
}
|
|
1837
2092
|
async lintFile(filePath) {
|
|
1838
2093
|
const resolvedFilePath = this.#resolveFilePath(filePath);
|
|
1839
|
-
if (!
|
|
2094
|
+
if (!existsSync2(resolvedFilePath))
|
|
1840
2095
|
throw new Error(`File not found: ${filePath}`);
|
|
1841
2096
|
return await this.#checkFile(resolvedFilePath);
|
|
1842
2097
|
}
|
|
@@ -1853,7 +2108,7 @@ class Linter {
|
|
|
1853
2108
|
output.push(`
|
|
1854
2109
|
${chalk.cyan(result.filePath)}`);
|
|
1855
2110
|
let sourceLines = [];
|
|
1856
|
-
if (
|
|
2111
|
+
if (existsSync2(result.filePath)) {
|
|
1857
2112
|
try {
|
|
1858
2113
|
const sourceContent = readFileSync(result.filePath, "utf8");
|
|
1859
2114
|
sourceLines = sourceContent.split(`
|
|
@@ -1925,7 +2180,7 @@ ${errorText}, ${warningText} found`];
|
|
|
1925
2180
|
}
|
|
1926
2181
|
async fixFile(filePath, dryRun = false) {
|
|
1927
2182
|
const resolvedFilePath = this.#resolveFilePath(filePath);
|
|
1928
|
-
if (!
|
|
2183
|
+
if (!existsSync2(resolvedFilePath))
|
|
1929
2184
|
throw new Error(`File not found: ${filePath}`);
|
|
1930
2185
|
if (!dryRun)
|
|
1931
2186
|
return await this.#checkFile(resolvedFilePath, { write: true });
|
|
@@ -1935,7 +2190,7 @@ ${errorText}, ${warningText} found`];
|
|
|
1935
2190
|
return { ...lintResult, fixed: stdout !== source, output: stdout };
|
|
1936
2191
|
}
|
|
1937
2192
|
async fixFiles(filePaths) {
|
|
1938
|
-
const resolved = filePaths.map((filePath) => this.#resolveFilePath(filePath)).filter(
|
|
2193
|
+
const resolved = filePaths.map((filePath) => this.#resolveFilePath(filePath)).filter(existsSync2);
|
|
1939
2194
|
if (resolved.length === 0)
|
|
1940
2195
|
return { fixed: [] };
|
|
1941
2196
|
const before = new Map(resolved.map((filePath) => [filePath, readFileSync(filePath, "utf8")]));
|
|
@@ -1955,7 +2210,7 @@ ${errorText}, ${warningText} found`];
|
|
|
1955
2210
|
}
|
|
1956
2211
|
async getConfigForFile(filePath) {
|
|
1957
2212
|
const resolvedFilePath = this.#resolveFilePath(filePath);
|
|
1958
|
-
if (!
|
|
2213
|
+
if (!existsSync2(resolvedFilePath))
|
|
1959
2214
|
throw new Error(`File not found: ${filePath}`);
|
|
1960
2215
|
return JSON.parse(readFileSync(this.configPath, "utf8"));
|
|
1961
2216
|
}
|
|
@@ -2099,7 +2354,7 @@ class ManifestDependencies {
|
|
|
2099
2354
|
|
|
2100
2355
|
// pkgs/@akanjs/devkit/repoIdentity.ts
|
|
2101
2356
|
import { execFileSync } from "child_process";
|
|
2102
|
-
import
|
|
2357
|
+
import path11 from "path";
|
|
2103
2358
|
var resolved = new Map;
|
|
2104
2359
|
var readRemoteName = (workspaceRoot) => {
|
|
2105
2360
|
try {
|
|
@@ -2117,13 +2372,13 @@ var resolveRepoName = (workspaceRoot) => {
|
|
|
2117
2372
|
const cached = resolved.get(workspaceRoot);
|
|
2118
2373
|
if (cached)
|
|
2119
2374
|
return cached;
|
|
2120
|
-
const repoName = readRemoteName(workspaceRoot) ??
|
|
2375
|
+
const repoName = readRemoteName(workspaceRoot) ?? path11.basename(workspaceRoot);
|
|
2121
2376
|
resolved.set(workspaceRoot, repoName);
|
|
2122
2377
|
return repoName;
|
|
2123
2378
|
};
|
|
2124
2379
|
|
|
2125
2380
|
// pkgs/@akanjs/devkit/scanInfo.ts
|
|
2126
|
-
import
|
|
2381
|
+
import path12 from "path";
|
|
2127
2382
|
|
|
2128
2383
|
// pkgs/@akanjs/devkit/workspaceLayout.ts
|
|
2129
2384
|
var appRootAllowedFiles = new Set([
|
|
@@ -2245,9 +2500,9 @@ var moduleUiFileTypes = {
|
|
|
2245
2500
|
scalar: new Set(["Template", "Unit"])
|
|
2246
2501
|
};
|
|
2247
2502
|
var createDependencyScanner = async (exec) => (await import("./dependencyScanner-06z2dmrr.js")).TypeScriptDependencyScanner.from(exec);
|
|
2248
|
-
var getScanPath = (exec, relativePath) =>
|
|
2503
|
+
var getScanPath = (exec, relativePath) => path12.posix.join(`${exec.type}s`, exec.name, relativePath.split(path12.sep).join("/"));
|
|
2249
2504
|
var getModuleNameFromPath = (kind, modulePath) => {
|
|
2250
|
-
const dirname =
|
|
2505
|
+
const dirname = path12.basename(modulePath);
|
|
2251
2506
|
return kind === "service" ? dirname.replace(/^_+/, "") : dirname;
|
|
2252
2507
|
};
|
|
2253
2508
|
async function assertScanConvention(exec, libRoot) {
|
|
@@ -2268,24 +2523,24 @@ async function assertScanConvention(exec, libRoot) {
|
|
|
2268
2523
|
for (const filename of await exec.readdir(facet)) {
|
|
2269
2524
|
const shadow = facetIndexShadowOf(`${facet}/${filename}`);
|
|
2270
2525
|
if (shadow)
|
|
2271
|
-
addViolation(
|
|
2526
|
+
addViolation(path12.join(facet, filename), shadow);
|
|
2272
2527
|
}
|
|
2273
2528
|
if (exec.type === "lib")
|
|
2274
2529
|
for (const { relativePath, reason } of await exec.getPageConventionViolations())
|
|
2275
2530
|
addViolation(relativePath, reason);
|
|
2276
2531
|
for (const filename of libRoot.files)
|
|
2277
2532
|
if (!isAllowedLibFacetRootFile(filename))
|
|
2278
|
-
addViolation(
|
|
2533
|
+
addViolation(path12.join("lib", filename), libFacetRootHintOf(filename) ?? "unsupported lib facet root file");
|
|
2279
2534
|
for (const dirname of libRoot.dirs)
|
|
2280
2535
|
if (dirname.startsWith("__") && !internalLibDirs.has(dirname))
|
|
2281
|
-
addViolation(
|
|
2536
|
+
addViolation(path12.join("lib", dirname), "unsupported internal lib folder");
|
|
2282
2537
|
const databaseDirs = libRoot.dirs.filter((dirname) => !dirname.startsWith("_"));
|
|
2283
2538
|
const serviceDirs = libRoot.dirs.filter((dirname) => dirname.startsWith("_") && !dirname.startsWith("__"));
|
|
2284
2539
|
const scalarDirs = await exec.readdir("lib/__scalar");
|
|
2285
2540
|
await Promise.all([
|
|
2286
|
-
...databaseDirs.map((dirname) => validateModuleFiles(exec, violations, "database",
|
|
2287
|
-
...serviceDirs.map((dirname) => validateModuleFiles(exec, violations, "service",
|
|
2288
|
-
...scalarDirs.map((dirname) => validateModuleFiles(exec, violations, "scalar",
|
|
2541
|
+
...databaseDirs.map((dirname) => validateModuleFiles(exec, violations, "database", path12.join("lib", dirname))),
|
|
2542
|
+
...serviceDirs.map((dirname) => validateModuleFiles(exec, violations, "service", path12.join("lib", dirname))),
|
|
2543
|
+
...scalarDirs.map((dirname) => validateModuleFiles(exec, violations, "scalar", path12.join("lib/__scalar", dirname)))
|
|
2289
2544
|
]);
|
|
2290
2545
|
if (violations.length > 0) {
|
|
2291
2546
|
throw new Error(`[scan-convention]
|
|
@@ -2297,11 +2552,11 @@ async function validateModuleFiles(exec, violations, kind, modulePath) {
|
|
|
2297
2552
|
const { files, dirs } = await exec.getFilesAndDirs(modulePath);
|
|
2298
2553
|
const moduleName = getModuleNameFromPath(kind, modulePath);
|
|
2299
2554
|
dirs.forEach((dirname) => {
|
|
2300
|
-
violations.push(`${getScanPath(exec,
|
|
2555
|
+
violations.push(`${getScanPath(exec, path12.join(modulePath, dirname))}: unsupported module folder`);
|
|
2301
2556
|
});
|
|
2302
2557
|
const uiModuleName = moduleName[0].toUpperCase() + moduleName.slice(1);
|
|
2303
2558
|
for (const filename of files) {
|
|
2304
|
-
const scanPath = getScanPath(exec,
|
|
2559
|
+
const scanPath = getScanPath(exec, path12.join(modulePath, filename));
|
|
2305
2560
|
if (filename === "index.ts" || filename === "index.tsx")
|
|
2306
2561
|
continue;
|
|
2307
2562
|
if (filename === `${moduleName}.abstract.md`)
|
|
@@ -2376,16 +2631,16 @@ class ScanInfo {
|
|
|
2376
2631
|
}
|
|
2377
2632
|
};
|
|
2378
2633
|
await Promise.all([
|
|
2379
|
-
...databaseDirs.map((name) => collect(
|
|
2380
|
-
...serviceDirs.map((dirname) => collect(
|
|
2381
|
-
...scalarDirs.map((name) => collect(
|
|
2634
|
+
...databaseDirs.map((name) => collect(path12.join("lib", name), name, "databases")),
|
|
2635
|
+
...serviceDirs.map((dirname) => collect(path12.join("lib", dirname), dirname.slice(1), "services")),
|
|
2636
|
+
...scalarDirs.map((name) => collect(path12.join("lib/__scalar", name), name, "scalars"))
|
|
2382
2637
|
]);
|
|
2383
2638
|
const routes = exec.type === "lib" ? [] : await exec.getPageKeys();
|
|
2384
2639
|
const common = {
|
|
2385
2640
|
name: exec.name,
|
|
2386
2641
|
type: exec.type,
|
|
2387
2642
|
repoName: exec.workspace.repoName,
|
|
2388
|
-
serveDomain: WorkspaceExecutor.getBaseDevEnv(
|
|
2643
|
+
serveDomain: WorkspaceExecutor.getBaseDevEnv(path12.join(exec.workspace.workspaceRoot, ".env")).serveDomain,
|
|
2389
2644
|
files,
|
|
2390
2645
|
libDeps,
|
|
2391
2646
|
pkgDeps,
|
|
@@ -2709,16 +2964,16 @@ class Executor {
|
|
|
2709
2964
|
linter = null;
|
|
2710
2965
|
constructor(name, cwdPath) {
|
|
2711
2966
|
this.name = name;
|
|
2712
|
-
this.logger = new
|
|
2967
|
+
this.logger = new Logger3(name);
|
|
2713
2968
|
this.logs = [];
|
|
2714
2969
|
this.cwdPath = cwdPath;
|
|
2715
2970
|
}
|
|
2716
2971
|
#stdout(data) {
|
|
2717
2972
|
if (Executor.verbose)
|
|
2718
|
-
|
|
2973
|
+
Logger3.raw(chalk2.dim(data.toString()));
|
|
2719
2974
|
}
|
|
2720
2975
|
#stderr(data) {
|
|
2721
|
-
|
|
2976
|
+
Logger3.raw(chalk2.red(data.toString()));
|
|
2722
2977
|
}
|
|
2723
2978
|
#settle(proc, target, { onClose = false, keepLogs = false, redStderr = false } = {}) {
|
|
2724
2979
|
let stdout = "";
|
|
@@ -2767,10 +3022,10 @@ class Executor {
|
|
|
2767
3022
|
return this.#settle(proc, target, { redStderr: true }).then(({ code, signal }) => ({ code, signal }));
|
|
2768
3023
|
}
|
|
2769
3024
|
getPath(filePath) {
|
|
2770
|
-
if (
|
|
3025
|
+
if (path13.isAbsolute(filePath))
|
|
2771
3026
|
return filePath;
|
|
2772
3027
|
if (filePath.startsWith("."))
|
|
2773
|
-
return
|
|
3028
|
+
return path13.join(this.cwdPath, filePath);
|
|
2774
3029
|
const baseParts = this.cwdPath.split(/[\\/]/).filter(Boolean);
|
|
2775
3030
|
const targetParts = filePath.split(/[\\/]/).filter(Boolean);
|
|
2776
3031
|
let overlapLength = 0;
|
|
@@ -2784,12 +3039,12 @@ class Executor {
|
|
|
2784
3039
|
if (isOverlap)
|
|
2785
3040
|
overlapLength = i;
|
|
2786
3041
|
}
|
|
2787
|
-
return
|
|
3042
|
+
return path13.join(this.cwdPath, ...targetParts.slice(overlapLength));
|
|
2788
3043
|
}
|
|
2789
3044
|
async mkdir(dirPath) {
|
|
2790
3045
|
const writePath = this.getPath(dirPath);
|
|
2791
3046
|
if (!await FileSys2.dirExists(writePath))
|
|
2792
|
-
await
|
|
3047
|
+
await mkdir3(writePath, { recursive: true });
|
|
2793
3048
|
this.logger.verbose(`Make directory ${writePath}`);
|
|
2794
3049
|
return this;
|
|
2795
3050
|
}
|
|
@@ -2825,26 +3080,28 @@ class Executor {
|
|
|
2825
3080
|
return this;
|
|
2826
3081
|
}
|
|
2827
3082
|
async removeDir(dirPath) {
|
|
2828
|
-
const readPath =
|
|
3083
|
+
const readPath = path13.join(this.getPath(dirPath), ".");
|
|
2829
3084
|
if (await FileSys2.entryExists(readPath))
|
|
2830
|
-
await
|
|
3085
|
+
await rm4(readPath, { recursive: true, force: true });
|
|
2831
3086
|
this.logger.verbose(`Remove directory ${readPath}`);
|
|
2832
3087
|
return this;
|
|
2833
3088
|
}
|
|
2834
3089
|
async writeFile(filePath, content, { overwrite = true, silent = false } = {}) {
|
|
2835
3090
|
const writePath = this.getPath(filePath);
|
|
2836
|
-
const dir =
|
|
3091
|
+
const dir = path13.dirname(writePath);
|
|
2837
3092
|
if (!await FileSys2.dirExists(dir))
|
|
2838
|
-
await
|
|
3093
|
+
await mkdir3(dir, { recursive: true });
|
|
2839
3094
|
let contentStr = typeof content === "string" ? content : JsonFormatter.stringify(content, writePath);
|
|
3095
|
+
let written = true;
|
|
2840
3096
|
if (await FileSys2.fileExists(writePath)) {
|
|
2841
3097
|
const currentContent = await FileSys2.readText(writePath);
|
|
2842
3098
|
if (currentContent === contentStr || !overwrite) {
|
|
2843
3099
|
this.logger.verbose(`File ${writePath} is unchanged`);
|
|
2844
3100
|
contentStr = currentContent;
|
|
3101
|
+
written = false;
|
|
2845
3102
|
} else {
|
|
2846
3103
|
await FileSys2.writeText(writePath, contentStr);
|
|
2847
|
-
if (
|
|
3104
|
+
if (Logger3.isVerbose())
|
|
2848
3105
|
this.logger.rawLog(chalk2.yellow(`File Update: ${filePath}`));
|
|
2849
3106
|
}
|
|
2850
3107
|
} else {
|
|
@@ -2852,13 +3109,13 @@ class Executor {
|
|
|
2852
3109
|
if (!silent)
|
|
2853
3110
|
this.logger.rawLog(chalk2.green(`File Create: ${filePath}`));
|
|
2854
3111
|
}
|
|
2855
|
-
return { filePath: writePath, content: contentStr };
|
|
3112
|
+
return { filePath: writePath, content: contentStr, written };
|
|
2856
3113
|
}
|
|
2857
3114
|
async writeJson(filePath, content) {
|
|
2858
3115
|
await this.writeFile(filePath, content);
|
|
2859
3116
|
}
|
|
2860
3117
|
async getLocalFile(targetPath) {
|
|
2861
|
-
const filePath =
|
|
3118
|
+
const filePath = path13.isAbsolute(targetPath) ? targetPath : targetPath.replace(this.cwdPath, "");
|
|
2862
3119
|
const content = await this.readFile(filePath);
|
|
2863
3120
|
return { filePath, content };
|
|
2864
3121
|
}
|
|
@@ -2875,7 +3132,7 @@ class Executor {
|
|
|
2875
3132
|
return;
|
|
2876
3133
|
const isDirectory = (await stat2(src)).isDirectory();
|
|
2877
3134
|
if (!await FileSys2.exists(dest) && isDirectory)
|
|
2878
|
-
await
|
|
3135
|
+
await mkdir3(dest, { recursive: true });
|
|
2879
3136
|
if (dereference || process.platform === "win32")
|
|
2880
3137
|
await cpEntry(src, dest, { recursive: isDirectory, dereference: true, force: true });
|
|
2881
3138
|
else
|
|
@@ -2945,9 +3202,9 @@ class Executor {
|
|
|
2945
3202
|
const result = await getContent.default(scanInfo ?? null, dict, options);
|
|
2946
3203
|
if (result === null)
|
|
2947
3204
|
return null;
|
|
2948
|
-
const filename = typeof result === "object" ? result.filename :
|
|
3205
|
+
const filename = typeof result === "object" ? result.filename : path13.basename(targetPath).replace(".js", ".ts");
|
|
2949
3206
|
const content = typeof result === "object" ? result.content : result;
|
|
2950
|
-
const convertedTargetPath = convertPath(`${
|
|
3207
|
+
const convertedTargetPath = convertPath(`${path13.dirname(targetPath)}/${filename}`);
|
|
2951
3208
|
this.logger.verbose(`Apply template ${templatePath} to ${convertedTargetPath}`);
|
|
2952
3209
|
return this.writeFile(convertedTargetPath, content, { overwrite });
|
|
2953
3210
|
} else if (targetPath.endsWith(".template")) {
|
|
@@ -2956,15 +3213,17 @@ class Executor {
|
|
|
2956
3213
|
const convertedContent = Object.entries(dict).reduce((data, [key, value]) => data.replace(new RegExp(`<%= ${key} %>`, "g"), value), content);
|
|
2957
3214
|
this.logger.verbose(`Apply template ${templatePath} to ${convertedTargetPath}`);
|
|
2958
3215
|
return this.writeFile(convertedTargetPath, convertedContent, { overwrite });
|
|
2959
|
-
} else if (staticTemplateFileExtensions.has(
|
|
3216
|
+
} else if (staticTemplateFileExtensions.has(path13.extname(targetPath).toLowerCase())) {
|
|
2960
3217
|
const convertedTargetPath = convertPath(targetPath);
|
|
2961
3218
|
const writePath = this.getPath(convertedTargetPath);
|
|
2962
|
-
|
|
3219
|
+
if (!overwrite && await FileSys2.fileExists(writePath))
|
|
3220
|
+
return { filePath: writePath, content: "", written: false };
|
|
3221
|
+
const dirname = path13.dirname(writePath);
|
|
2963
3222
|
if (!await FileSys2.dirExists(dirname))
|
|
2964
|
-
await
|
|
3223
|
+
await mkdir3(dirname, { recursive: true });
|
|
2965
3224
|
await copyFile(templatePath, writePath);
|
|
2966
3225
|
this.logger.verbose(`Apply template ${templatePath} to ${convertedTargetPath}`);
|
|
2967
|
-
return { filePath: writePath, content: "" };
|
|
3226
|
+
return { filePath: writePath, content: "", written: true };
|
|
2968
3227
|
} else
|
|
2969
3228
|
return null;
|
|
2970
3229
|
}
|
|
@@ -2981,7 +3240,7 @@ class Executor {
|
|
|
2981
3240
|
if ((await stat2(templatePath)).isFile()) {
|
|
2982
3241
|
const fileContent = await this.#applyTemplateFile({
|
|
2983
3242
|
templatePath,
|
|
2984
|
-
targetPath:
|
|
3243
|
+
targetPath: path13.join(basePath, path13.basename(templatePath)),
|
|
2985
3244
|
scanInfo,
|
|
2986
3245
|
overwrite
|
|
2987
3246
|
}, dict, options);
|
|
@@ -2989,19 +3248,19 @@ class Executor {
|
|
|
2989
3248
|
} else {
|
|
2990
3249
|
const subdirs = await readDirEntries(templatePath);
|
|
2991
3250
|
const fileContents = (await Promise.all(subdirs.map(async (subdir) => {
|
|
2992
|
-
const subpath =
|
|
3251
|
+
const subpath = path13.join(templatePath, subdir);
|
|
2993
3252
|
if ((await stat2(subpath)).isFile()) {
|
|
2994
3253
|
const fileContent = await this.#applyTemplateFile({
|
|
2995
3254
|
templatePath: subpath,
|
|
2996
|
-
targetPath:
|
|
3255
|
+
targetPath: path13.join(basePath, subdir),
|
|
2997
3256
|
scanInfo,
|
|
2998
3257
|
overwrite
|
|
2999
3258
|
}, dict, options);
|
|
3000
3259
|
return fileContent ? [fileContent] : [];
|
|
3001
3260
|
} else
|
|
3002
3261
|
return await this._applyTemplate({
|
|
3003
|
-
basePath:
|
|
3004
|
-
template:
|
|
3262
|
+
basePath: path13.join(basePath, subdir),
|
|
3263
|
+
template: path13.join(template, subdir),
|
|
3005
3264
|
scanInfo,
|
|
3006
3265
|
dict,
|
|
3007
3266
|
overwrite,
|
|
@@ -3013,8 +3272,8 @@ class Executor {
|
|
|
3013
3272
|
}
|
|
3014
3273
|
async#resolveTemplateRoot() {
|
|
3015
3274
|
const candidates = [
|
|
3016
|
-
|
|
3017
|
-
|
|
3275
|
+
path13.resolve(getDirname2(import.meta.url), "templates"),
|
|
3276
|
+
path13.resolve(getDirname2(import.meta.url), "../cli/templates")
|
|
3018
3277
|
];
|
|
3019
3278
|
for (const candidate of candidates) {
|
|
3020
3279
|
if (await FileSys2.dirExists(candidate))
|
|
@@ -3024,7 +3283,7 @@ class Executor {
|
|
|
3024
3283
|
}
|
|
3025
3284
|
async applyTemplate(options) {
|
|
3026
3285
|
const fileContents = await this._applyTemplate({ ...options, dict: withCapitalizedDict(options.dict) });
|
|
3027
|
-
await this.#formatAppliedTemplate(fileContents);
|
|
3286
|
+
await this.#formatAppliedTemplate(fileContents.filter((fileContent) => fileContent.written));
|
|
3028
3287
|
return fileContents;
|
|
3029
3288
|
}
|
|
3030
3289
|
async#formatAppliedTemplate(fileContents) {
|
|
@@ -3036,7 +3295,7 @@ class Executor {
|
|
|
3036
3295
|
if (fixed.length > 0)
|
|
3037
3296
|
this.logger.verbose(`Formatted ${fixed.length} scaffolded file(s)`);
|
|
3038
3297
|
} catch (err) {
|
|
3039
|
-
this.logger.verbose(`Skipped formatting scaffolded files: ${err
|
|
3298
|
+
this.logger.verbose(`Skipped formatting scaffolded files: ${toError2(err).message}`);
|
|
3040
3299
|
}
|
|
3041
3300
|
}
|
|
3042
3301
|
async getTypeChecker() {
|
|
@@ -3053,7 +3312,7 @@ class Executor {
|
|
|
3053
3312
|
}
|
|
3054
3313
|
async typeCheckAsync(filePath) {
|
|
3055
3314
|
const path = this.getPath(filePath);
|
|
3056
|
-
const entry = await
|
|
3315
|
+
const entry = await ProjectTypecheck2.workerEntry(process.cwd());
|
|
3057
3316
|
const proc = Bun.spawn([process.execPath, entry], {
|
|
3058
3317
|
cwd: this.cwdPath,
|
|
3059
3318
|
env: {
|
|
@@ -3079,20 +3338,6 @@ class Executor {
|
|
|
3079
3338
|
message: result.message
|
|
3080
3339
|
};
|
|
3081
3340
|
}
|
|
3082
|
-
async#resolveTypecheckWorkerEntry() {
|
|
3083
|
-
const dirname = getDirname2(import.meta.url);
|
|
3084
|
-
const candidates = [
|
|
3085
|
-
path12.join(process.cwd(), "pkgs/@akanjs/devkit/typecheck/typecheck.proc.ts"),
|
|
3086
|
-
path12.join(process.cwd(), "node_modules/@akanjs/devkit/typecheck/typecheck.proc.ts"),
|
|
3087
|
-
path12.join(dirname, "typecheck/typecheck.proc.ts"),
|
|
3088
|
-
path12.join(dirname, "typecheck.proc.js"),
|
|
3089
|
-
path12.join(dirname, "typecheck.proc.ts")
|
|
3090
|
-
];
|
|
3091
|
-
for (const candidate of candidates)
|
|
3092
|
-
if (await Bun.file(candidate).exists())
|
|
3093
|
-
return candidate;
|
|
3094
|
-
throw new Error(`[devkit] typecheck worker entry not found; looked in: ${candidates.join(", ")}`);
|
|
3095
|
-
}
|
|
3096
3341
|
getLinter() {
|
|
3097
3342
|
this.linter ??= new Linter(this.cwdPath);
|
|
3098
3343
|
return this.linter;
|
|
@@ -3124,8 +3369,12 @@ class WorkspaceExecutor extends Executor {
|
|
|
3124
3369
|
} = {}) {
|
|
3125
3370
|
return new WorkspaceExecutor({ workspaceRoot, repoName });
|
|
3126
3371
|
}
|
|
3372
|
+
static publicEnvPin = null;
|
|
3127
3373
|
static getBaseDevEnv(envPath) {
|
|
3128
|
-
const sourceEnv =
|
|
3374
|
+
const sourceEnv = {
|
|
3375
|
+
...envPath ? { ...process.env, ...parseEnvFile(envPath) } : process.env,
|
|
3376
|
+
...WorkspaceExecutor.publicEnvPin
|
|
3377
|
+
};
|
|
3129
3378
|
const appName = sourceEnv.AKAN_PUBLIC_APP_NAME;
|
|
3130
3379
|
const workspaceRoot = sourceEnv.AKAN_WORKSPACE_ROOT;
|
|
3131
3380
|
const workspaceId = sourceEnv.AKAN_WORKSPACE_ID;
|
|
@@ -3213,7 +3462,7 @@ class WorkspaceExecutor extends Executor {
|
|
|
3213
3462
|
await Promise.all(dirs.map(async (dir) => {
|
|
3214
3463
|
if (dir.includes("_") || AVOID_DIRS.includes(dir))
|
|
3215
3464
|
return;
|
|
3216
|
-
const dirPath =
|
|
3465
|
+
const dirPath = path13.join(dirname, dir);
|
|
3217
3466
|
if ((await stat2(dirPath)).isDirectory()) {
|
|
3218
3467
|
results.push(`${prefix}${dir}`);
|
|
3219
3468
|
if (maxDepth > 0)
|
|
@@ -3250,9 +3499,9 @@ class WorkspaceExecutor extends Executor {
|
|
|
3250
3499
|
await Promise.all(dirs.map(async (dir) => {
|
|
3251
3500
|
if (AVOID_DIRS.includes(dir))
|
|
3252
3501
|
return;
|
|
3253
|
-
const dirPath =
|
|
3502
|
+
const dirPath = path13.join(dirname, dir);
|
|
3254
3503
|
if (await FileSys2.dirExists(dirPath)) {
|
|
3255
|
-
const hasTargetFile = await FileSys2.fileExists(
|
|
3504
|
+
const hasTargetFile = await FileSys2.fileExists(path13.join(dirPath, targetFilename));
|
|
3256
3505
|
if (hasTargetFile)
|
|
3257
3506
|
results.push(`${prefix}${dir}`);
|
|
3258
3507
|
if (maxDepth > 0)
|
|
@@ -3311,7 +3560,7 @@ class SysExecutor extends Executor {
|
|
|
3311
3560
|
return [];
|
|
3312
3561
|
const violations = [];
|
|
3313
3562
|
for (const entry of await this.getAllFiles("**/*", { cwd: this.getPath("page") })) {
|
|
3314
|
-
const relativePath = entry.split(
|
|
3563
|
+
const relativePath = entry.split(path13.sep).join("/");
|
|
3315
3564
|
const reason = getPageSourceFileViolation(relativePath);
|
|
3316
3565
|
if (reason)
|
|
3317
3566
|
violations.push({ relativePath: `page/${relativePath}`, reason });
|
|
@@ -3406,21 +3655,21 @@ class SysExecutor extends Executor {
|
|
|
3406
3655
|
await this.setPackageJson({ ...packageJson, dependencies, devDependencies });
|
|
3407
3656
|
}
|
|
3408
3657
|
async getLocalFile(targetPath) {
|
|
3409
|
-
const filePath =
|
|
3658
|
+
const filePath = path13.isAbsolute(targetPath) ? targetPath : `${this.type}s/${this.name}/${targetPath}`;
|
|
3410
3659
|
const content = await this.workspace.readFile(filePath);
|
|
3411
3660
|
return { filePath, content };
|
|
3412
3661
|
}
|
|
3413
3662
|
async getDatabaseModules() {
|
|
3414
|
-
return (await this.readdir("lib")).filter((name) => !name.startsWith("_") && !name.endsWith(".ts")).filter((name) =>
|
|
3663
|
+
return (await this.readdir("lib")).filter((name) => !name.startsWith("_") && !name.endsWith(".ts")).filter((name) => existsSync3(`${this.cwdPath}/lib/${name}/${name}.constant.ts`));
|
|
3415
3664
|
}
|
|
3416
3665
|
async getServiceModules() {
|
|
3417
|
-
return (await this.readdir("lib")).filter((name) => name.startsWith("_") && !name.startsWith("__")).filter((name) =>
|
|
3666
|
+
return (await this.readdir("lib")).filter((name) => name.startsWith("_") && !name.startsWith("__")).filter((name) => existsSync3(`${this.cwdPath}/lib/${name}/${name.slice(1)}.service.ts`));
|
|
3418
3667
|
}
|
|
3419
3668
|
async getScalarModules() {
|
|
3420
|
-
return (await this.readdir("lib/__scalar")).filter((name) => !name.startsWith("_")).filter((name) =>
|
|
3669
|
+
return (await this.readdir("lib/__scalar")).filter((name) => !name.startsWith("_")).filter((name) => existsSync3(`${this.cwdPath}/lib/__scalar/${name}/${name}.constant.ts`));
|
|
3421
3670
|
}
|
|
3422
3671
|
async#getComponentModules(role) {
|
|
3423
|
-
return (await this.readdir("lib")).filter((name) => !name.startsWith("_") && !name.endsWith(".ts")).filter((name) =>
|
|
3672
|
+
return (await this.readdir("lib")).filter((name) => !name.startsWith("_") && !name.endsWith(".ts")).filter((name) => existsSync3(`${this.cwdPath}/lib/${name}/${capitalize(name)}.${role}.tsx`));
|
|
3424
3673
|
}
|
|
3425
3674
|
async#getComponentSources(role) {
|
|
3426
3675
|
const modules = await this.#getComponentModules(role);
|
|
@@ -3494,19 +3743,19 @@ class AppExecutor extends SysExecutor {
|
|
|
3494
3743
|
static from(executor, name) {
|
|
3495
3744
|
return new AppExecutor({ workspace: executor instanceof WorkspaceExecutor ? executor : executor.workspace, name });
|
|
3496
3745
|
}
|
|
3497
|
-
static #keptOnStart = [
|
|
3498
|
-
static #legacyNative =
|
|
3746
|
+
static #keptOnStart = [path13.join(".akan", "cache", "bin")];
|
|
3747
|
+
static #legacyNative = path13.join(".akan", "native");
|
|
3499
3748
|
async#removeDevOutput(dir = ".akan") {
|
|
3500
3749
|
const entries = await readDirEntries(this.getPath(dir)).catch(() => []);
|
|
3501
3750
|
await Promise.all(entries.map(async (name) => {
|
|
3502
|
-
const entry =
|
|
3751
|
+
const entry = path13.join(dir, name);
|
|
3503
3752
|
if (entry === AppExecutor.#legacyNative) {
|
|
3504
3753
|
this.logger.warn(`apps/${this.name}/.akan/native holds native builds from before they moved to dist/native/${this.name}: upload any release waiting in its updates folders, then delete it.`);
|
|
3505
3754
|
return;
|
|
3506
3755
|
}
|
|
3507
3756
|
if (AppExecutor.#keptOnStart.includes(entry))
|
|
3508
3757
|
return;
|
|
3509
|
-
if (AppExecutor.#keptOnStart.some((kept) => kept.startsWith(`${entry}${
|
|
3758
|
+
if (AppExecutor.#keptOnStart.some((kept) => kept.startsWith(`${entry}${path13.sep}`)))
|
|
3510
3759
|
return await this.#removeDevOutput(entry);
|
|
3511
3760
|
await this.removeDir(entry);
|
|
3512
3761
|
}));
|
|
@@ -3630,13 +3879,13 @@ class AppExecutor extends SysExecutor {
|
|
|
3630
3879
|
absolute: false,
|
|
3631
3880
|
onlyFiles: true
|
|
3632
3881
|
})) {
|
|
3633
|
-
const segments = rel.split(
|
|
3882
|
+
const segments = rel.split(path13.sep);
|
|
3634
3883
|
if (segments.some((s) => s === "node_modules"))
|
|
3635
3884
|
continue;
|
|
3636
3885
|
const posix = `${root.keyPrefix}${segments.join("/")}`;
|
|
3637
3886
|
if (!root.keyPrefix && libKeyPrefixes.some((prefix) => posix.startsWith(prefix)))
|
|
3638
3887
|
continue;
|
|
3639
|
-
const absPath =
|
|
3888
|
+
const absPath = path13.join(root.dir, ...segments);
|
|
3640
3889
|
validatePageSourceFile(posix, { filePath: absPath });
|
|
3641
3890
|
if (!isRouteSourceFile(posix))
|
|
3642
3891
|
continue;
|
|
@@ -3777,12 +4026,12 @@ class AppExecutor extends SysExecutor {
|
|
|
3777
4026
|
static async#linkLibAsset(targetPath, linkPath) {
|
|
3778
4027
|
const isWindows = process.platform === "win32";
|
|
3779
4028
|
try {
|
|
3780
|
-
const target = isWindows ? targetPath :
|
|
4029
|
+
const target = isWindows ? targetPath : path13.relative(path13.dirname(linkPath), targetPath);
|
|
3781
4030
|
await symlink(target, linkPath, isWindows ? "junction" : "dir");
|
|
3782
4031
|
} catch (error) {
|
|
3783
4032
|
if (!isWindows)
|
|
3784
4033
|
throw error;
|
|
3785
|
-
await
|
|
4034
|
+
await mkdir3(linkPath, { recursive: true });
|
|
3786
4035
|
await cpEntry(targetPath, linkPath, { recursive: true, dereference: true, force: true });
|
|
3787
4036
|
}
|
|
3788
4037
|
}
|
|
@@ -3820,7 +4069,7 @@ class AppExecutor extends SysExecutor {
|
|
|
3820
4069
|
await this.writeFile(rel, content, opts);
|
|
3821
4070
|
},
|
|
3822
4071
|
readEnvClient: async () => {
|
|
3823
|
-
const envClientPath =
|
|
4072
|
+
const envClientPath = path13.join(this.cwdPath, "env", "env.client.ts");
|
|
3824
4073
|
if (!await FileSys2.fileExists(envClientPath))
|
|
3825
4074
|
return null;
|
|
3826
4075
|
try {
|
|
@@ -3914,7 +4163,7 @@ class PkgExecutor extends Executor {
|
|
|
3914
4163
|
return overrideVersion;
|
|
3915
4164
|
try {
|
|
3916
4165
|
const packageJsonPath = `pkgs/${dep}/package.json`;
|
|
3917
|
-
if (!await Bun.file(
|
|
4166
|
+
if (!await Bun.file(path13.join(this.workspace.workspaceRoot, packageJsonPath)).exists())
|
|
3918
4167
|
return;
|
|
3919
4168
|
const packageJson = await this.workspace.readJson(packageJsonPath);
|
|
3920
4169
|
if (packageJson.name === dep)
|
|
@@ -4007,4 +4256,4 @@ class ModuleExecutor extends Executor {
|
|
|
4007
4256
|
}
|
|
4008
4257
|
}
|
|
4009
4258
|
|
|
4010
|
-
export { AGENT_BLOCK_START, AGENT_BLOCK_END, stampBlockVersion, extractBlockVersion, readDevkitVersion, upsertAgentBlock, extractAgentBlock, renderRecipeEntries, collectScopeRecipeSources, renderScopeAgentBlock, CLAUDE_COMMENT_RULE, binPlatforms, ServerEnvProfile, AkanAppConfig, AkanWorkspaceConfig, AsyncDefaultExportDetector, resolveSsrPageEntriesForApp, CodegenLock, Linter, LibSource, formatLibStatuses, rootAllowedFiles, rootAllowedDirs, isAllowedLibFacetRootFile, libFacetRootHintOf, barrelFacetDirs, facetIndexShadowOf, rootEntryHintOf, isScannedRootEntry, AppInfo, Spinner, Executor, WorkspaceExecutor, SysExecutor, AppExecutor, LibExecutor, PkgExecutor, ModuleExecutor };
|
|
4259
|
+
export { AGENT_BLOCK_START, AGENT_BLOCK_END, stampBlockVersion, extractBlockVersion, readDevkitVersion, upsertAgentBlock, extractAgentBlock, renderRecipeEntries, collectScopeRecipeSources, renderScopeAgentBlock, CLAUDE_COMMENT_RULE, binPlatforms, ServerEnvProfile, AkanAppConfig, AkanWorkspaceConfig, AsyncDefaultExportDetector, resolveSsrPageEntriesForApp, CodegenLock, Linter, LibSource, formatLibStatuses, rootAllowedFiles, rootAllowedDirs, isAllowedLibFacetRootFile, libFacetRootHintOf, barrelFacetDirs, facetIndexShadowOf, rootEntryHintOf, isScannedRootEntry, AppInfo, Spinner, ProjectTypecheck2, Executor, WorkspaceExecutor, SysExecutor, AppExecutor, LibExecutor, PkgExecutor, ModuleExecutor };
|