@akanjs/cli 3.1.0-rc.1 → 3.1.0-rc.11
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-kyprmy06.js → CodeAgent-rkjyxgec.js} +2 -2
- package/{CodeTui-21mtze3j.js → CodeTui-h7pyj6xa.js} +1 -1
- package/TunnelShare-661cxz5e.js +14 -0
- package/{agent.command-f3gsc1gd.js → agent.command-3qxf4xcj.js} +5 -5
- package/akanCode.js +2 -2
- package/{application.command-5s780vnz.js → application.command-m1kcd7jf.js} +54 -42
- package/{applicationBuildRunner-1yaqsrdq.js → applicationBuildRunner-dj0xkrep.js} +26 -33
- package/buildBatch.proc.js +24 -15
- package/{cloud.command-yj6xb2a7.js → cloud.command-9cp1t0f5.js} +60 -67
- package/cloud.script-dgstqt1m.js +24 -0
- package/{code.command-3wkn4ja5.js → code.command-0ad6gw2a.js} +6 -6
- package/{context.command-9jjn3gcw.js → context.command-0qx447mb.js} +17 -13
- package/context.runner-1vsjztjg.js +25 -0
- package/{dependencyScanner-06z2dmrr.js → dependencyScanner-3afj2vt1.js} +1 -1
- package/{guideline.command-dtczrqpf.js → guideline.command-cv5bmrx2.js} +2 -2
- package/guidelines/agentRule/agentRule.instruction.md +3 -3
- package/guidelines/conventions/conventions.instruction.md +457 -1071
- package/guidelines/cssRule/cssRule.instruction.md +28 -7
- package/guidelines/fieldRule/fieldRule.instruction.md +62 -0
- package/guidelines/framework/framework.instruction.md +4 -7
- package/guidelines/mcpRule/mcpRule.instruction.md +43 -20
- package/guidelines/modelConstant/modelConstant.instruction.md +2 -1
- package/guidelines/modelStore/modelStore.instruction.md +9 -0
- package/guidelines/pageRule/pageRule.instruction.md +80 -0
- package/guidelines/queryRule/queryRule.instruction.md +44 -11
- package/guidelines/runtimeRule/runtimeRule.instruction.md +113 -17
- package/guidelines/ssrRule/ssrRule.instruction.md +13 -0
- package/guidelines/transportRule/transportRule.instruction.md +135 -13
- package/guidelines/workspaceOnboarding/workspaceOnboarding.instruction.md +4 -4
- package/guidelines/workspaceRecipes/workspaceRecipes.instruction.md +68 -7
- package/incrementalBuilder.proc.js +10 -7
- package/index-192ha4xh.js +1173 -0
- package/{index-c76k723e.js → index-1p98exm2.js} +2 -2
- package/{index-rwq3ekqc.js → index-1xj2t7cb.js} +7 -7
- package/{index-rtct7d43.js → index-20bxxyvn.js} +80 -80
- package/{index-qt22xstj.js → index-35hc95h5.js} +5 -3
- package/{index-8r4d7cmt.js → index-4mhy6qme.js} +4 -4
- package/{index-ww9bhp67.js → index-4pf8g7ff.js} +1 -1
- package/{index-2mx692qq.js → index-5edzv8fn.js} +5 -5
- package/{index-k9rrten6.js → index-5nd1wtkv.js} +92 -94
- package/index-62md4dwy.js +260 -0
- package/index-665zp9rs.js +271 -0
- package/{index-2etcv0sc.js → index-86jt2a0e.js} +1 -1
- package/{index-krq7btbt.js → index-8fp3b47h.js} +419 -418
- package/{index-c2yrsxdh.js → index-9z426msb.js} +109 -328
- package/{index-ctanya41.js → index-awbdfcf2.js} +4 -4
- package/{index-3hx1z96c.js → index-bqbf654f.js} +1 -1
- package/{index-rc5rwyps.js → index-bspm21y1.js} +71 -60
- package/index-cpb3nvma.js +667 -0
- package/{index-jx4bzvwg.js → index-d47ypebv.js} +97 -55
- package/{index-ae5pwhgs.js → index-d72hfmdm.js} +15 -13
- package/{index-e0cvze1s.js → index-fmzcs8v6.js} +4 -4
- package/{index-8ggztp8k.js → index-fyq1z6kt.js} +68 -201
- package/{index-1w2qaee2.js → index-g1mbppa9.js} +331 -259
- package/{index-gnyf1fqy.js → index-j237xtew.js} +6 -6
- package/{index-fm46xrf1.js → index-j4mkhz93.js} +3 -3
- package/{index-6jjtnhkw.js → index-jw1zm273.js} +74 -34
- package/{index-q958tqqg.js → index-m9jj544v.js} +1 -1
- package/index-mdjc93e1.js +33 -0
- package/{index-c1x0be5j.js → index-pydbnfez.js} +2 -2
- package/{index-56d2g8ms.js → index-qwa5k1j8.js} +1 -1
- package/{index-v29746vb.js → index-tamvyvj3.js} +3 -3
- package/{index-05g3jayg.js → index-trpasv9q.js} +260 -259
- package/{index-2ahj896a.js → index-tvehn2sp.js} +1 -1
- package/{index-55mynnqs.js → index-va06nr10.js} +1434 -1173
- package/index-vk0pa0mn.js +240 -0
- package/{index-aprt7pkz.js → index-w44cy4vs.js} +2271 -3369
- package/index.js +22 -22
- package/{library.command-gaga5bmq.js → library.command-erp2f56n.js} +3 -3
- package/{localRegistry.command-vsreyy42.js → localRegistry.command-y176p558.js} +15 -11
- package/{module.command-7vpf6db4.js → module.command-gs2r615e.js} +5 -5
- package/{package.command-twmvvf69.js → package.command-j10r76ax.js} +4 -4
- package/package.json +21 -21
- package/{page.command-5asq7fdf.js → page.command-4h4a8pnh.js} +3 -3
- package/{pipeline.command-d0v38572.js → pipeline.command-a2nrk26m.js} +221 -220
- package/{primitive.command-dyrs2fm8.js → primitive.command-w6t0asqz.js} +6 -6
- package/{projectTypecheck-cvtkrzp2.js → projectTypecheck-f534enqj.js} +1 -1
- package/{quality.command-6emsqpbg.js → quality.command-77x7vbbx.js} +4 -4
- package/{repair.command-8t6aegv5.js → repair.command-y6sstev7.js} +5 -5
- package/{scalar.command-bnggy1x7.js → scalar.command-20b8cy0k.js} +4 -4
- package/{subspace.command-md2teygq.js → subspace.command-5c7yasd7.js} +424 -364
- package/templates/appSample/lib/task/Task.Util.tsx +7 -7
- package/templates/appSample/lib/task/Task.Zone.tsx +1 -1
- package/templates/appSample/ui/QuantityControl.tsx +2 -2
- package/templates/crudSinglePage/page.tsx +1 -1
- package/templates/lib/cnst.ts +0 -1
- package/templates/workspaceRoot/.gitignore.template +23 -52
- package/templates/workspaceRoot/biome.json.template +1 -1
- package/{tunnel.command-jx3tdm2d.js → tunnel.command-2kw8vt9g.js} +8 -4
- package/{workflow.command-jx0wag5r.js → workflow.command-2djwmpqf.js} +10 -10
- package/{workspace.command-yxd7rr7g.js → workspace.command-6qfgq4b4.js} +38 -34
- package/TunnelShare-q4jq08qg.js +0 -9
- package/cloud.script-h6pvcpa2.js +0 -20
- package/context.runner-atcrzz5b.js +0 -21
- package/index-6w0rbr9d.js +0 -68
- package/index-n68jx79w.js +0 -120
- package/index-pgj2rt78.js +0 -404
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
import {
|
|
3
|
+
DesktopBin,
|
|
4
|
+
UpdatePackFolder
|
|
5
|
+
} from "./index-g1mbppa9.js";
|
|
6
|
+
// pkgs/@akanjs/devkit/mobile/desktopServerStage.ts
|
|
7
|
+
import { cp, mkdir, readdir, rm, writeFile } from "fs/promises";
|
|
8
|
+
import path from "path";
|
|
9
|
+
import { toError } from "akanjs/common";
|
|
10
|
+
class DesktopServerStage {
|
|
11
|
+
app;
|
|
12
|
+
static uncarried = new Set([
|
|
13
|
+
"Dockerfile",
|
|
14
|
+
"package.json",
|
|
15
|
+
"rscWorker.js",
|
|
16
|
+
"console.js",
|
|
17
|
+
"console-runtime.js",
|
|
18
|
+
"csr",
|
|
19
|
+
"public",
|
|
20
|
+
".akan"
|
|
21
|
+
]);
|
|
22
|
+
static rscRenderer = "react-server-dom-webpack";
|
|
23
|
+
dir;
|
|
24
|
+
constructor(app) {
|
|
25
|
+
this.app = app;
|
|
26
|
+
this.dir = path.join(app.cwdPath, ".akan", "desktop", "server");
|
|
27
|
+
}
|
|
28
|
+
static assertCarriable(config) {
|
|
29
|
+
if (!config.database.modes.includes("single"))
|
|
30
|
+
throw new Error(`desktop.server in native puts the server in the app, where only database mode single runs (no Redis or Postgres); apps/${config.app.name}/akan.config.ts declares ${config.database.modes.join(", ")}.`);
|
|
31
|
+
}
|
|
32
|
+
static env(config, environment, { modules = [], env = {} } = {}) {
|
|
33
|
+
const { PORT: _port, AKAN_LOG_TO_FILE: _fileLog, ...image } = config.getProductionEnv(environment);
|
|
34
|
+
return {
|
|
35
|
+
...image,
|
|
36
|
+
...env,
|
|
37
|
+
AKAN_PUBLIC_OPERATION_MODE: "edge",
|
|
38
|
+
AKAN_DATABASE_MODE: "single",
|
|
39
|
+
AKAN_DATABASE_MODES: "single",
|
|
40
|
+
AKAN_SSR: "false",
|
|
41
|
+
AKAN_CSR: "false",
|
|
42
|
+
AKAN_MCP: "false",
|
|
43
|
+
AKAN_SHUTDOWN_TIMEOUT_MS: "1000",
|
|
44
|
+
...modules.length ? { AKAN_MODULES: modules.join(",") } : {}
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
static imageStepsNotice(config, carried) {
|
|
48
|
+
const steps = typeof config.docker === "string" ? null : config.docker.preRuns.length + config.docker.postRuns.length;
|
|
49
|
+
if (steps === 0)
|
|
50
|
+
return null;
|
|
51
|
+
const image = steps === null ? `apps/${config.app.name}/akan.config.ts writes its own Dockerfile` : `The image runs ${steps} docker step${steps === 1 ? "" : "s"} from the app and its libs`;
|
|
52
|
+
return carried.length ? {
|
|
53
|
+
level: "info",
|
|
54
|
+
message: `${image}; the desktop app's server runs none of them and carries only bin: ${carried.join(", ")}.`
|
|
55
|
+
} : {
|
|
56
|
+
level: "warn",
|
|
57
|
+
message: `${image}, and a desktop app's server runs none of them: declare in bin the executables its code spawns, or those calls fail on the user's computer.`
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
static packageJson(config, built, omit = []) {
|
|
61
|
+
const otherDrivers = new Set(config.database.modes.filter((mode) => mode !== "single").flatMap((mode) => config.getDatabaseModeRuntimePackages(mode)));
|
|
62
|
+
const dependencies = Object.entries(built.dependencies ?? {}).filter(([name]) => !omit.includes(name) && (config.externalLibs.includes(name) || name !== DesktopServerStage.rscRenderer && !otherDrivers.has(name)));
|
|
63
|
+
return { ...built, dependencies: Object.fromEntries(dependencies) };
|
|
64
|
+
}
|
|
65
|
+
static async dependentsOf(nodeModules, name) {
|
|
66
|
+
const dependents = new Set;
|
|
67
|
+
for await (const manifest of new Bun.Glob("**/package.json").scan({ cwd: nodeModules, onlyFiles: true })) {
|
|
68
|
+
const json = await Bun.file(path.join(nodeModules, manifest)).json().catch(() => null);
|
|
69
|
+
if (!json?.name || json.name === name)
|
|
70
|
+
continue;
|
|
71
|
+
const declared = { ...json.dependencies, ...json.optionalDependencies, ...json.peerDependencies };
|
|
72
|
+
if (name in declared)
|
|
73
|
+
dependents.add(json.name);
|
|
74
|
+
}
|
|
75
|
+
return [...dependents].sort();
|
|
76
|
+
}
|
|
77
|
+
static installArgs(arch) {
|
|
78
|
+
return ["install", "--production", "--prefer-offline", ...arch && arch !== process.arch ? [`--cpu=${arch}`] : []];
|
|
79
|
+
}
|
|
80
|
+
async install(arch, omit = []) {
|
|
81
|
+
const config = await this.app.getConfig();
|
|
82
|
+
DesktopServerStage.assertCarriable(config);
|
|
83
|
+
await rm(this.dir, { recursive: true, force: true });
|
|
84
|
+
await mkdir(this.dir, { recursive: true });
|
|
85
|
+
const built = config.getProductionPackageJson();
|
|
86
|
+
for (const name of omit.filter((name) => !(name in (built.dependencies ?? {}))))
|
|
87
|
+
this.app.logger.warn(`desktop.server.omit names ${name}, which the server does not depend on directly.`);
|
|
88
|
+
await writeFile(path.join(this.dir, "package.json"), JSON.stringify(DesktopServerStage.packageJson(config, built, omit), null, 2));
|
|
89
|
+
try {
|
|
90
|
+
await this.app.spawn(process.execPath, DesktopServerStage.installArgs(arch), { cwd: this.dir });
|
|
91
|
+
} catch (error) {
|
|
92
|
+
throw new Error(`The desktop app's server could not install its packages in ${this.dir}: it needs the npm registry, or a Bun cache that already holds every one of them.
|
|
93
|
+
${toError(error).message}`, { cause: error });
|
|
94
|
+
}
|
|
95
|
+
await rm(path.join(this.dir, "node_modules", ".bin"), { recursive: true, force: true });
|
|
96
|
+
await this.#assertOmitted(omit);
|
|
97
|
+
}
|
|
98
|
+
async#assertOmitted(omit) {
|
|
99
|
+
const nodeModules = path.join(this.dir, "node_modules");
|
|
100
|
+
const kept = [];
|
|
101
|
+
for (const name of omit) {
|
|
102
|
+
if (!await Bun.file(path.join(nodeModules, name, "package.json")).exists())
|
|
103
|
+
continue;
|
|
104
|
+
const dependents = await DesktopServerStage.dependentsOf(nodeModules, name);
|
|
105
|
+
kept.push(`${name} (needed by ${dependents.join(", ") || "a package that names it"})`);
|
|
106
|
+
}
|
|
107
|
+
if (kept.length)
|
|
108
|
+
throw new Error(`desktop.server.omit leaves out packages another dependency still installs: ${kept.join("; ")}. Omit those too, or keep the package.`);
|
|
109
|
+
}
|
|
110
|
+
async prepare(environment, run = {}) {
|
|
111
|
+
const config = await this.app.getConfig();
|
|
112
|
+
const dist = this.app.dist.cwdPath;
|
|
113
|
+
await mkdir(this.dir, { recursive: true });
|
|
114
|
+
for (const name of await readdir(dist))
|
|
115
|
+
if (!DesktopServerStage.uncarried.has(name))
|
|
116
|
+
await cp(path.join(dist, name), path.join(this.dir, name), { recursive: true });
|
|
117
|
+
const scanInfo = this.app.getScanInfo({ allowEmpty: true }) ?? await this.app.scan({ write: false });
|
|
118
|
+
const carried = [...DesktopBin.select(config, scanInfo.getLibs()).keys()];
|
|
119
|
+
const notice = DesktopServerStage.imageStepsNotice(config, carried);
|
|
120
|
+
if (notice)
|
|
121
|
+
this.app.logger[notice.level](notice.message);
|
|
122
|
+
return {
|
|
123
|
+
dir: this.dir,
|
|
124
|
+
entry: "main.js",
|
|
125
|
+
env: DesktopServerStage.env(config, environment, run),
|
|
126
|
+
...run.restart !== undefined ? { restart: run.restart } : {},
|
|
127
|
+
...run.watchdog ? { watchdog: run.watchdog } : {}
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
// pkgs/@akanjs/devkit/mobile/placeholderAppId.ts
|
|
132
|
+
var PLACEHOLDER_APP_IDS = [
|
|
133
|
+
"com.myapp.app",
|
|
134
|
+
"com.myorg.myapp",
|
|
135
|
+
"com.example.app",
|
|
136
|
+
"com.example.myapp"
|
|
137
|
+
];
|
|
138
|
+
var placeholderAppIdSegment = /^(example|examples|myorg|myapp|mycompany|myorganization|changeme|todo|sample|test)$/;
|
|
139
|
+
var isPlaceholderAppId = (appId) => {
|
|
140
|
+
const normalized = appId?.trim().toLowerCase() ?? "";
|
|
141
|
+
if (!normalized)
|
|
142
|
+
return true;
|
|
143
|
+
if (PLACEHOLDER_APP_IDS.includes(normalized))
|
|
144
|
+
return true;
|
|
145
|
+
return normalized.split(".").some((segment) => placeholderAppIdSegment.test(segment));
|
|
146
|
+
};
|
|
147
|
+
// pkgs/@akanjs/devkit/mobile/updateUploader.ts
|
|
148
|
+
import { readdir as readdir2 } from "fs/promises";
|
|
149
|
+
import path2 from "path";
|
|
150
|
+
import { isRecord, trimTrailingSlashes } from "akanjs/common";
|
|
151
|
+
class UpdateUploader {
|
|
152
|
+
bucket;
|
|
153
|
+
prefix;
|
|
154
|
+
static #manifest = /\.json(?:\.sig)?$/;
|
|
155
|
+
static parseTarget(target) {
|
|
156
|
+
const match = /^s3:\/\/([^/]+)(?:\/(.*))?$/.exec(target.trim());
|
|
157
|
+
if (!match?.[1])
|
|
158
|
+
throw new Error(`--upload takes s3://<bucket>/<prefix>, not ${JSON.stringify(target)}.`);
|
|
159
|
+
return { bucket: match[1], prefix: trimTrailingSlashes(match[2] ?? "") };
|
|
160
|
+
}
|
|
161
|
+
static order(files) {
|
|
162
|
+
const manifests = files.filter((file) => UpdateUploader.#manifest.test(file));
|
|
163
|
+
return {
|
|
164
|
+
releases: files.filter((file) => !UpdateUploader.#manifest.test(file)).sort((a, b) => a.localeCompare(b)),
|
|
165
|
+
manifests: manifests.sort((a, b) => Number(a.endsWith(".json")) - Number(b.endsWith(".json")) || a.localeCompare(b))
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
static async releaseFiles(updatesDir, { dir, channel, bundle }) {
|
|
169
|
+
const keyOf = (file) => UpdateUploader.#keyOf(updatesDir, dir, file);
|
|
170
|
+
const desktop = await UpdateUploader.#read(path2.join(dir, `${channel}.json`));
|
|
171
|
+
if (desktop?.bundle === bundle) {
|
|
172
|
+
const archive = desktop.archive?.url;
|
|
173
|
+
const patches = UpdateUploader.#field(desktop.patches, "url");
|
|
174
|
+
return [archive, ...patches, `${channel}.json.sig`, `${channel}.json`].map(keyOf);
|
|
175
|
+
}
|
|
176
|
+
const names = await readdir2(path2.join(dir, channel)).catch(() => []);
|
|
177
|
+
for (const name of names.filter((name) => name.endsWith(".json"))) {
|
|
178
|
+
const envelope = await UpdateUploader.#read(path2.join(dir, channel, name));
|
|
179
|
+
const manifest = typeof envelope?.manifest === "string" ? UpdateUploader.#parse(Buffer.from(envelope.manifest, "base64").toString("utf8")) : null;
|
|
180
|
+
if (manifest?.bundle !== bundle)
|
|
181
|
+
continue;
|
|
182
|
+
const hashes = [...new Set(UpdateUploader.#field(manifest.files, "sha256"))];
|
|
183
|
+
if (!hashes.every((hash) => typeof hash === "string" && /^[0-9a-f]{64}$/.test(hash)))
|
|
184
|
+
throw new Error(`${path2.join(dir, channel, name)} lists a file whose sha256 is not 64 hex digits; nothing was uploaded.`);
|
|
185
|
+
return [...hashes.map((hash) => `${UpdatePackFolder.filesDir}/${hash}`), `${channel}/${name}`].map(keyOf);
|
|
186
|
+
}
|
|
187
|
+
throw new Error(`${dir} holds no ${channel} manifest of release ${bundle}; nothing was uploaded.`);
|
|
188
|
+
}
|
|
189
|
+
static #keyOf(updatesDir, dir, file) {
|
|
190
|
+
const resolved = typeof file === "string" ? path2.resolve(dir, file) : "";
|
|
191
|
+
const key = path2.relative(updatesDir, resolved);
|
|
192
|
+
if (!resolved.startsWith(`${path2.resolve(dir)}${path2.sep}`) || key.startsWith("..") || path2.isAbsolute(key))
|
|
193
|
+
throw new Error(`The release in ${dir} names ${typeof file === "string" ? JSON.stringify(file) : "a file with no path"}, which is not inside that folder; nothing was uploaded.`);
|
|
194
|
+
return key.split(path2.sep).join("/");
|
|
195
|
+
}
|
|
196
|
+
static #field(list, key) {
|
|
197
|
+
return Array.isArray(list) ? list.map((item) => isRecord(item) ? item[key] : undefined) : [];
|
|
198
|
+
}
|
|
199
|
+
static async#read(file) {
|
|
200
|
+
const text = await Bun.file(file).text().catch(() => "");
|
|
201
|
+
return UpdateUploader.#parse(text);
|
|
202
|
+
}
|
|
203
|
+
static #parse(text) {
|
|
204
|
+
try {
|
|
205
|
+
const value = JSON.parse(text);
|
|
206
|
+
return isRecord(value) ? value : null;
|
|
207
|
+
} catch {
|
|
208
|
+
return null;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
constructor(bucket, prefix) {
|
|
212
|
+
this.bucket = bucket;
|
|
213
|
+
this.prefix = prefix;
|
|
214
|
+
}
|
|
215
|
+
static forTarget(target) {
|
|
216
|
+
const { bucket, prefix } = UpdateUploader.parseTarget(target);
|
|
217
|
+
return new UpdateUploader(new Bun.S3Client({ bucket }), prefix);
|
|
218
|
+
}
|
|
219
|
+
async upload(updatesDir, release) {
|
|
220
|
+
const { releases, manifests } = UpdateUploader.order(await UpdateUploader.releaseFiles(updatesDir, release));
|
|
221
|
+
const keyOf = (file) => this.prefix ? `${this.prefix}/${file}` : file;
|
|
222
|
+
let uploaded = 0;
|
|
223
|
+
let skipped = 0;
|
|
224
|
+
for (const file of releases) {
|
|
225
|
+
if (await this.bucket.exists(keyOf(file))) {
|
|
226
|
+
skipped++;
|
|
227
|
+
continue;
|
|
228
|
+
}
|
|
229
|
+
await this.bucket.write(keyOf(file), Bun.file(path2.join(updatesDir, file)));
|
|
230
|
+
uploaded++;
|
|
231
|
+
}
|
|
232
|
+
for (const file of manifests) {
|
|
233
|
+
const type = file.endsWith(".json") ? "application/json" : "text/plain";
|
|
234
|
+
await this.bucket.write(keyOf(file), Bun.file(path2.join(updatesDir, file)), { type });
|
|
235
|
+
uploaded++;
|
|
236
|
+
}
|
|
237
|
+
return { uploaded, skipped };
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
export { DesktopServerStage, isPlaceholderAppId, UpdateUploader };
|