@akanjs/cli 3.0.2 → 3.1.0-rc.1
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-szbjv4gv.js → CodeAgent-kyprmy06.js} +2 -2
- package/{CodeTui-5nw86ab6.js → CodeTui-21mtze3j.js} +1 -1
- package/{TunnelShare-cwh9ezsm.js → TunnelShare-q4jq08qg.js} +2 -2
- package/{agent.command-np1dffkb.js → agent.command-f3gsc1gd.js} +5 -5
- package/akanCode.js +2 -2
- package/{application.command-gk69v1xs.js → application.command-5s780vnz.js} +37 -15
- package/{applicationBuildRunner-wx09n9g6.js → applicationBuildRunner-1yaqsrdq.js} +115 -103
- package/buildBatch.proc.js +16 -5
- package/cloud.command-yj6xb2a7.js +192 -0
- package/cloud.script-h6pvcpa2.js +20 -0
- package/{code.command-hmtx14b2.js → code.command-3wkn4ja5.js} +6 -6
- package/commandManifest.json +1 -1
- package/{context.command-6aqvdx43.js → context.command-9jjn3gcw.js} +13 -13
- package/context.runner-atcrzz5b.js +21 -0
- package/{guideline.command-axvf68gc.js → guideline.command-dtczrqpf.js} +2 -2
- package/guidelines/agentRule/agentRule.instruction.md +10 -2
- package/guidelines/conventions/conventions.instruction.md +70 -25
- package/guidelines/cssRule/cssRule.instruction.md +19 -0
- package/guidelines/mcpRule/mcpRule.instruction.md +72 -12
- package/guidelines/queryRule/queryRule.instruction.md +11 -1
- package/guidelines/runtimeRule/runtimeRule.instruction.md +97 -16
- package/guidelines/ssrRule/ssrRule.instruction.md +10 -7
- package/guidelines/transportRule/transportRule.instruction.md +19 -0
- package/guidelines/workspaceRecipes/workspaceRecipes.instruction.md +6 -6
- package/incrementalBuilder.proc.js +24 -3
- package/{index-h9ht6s60.js → index-05g3jayg.js} +1 -1
- package/{index-jk9xnck6.js → index-1w2qaee2.js} +240 -133
- package/{index-g38nhv87.js → index-2etcv0sc.js} +1 -1
- package/{index-zyw622y1.js → index-2mx692qq.js} +3 -3
- package/{index-50wb2a26.js → index-55mynnqs.js} +354 -129
- package/{index-2wc41k5w.js → index-56d2g8ms.js} +2 -2
- package/{index-q6ft9fkc.js → index-6jjtnhkw.js} +8 -3
- package/index-6w0rbr9d.js +68 -0
- package/{index-hxnnfy57.js → index-8ggztp8k.js} +204 -8
- package/{index-zwd84m0w.js → index-8r4d7cmt.js} +4 -4
- package/{index-j38qkvkk.js → index-ae5pwhgs.js} +12 -10
- package/{index-gtjgnv36.js → index-aprt7pkz.js} +6 -9
- package/{index-1bjkfvm9.js → index-c1x0be5j.js} +2 -2
- package/{index-z750gk1j.js → index-c2yrsxdh.js} +13 -7
- package/{index-cb6yc5mb.js → index-c76k723e.js} +2 -2
- package/{index-6keyf4sg.js → index-ctanya41.js} +4 -4
- package/{index-f4e08ptp.js → index-e0cvze1s.js} +4 -4
- package/{index-hp3stk1m.js → index-fm46xrf1.js} +3 -3
- package/{index-3xmzr1p1.js → index-gnyf1fqy.js} +2 -2
- package/{index-nwd6qzvv.js → index-jx4bzvwg.js} +138 -55
- package/{index-jb0hmt3k.js → index-krq7btbt.js} +3 -3
- package/{index-94s080qm.js → index-pgj2rt78.js} +139 -1
- package/index-q958tqqg.js +77 -0
- package/{index-50jjwg8y.js → index-qt22xstj.js} +1 -1
- package/index-rc5rwyps.js +201 -0
- package/{index-xwprn9nr.js → index-rtct7d43.js} +1 -1
- package/{index-bsv901fd.js → index-rwq3ekqc.js} +7 -7
- package/{index-1gs9g12n.js → index-v29746vb.js} +4 -4
- package/index-ww9bhp67.js +62 -0
- package/index.js +22 -21
- package/{library.command-9m2rdwwz.js → library.command-gaga5bmq.js} +3 -3
- package/{localRegistry.command-kda81jyk.js → localRegistry.command-vsreyy42.js} +9 -9
- package/{module.command-q63dffya.js → module.command-7vpf6db4.js} +5 -5
- package/{package.command-y2jh0m99.js → package.command-twmvvf69.js} +3 -3
- package/package.json +2 -2
- package/{page.command-f3e4cwhh.js → page.command-5asq7fdf.js} +3 -3
- package/pipeline.command-d0v38572.js +1334 -0
- package/{primitive.command-r4kq75qj.js → primitive.command-dyrs2fm8.js} +6 -6
- package/projectTypecheck-cvtkrzp2.js +7 -0
- package/{quality.command-f7gzh26z.js → quality.command-6emsqpbg.js} +5 -4
- package/{repair.command-0qmyr6qa.js → repair.command-8t6aegv5.js} +5 -5
- package/{scalar.command-kddn7av4.js → scalar.command-bnggy1x7.js} +4 -4
- package/{subspace.command-ft65qvc4.js → subspace.command-md2teygq.js} +158 -91
- package/templates/app/env/env.client.type.ts +2 -3
- package/templates/app/page/styles.css.template +8 -0
- package/templates/appSample/page/task/[taskId]/_index.tsx +2 -2
- package/templates/appSample/page/task/[taskId]/edit.tsx +2 -2
- package/templates/appSample/page/task/_index.tsx +2 -2
- package/templates/crudPages/[__model__Id]/edit/page.tsx +2 -2
- package/templates/crudPages/[__model__Id]/page.tsx +3 -3
- package/templates/crudPages/page.tsx +2 -2
- package/templates/crudSinglePage/page.tsx +3 -3
- package/templates/lib/cnst.ts +2 -2
- package/templates/lib/sig.ts +3 -3
- package/{tunnel.command-b3ymdk4f.js → tunnel.command-jx3tdm2d.js} +16 -13
- package/{workflow.command-z1srcdfw.js → workflow.command-jx0wag5r.js} +10 -10
- package/{workspace.command-vp6vmx3z.js → workspace.command-yxd7rr7g.js} +57 -27
- package/cloud.command-tw06613r.js +0 -95
- package/cloud.script-64gzba8r.js +0 -20
- package/context.runner-hnkvv3z3.js +0 -21
- package/index-t91a7339.js +0 -104
|
@@ -1,86 +1,17 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
import {
|
|
3
3
|
binPlatforms,
|
|
4
|
+
ServerEnvProfile,
|
|
4
5
|
Executor
|
|
5
|
-
} from "./index-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
const config = await app.getConfig();
|
|
11
|
-
return Object.entries(config.native.targets).map(([name, target]) => ({ name, config: target }));
|
|
12
|
-
};
|
|
13
|
-
var isTemplateOnly = ({ basePaths, native }) => basePaths.size > 0 && Object.values(native.targets).every((target) => !target.basePath);
|
|
14
|
-
var getMobileTargetChoices = async (app) => {
|
|
15
|
-
const config = await app.getConfig();
|
|
16
|
-
const targetNames = Object.keys(config.native.targets);
|
|
17
|
-
if (targetNames.length > 0 && !isTemplateOnly(config))
|
|
18
|
-
return targetNames;
|
|
19
|
-
return [...config.basePaths];
|
|
20
|
-
};
|
|
21
|
-
var resolveMobileTargetByBasePath = (targets, basePath) => {
|
|
22
|
-
const normalizedBasePath = trimSlashes(basePath);
|
|
23
|
-
const byBasePath = targets.find(({ config }) => config.basePath !== undefined && trimSlashes(config.basePath) === normalizedBasePath);
|
|
24
|
-
if (byBasePath)
|
|
25
|
-
return byBasePath;
|
|
26
|
-
const [template] = targets;
|
|
27
|
-
if (!template)
|
|
28
|
-
return;
|
|
29
|
-
return {
|
|
30
|
-
name: normalizedBasePath,
|
|
31
|
-
config: { ...template.config, name: normalizedBasePath, basePath: normalizedBasePath }
|
|
32
|
-
};
|
|
33
|
-
};
|
|
34
|
-
var resolveMobileTargets = async (app, selection) => {
|
|
35
|
-
const config = await app.getConfig();
|
|
36
|
-
const targets = await getMobileTargets(app);
|
|
37
|
-
if (targets.length === 0)
|
|
38
|
-
throw new Error(`No native targets configured for ${app.name}`);
|
|
39
|
-
if (!selection && targets.length === 1 && !isTemplateOnly(config))
|
|
40
|
-
return targets;
|
|
41
|
-
if (!selection) {
|
|
42
|
-
const choices = await getMobileTargetChoices(app);
|
|
43
|
-
if (choices.length === 1)
|
|
44
|
-
return resolveMobileTargets(app, choices[0]);
|
|
45
|
-
throw new Error(`Multiple native targets found for ${app.name}. Pass --target <${choices.join("|")}|all>.`);
|
|
46
|
-
}
|
|
47
|
-
if (selection === "all") {
|
|
48
|
-
if (Object.keys(config.native.targets).length > 1)
|
|
49
|
-
return targets;
|
|
50
|
-
const basePaths = [...config.basePaths];
|
|
51
|
-
if (basePaths.length > 1 || isTemplateOnly(config)) {
|
|
52
|
-
return basePaths.flatMap((basePath) => {
|
|
53
|
-
const resolved = resolveMobileTargetByBasePath(targets, basePath);
|
|
54
|
-
return resolved ? [resolved] : [];
|
|
55
|
-
});
|
|
56
|
-
}
|
|
57
|
-
return targets;
|
|
58
|
-
}
|
|
59
|
-
const target = targets.find((candidate) => candidate.name === selection);
|
|
60
|
-
if (target)
|
|
61
|
-
return [target];
|
|
62
|
-
const basePathTarget = resolveMobileTargetByBasePath(targets, selection);
|
|
63
|
-
if (basePathTarget && config.basePaths.has(trimSlashes(selection)))
|
|
64
|
-
return [basePathTarget];
|
|
65
|
-
const choices = await getMobileTargetChoices(app);
|
|
66
|
-
throw new Error(`Native target '${selection}' was not found. Available: ${choices.join(", ")}`);
|
|
67
|
-
};
|
|
68
|
-
var resolveAppId = (appId, platform) => {
|
|
69
|
-
if (typeof appId === "string")
|
|
70
|
-
return appId;
|
|
71
|
-
const id = appId[platform] ?? appId.default;
|
|
72
|
-
if (!id)
|
|
73
|
-
throw new Error(`native.appId names no id for ${platform}; give it ${platform} or default.`);
|
|
74
|
-
return id;
|
|
75
|
-
};
|
|
76
|
-
var appIdsOf = (appId) => typeof appId === "string" ? [appId] : Object.values(appId).filter((id) => !!id);
|
|
77
|
-
var targetHtmlFilename = (target) => {
|
|
78
|
-
const basePath = trimSlashes(target.basePath ?? "");
|
|
79
|
-
return basePath ? `${basePath}.html` : "index.html";
|
|
80
|
-
};
|
|
6
|
+
} from "./index-55mynnqs.js";
|
|
7
|
+
import {
|
|
8
|
+
resolveAppId,
|
|
9
|
+
targetHtmlFilename
|
|
10
|
+
} from "./index-q958tqqg.js";
|
|
81
11
|
|
|
82
12
|
// pkgs/@akanjs/devkit/mobile/nativeApp.ts
|
|
83
|
-
import
|
|
13
|
+
import { rm as rm3 } from "fs/promises";
|
|
14
|
+
import path7 from "path";
|
|
84
15
|
|
|
85
16
|
// pkgs/@akanjs/devkit/mobile/desktopBin.ts
|
|
86
17
|
import { chmod, mkdir, rename, rm, writeFile } from "fs/promises";
|
|
@@ -417,11 +348,19 @@ class NativeConfig {
|
|
|
417
348
|
const desktop = NativeConfig.#compact({
|
|
418
349
|
recovery: target.desktop?.recovery,
|
|
419
350
|
screenCapture: target.desktop?.screenCapture,
|
|
351
|
+
webview2: target.desktop?.webview2,
|
|
352
|
+
publisher: target.desktop?.publisher,
|
|
420
353
|
window: NativeConfig.#compact({ ...target.desktop?.window }),
|
|
421
354
|
server: desktopServer,
|
|
422
355
|
bin: desktopBin,
|
|
423
356
|
dmg: NativeConfig.#dmg(target.desktop?.dmg, abs)
|
|
424
357
|
});
|
|
358
|
+
const background = target.backgroundColor;
|
|
359
|
+
const shell = NativeConfig.#compact({
|
|
360
|
+
backgroundColor: typeof background === "string" ? background : background?.light,
|
|
361
|
+
backgroundColorDark: typeof background === "string" ? undefined : background?.dark,
|
|
362
|
+
devtools: target.devtools ? true : undefined
|
|
363
|
+
});
|
|
425
364
|
const config = {
|
|
426
365
|
app: {
|
|
427
366
|
id: resolveAppId(target.appId, platform),
|
|
@@ -457,7 +396,8 @@ class NativeConfig {
|
|
|
457
396
|
keyboard: { resize: "none" },
|
|
458
397
|
...desktop ? { desktop } : {},
|
|
459
398
|
...target.icon ? { icon: NativeConfig.#icon(target.icon, abs) } : {},
|
|
460
|
-
...target.splash ? { splash: NativeConfig.#splash(target.splash, abs) } : {}
|
|
399
|
+
...target.splash ? { splash: NativeConfig.#splash(target.splash, abs) } : {},
|
|
400
|
+
...shell ? { shell } : {}
|
|
461
401
|
};
|
|
462
402
|
return { config, warnings };
|
|
463
403
|
}
|
|
@@ -480,8 +420,9 @@ class NativeConfig {
|
|
|
480
420
|
};
|
|
481
421
|
}
|
|
482
422
|
static #updates(target, env) {
|
|
483
|
-
const { url, publicKey, channel = env, ...rest } = target.updates ?? {};
|
|
484
|
-
|
|
423
|
+
const { url, publicKey, publicKeys, channel = env, ...rest } = target.updates ?? {};
|
|
424
|
+
const keys = publicKeys ? { publicKeys: [...publicKeys] } : publicKey ? { publicKey } : null;
|
|
425
|
+
return url && keys ? { ...rest, url, ...keys, ...channel ? { channel } : {} } : undefined;
|
|
485
426
|
}
|
|
486
427
|
static fileNameOf(name) {
|
|
487
428
|
return name.replace(/[^A-Za-z0-9._-]/g, "-").replace(/^[-.]+|[-.]+$/g, "") || "app";
|
|
@@ -614,6 +555,71 @@ ${script}${html.slice(at)}`;
|
|
|
614
555
|
}
|
|
615
556
|
}
|
|
616
557
|
|
|
558
|
+
// pkgs/@akanjs/devkit/mobile/serverBundleSecrets.ts
|
|
559
|
+
import { readdir as readdir2, readFile, stat as stat2 } from "fs/promises";
|
|
560
|
+
import path6 from "path";
|
|
561
|
+
class ServerBundleSecrets {
|
|
562
|
+
workspaceRoot;
|
|
563
|
+
appDir;
|
|
564
|
+
static #largestFile = 64 * 1024 * 1024;
|
|
565
|
+
constructor(workspaceRoot, appDir) {
|
|
566
|
+
this.workspaceRoot = workspaceRoot;
|
|
567
|
+
this.appDir = appDir;
|
|
568
|
+
}
|
|
569
|
+
async envFiles() {
|
|
570
|
+
const libsDir = path6.join(this.workspaceRoot, "libs");
|
|
571
|
+
const libs = await readdir2(libsDir).catch(() => []);
|
|
572
|
+
const dirs = [path6.join(this.appDir, "env"), ...libs.map((lib) => path6.join(libsDir, lib, "env"))];
|
|
573
|
+
const files = await Promise.all(dirs.map(async (dir) => (await readdir2(dir).catch(() => [])).filter((name) => /^env\.server\.[A-Za-z0-9.-]+\.ts$/.test(name) && !/\.(type|example)\.ts$/.test(name)).map((name) => path6.join(dir, name))));
|
|
574
|
+
return files.flat();
|
|
575
|
+
}
|
|
576
|
+
async assertClean(bundleDir) {
|
|
577
|
+
const unread = [];
|
|
578
|
+
const secrets = [];
|
|
579
|
+
for (const file of await this.envFiles()) {
|
|
580
|
+
try {
|
|
581
|
+
const value = await ServerEnvProfile.evaluate(file, path6.dirname(path6.dirname(file)));
|
|
582
|
+
secrets.push(...ServerEnvProfile.secretValues(value).map((secret) => ({ file, ...secret })));
|
|
583
|
+
} catch {
|
|
584
|
+
unread.push(ServerBundleSecrets.#relative(this.workspaceRoot, file));
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
const found = [];
|
|
588
|
+
for (const bundled of await ServerBundleSecrets.#files(bundleDir)) {
|
|
589
|
+
const text = await readFile(bundled, "latin1");
|
|
590
|
+
for (const secret of secrets)
|
|
591
|
+
if (ServerBundleSecrets.#needles(secret.value).some((needle) => text.includes(needle)))
|
|
592
|
+
found.push(`${ServerBundleSecrets.#relative(bundleDir, bundled)} holds ${ServerBundleSecrets.#relative(this.workspaceRoot, secret.file)} ${secret.path}`);
|
|
593
|
+
}
|
|
594
|
+
if (found.length)
|
|
595
|
+
throw new Error(`The server this app carries holds secret values from the workspace's env files, so it is not signed or sent anywhere:
|
|
596
|
+
${[...new Set(found)].join(`
|
|
597
|
+
`)}
|
|
598
|
+
Build its server with a profile (native.desktop.server.profile), whose bundle carries no secret.`);
|
|
599
|
+
return unread;
|
|
600
|
+
}
|
|
601
|
+
static #needles(value) {
|
|
602
|
+
const escaped = JSON.stringify(value).slice(1, -1);
|
|
603
|
+
const line = value.split(/\r?\n/).find((part) => part.length >= 32);
|
|
604
|
+
return [...new Set([value, escaped, ...line ? [line] : []])];
|
|
605
|
+
}
|
|
606
|
+
static #relative(from, to) {
|
|
607
|
+
return path6.relative(from, to).split(path6.sep).join("/");
|
|
608
|
+
}
|
|
609
|
+
static async#files(dir) {
|
|
610
|
+
const files = [];
|
|
611
|
+
for (const entry of await readdir2(dir, { withFileTypes: true })) {
|
|
612
|
+
const file = path6.join(dir, entry.name);
|
|
613
|
+
if (entry.isDirectory()) {
|
|
614
|
+
if (entry.name !== "node_modules")
|
|
615
|
+
files.push(...await ServerBundleSecrets.#files(file));
|
|
616
|
+
} else if (entry.isFile() && (await stat2(file)).size <= ServerBundleSecrets.#largestFile)
|
|
617
|
+
files.push(file);
|
|
618
|
+
}
|
|
619
|
+
return files;
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
|
|
617
623
|
// pkgs/@akanjs/devkit/mobile/nativeApp.ts
|
|
618
624
|
class NativeApp {
|
|
619
625
|
app;
|
|
@@ -626,14 +632,14 @@ class NativeApp {
|
|
|
626
632
|
this.app = app;
|
|
627
633
|
this.target = target;
|
|
628
634
|
this.env = env;
|
|
629
|
-
this.targetRoot =
|
|
630
|
-
this.web = new NativeWebDir(
|
|
635
|
+
this.targetRoot = path7.join(app.workspace.workspaceRoot, "dist", "native", app.name, target.name);
|
|
636
|
+
this.web = new NativeWebDir(path7.join(this.targetRoot, "web"));
|
|
631
637
|
}
|
|
632
638
|
outDir(platform) {
|
|
633
|
-
return
|
|
639
|
+
return path7.join(this.targetRoot, "build", platform);
|
|
634
640
|
}
|
|
635
641
|
devOutDir(platform) {
|
|
636
|
-
return
|
|
642
|
+
return path7.join(this.targetRoot, "dev", platform);
|
|
637
643
|
}
|
|
638
644
|
indexPath(platform) {
|
|
639
645
|
const { config } = this.target;
|
|
@@ -643,13 +649,13 @@ class NativeApp {
|
|
|
643
649
|
async assembleWeb(platform) {
|
|
644
650
|
const dist = this.app.dist.cwdPath;
|
|
645
651
|
return await this.web.assemble({ ...this.target.config, indexPath: this.indexPath(platform) }, {
|
|
646
|
-
html:
|
|
647
|
-
publicDir:
|
|
648
|
-
fontsDir:
|
|
652
|
+
html: path7.join(dist, "csr", targetHtmlFilename(this.target.config)),
|
|
653
|
+
publicDir: path7.join(dist, "public"),
|
|
654
|
+
fontsDir: path7.join(dist, ".akan", "artifact", "fonts")
|
|
649
655
|
});
|
|
650
656
|
}
|
|
651
657
|
get binDir() {
|
|
652
|
-
return
|
|
658
|
+
return path7.join(this.targetRoot, "bin");
|
|
653
659
|
}
|
|
654
660
|
async config({
|
|
655
661
|
server,
|
|
@@ -729,27 +735,18 @@ class NativeApp {
|
|
|
729
735
|
return {};
|
|
730
736
|
return { PUBLIC_AKAN_SERVER_URL: `http://localhost:${await this.app.getDevPort()}` };
|
|
731
737
|
}
|
|
732
|
-
async build(platform, { profile = "release", server, installer = false, arch } = {}) {
|
|
738
|
+
async build(platform, { profile = "release", server, installer = false, arch, sign = false, notarize = false } = {}) {
|
|
733
739
|
NativeApp.#assertServerPlatform(platform, server);
|
|
734
740
|
NativeApp.assertInstaller(platform, installer);
|
|
735
741
|
await this.assembleWeb(platform);
|
|
736
742
|
const { api, config } = await this.prepare(platform, server, arch);
|
|
743
|
+
const distribution = await this.#distribution(api, platform, profile, { sign, notarize }, server);
|
|
737
744
|
return await api.build({
|
|
738
745
|
...this.#task(platform, config, { env: await this.#runtimeEnv(server) }),
|
|
739
746
|
profile,
|
|
740
747
|
...arch ? { arch } : {},
|
|
741
|
-
...platform === "windows" ? {
|
|
742
|
-
|
|
743
|
-
...installer ? { installer } : {},
|
|
744
|
-
...profile === "release" ? NativeApp.#windowsSigning(api) : {}
|
|
745
|
-
}
|
|
746
|
-
} : {},
|
|
747
|
-
...platform === "macos" ? {
|
|
748
|
-
macos: {
|
|
749
|
-
...profile === "release" ? api.macosDistributionFromEnv(process.env) : {},
|
|
750
|
-
...installer ? { dmg: true } : {}
|
|
751
|
-
}
|
|
752
|
-
} : {},
|
|
748
|
+
...platform === "windows" ? { windows: { ...installer ? { installer } : {}, ...distribution.windows } } : {},
|
|
749
|
+
...platform === "macos" ? { macos: { ...distribution.macos, ...installer ? { dmg: true } : {} } } : {},
|
|
753
750
|
...platform === "linux" && installer ? { linux: { appImage: true } } : {}
|
|
754
751
|
});
|
|
755
752
|
}
|
|
@@ -815,23 +812,46 @@ class NativeApp {
|
|
|
815
812
|
});
|
|
816
813
|
}
|
|
817
814
|
get updatesDir() {
|
|
818
|
-
return
|
|
815
|
+
return path7.join(this.targetRoot, "updates");
|
|
819
816
|
}
|
|
820
|
-
async publishUpdate(platform, {
|
|
817
|
+
async publishUpdate(platform, {
|
|
818
|
+
channel,
|
|
819
|
+
server,
|
|
820
|
+
arch,
|
|
821
|
+
sign = false,
|
|
822
|
+
notarize = false
|
|
823
|
+
} = {}) {
|
|
821
824
|
NativeApp.#assertServerPlatform(platform, server);
|
|
822
825
|
this.#assertUpdates();
|
|
823
826
|
await this.assembleWeb(platform);
|
|
824
|
-
const { api, config } = await this.prepare(platform, server);
|
|
827
|
+
const { api, config } = await this.prepare(platform, server, arch);
|
|
828
|
+
const distribution = await this.#distribution(api, platform, "release", { sign, notarize }, server);
|
|
825
829
|
return await api.publishUpdate({
|
|
826
830
|
...this.#task(platform, config, { env: await this.#runtimeEnv(server) }),
|
|
827
831
|
platform,
|
|
828
832
|
out: this.updatesDir,
|
|
829
833
|
...channel ? { channel } : {},
|
|
830
|
-
...
|
|
831
|
-
...
|
|
834
|
+
...arch ? { arch } : {},
|
|
835
|
+
...distribution.macos ? { macos: distribution.macos } : {},
|
|
836
|
+
...distribution.windows ? { windows: distribution.windows } : {}
|
|
832
837
|
});
|
|
833
838
|
}
|
|
834
|
-
async
|
|
839
|
+
async#distribution(api, platform, profile, request, server) {
|
|
840
|
+
const distribution = api.requestedDistribution(platform, profile, request);
|
|
841
|
+
if (!distribution.notice) {
|
|
842
|
+
if (distribution.unused.length && (platform === "macos" || platform === "windows"))
|
|
843
|
+
this.app.logger.info(`${distribution.unused.join(", ")} set: this build is not signed without --sign.`);
|
|
844
|
+
return distribution;
|
|
845
|
+
}
|
|
846
|
+
if (server) {
|
|
847
|
+
const unread = await new ServerBundleSecrets(this.app.workspace.workspaceRoot, this.app.cwdPath).assertClean(server.dir);
|
|
848
|
+
if (unread.length)
|
|
849
|
+
this.app.logger.warn(`Not searched for secrets, as they could not be evaluated: ${unread.join(", ")}.`);
|
|
850
|
+
}
|
|
851
|
+
this.app.logger.warn(distribution.notice);
|
|
852
|
+
return distribution;
|
|
853
|
+
}
|
|
854
|
+
async assertPublishable(platform, { channel, server = false, arch } = {}) {
|
|
835
855
|
this.#assertUpdates();
|
|
836
856
|
const [api, { config }] = await Promise.all([
|
|
837
857
|
NativeApi.load(this.app.cwdPath),
|
|
@@ -845,7 +865,8 @@ class NativeApp {
|
|
|
845
865
|
platform,
|
|
846
866
|
out: this.updatesDir,
|
|
847
867
|
server,
|
|
848
|
-
...channel ? { channel } : {}
|
|
868
|
+
...channel ? { channel } : {},
|
|
869
|
+
...arch ? { arch } : {}
|
|
849
870
|
});
|
|
850
871
|
} catch (error) {
|
|
851
872
|
throw new Error(`Native target '${this.target.name}': ${error instanceof Error ? error.message : String(error)}`, {
|
|
@@ -880,12 +901,40 @@ class NativeApp {
|
|
|
880
901
|
const { api, config } = await this.prepare(platform);
|
|
881
902
|
if (!config.updates)
|
|
882
903
|
throw new Error(`Native target '${this.target.name}' takes no updates: add native.updates: { url, publicKey } to akan.config.ts first.`);
|
|
883
|
-
const
|
|
884
|
-
|
|
904
|
+
const dir = path7.resolve(out ?? path7.join(this.updatesDir, platform));
|
|
905
|
+
await Promise.all(["pack.json", "compat.json"].map((name) => rm3(path7.join(dir, name), { force: true })));
|
|
906
|
+
const packed = await api.packUpdate({ ...this.#task(platform, config), platform, out: dir });
|
|
907
|
+
const channel = config.updates.channel ?? "production";
|
|
908
|
+
const bundle = await Bun.file(path7.join(packed.out, "bundle.json")).json();
|
|
909
|
+
const pack = {
|
|
910
|
+
schema: 1,
|
|
911
|
+
app: this.app.name,
|
|
912
|
+
target: this.target.name,
|
|
885
913
|
platform,
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
914
|
+
env: this.env ?? "main",
|
|
915
|
+
channel,
|
|
916
|
+
appId: packed.manifest.app,
|
|
917
|
+
appVersion: packed.manifest.version,
|
|
918
|
+
nativeApi: packed.manifest.nativeApi ?? "",
|
|
919
|
+
webHash: bundle.web.hash,
|
|
920
|
+
files: packed.manifest.files.length,
|
|
921
|
+
size: packed.manifest.files.reduce((sum, file) => sum + file.size, 0),
|
|
922
|
+
out: packed.out
|
|
923
|
+
};
|
|
924
|
+
return { ...packed, channel, pack };
|
|
925
|
+
}
|
|
926
|
+
static async writePack(pack) {
|
|
927
|
+
await Bun.write(path7.join(pack.out, "pack.json"), `${JSON.stringify(pack, null, 2)}
|
|
928
|
+
`);
|
|
929
|
+
}
|
|
930
|
+
static async readPack(dir) {
|
|
931
|
+
const file = Bun.file(path7.join(dir, "pack.json"));
|
|
932
|
+
if (!await file.exists())
|
|
933
|
+
throw new Error(`${dir} holds no pack.json: give the folder \`akan pack-update\` wrote.`);
|
|
934
|
+
const pack = await file.json();
|
|
935
|
+
if (pack.schema !== 1)
|
|
936
|
+
throw new Error(`${dir}/pack.json is schema ${pack.schema}, and this akan reads schema 1.`);
|
|
937
|
+
return { ...pack, out: path7.resolve(dir) };
|
|
889
938
|
}
|
|
890
939
|
static async compareBundles(appDir, shipped, bundle) {
|
|
891
940
|
return (await NativeApi.load(appDir)).compareBundles(shipped, bundle);
|
|
@@ -916,10 +965,6 @@ class NativeApp {
|
|
|
916
965
|
const home = [lang, basePath, indexPath].flatMap((part) => (part ?? "").split("/")).filter((segment) => segment.length > 0);
|
|
917
966
|
return `/${home.join("/")}?${params}`;
|
|
918
967
|
}
|
|
919
|
-
static #windowsSigning(api) {
|
|
920
|
-
const signing = api.windowsSigningFromEnv(process.env);
|
|
921
|
-
return signing ? { signing } : {};
|
|
922
|
-
}
|
|
923
968
|
static assertInstaller(platform, installer) {
|
|
924
969
|
if (installer && (platform === "ios" || platform === "android"))
|
|
925
970
|
throw new Error(`An installer is built for a desktop app (Windows, macOS, Linux), not for ${platform}.`);
|
|
@@ -962,8 +1007,8 @@ var isPlaceholderAppId = (appId) => {
|
|
|
962
1007
|
return normalized.split(".").some((segment) => placeholderAppIdSegment.test(segment));
|
|
963
1008
|
};
|
|
964
1009
|
// pkgs/@akanjs/devkit/mobile/desktopServerStage.ts
|
|
965
|
-
import { cp as cp2, mkdir as mkdir3, readdir as
|
|
966
|
-
import
|
|
1010
|
+
import { cp as cp2, mkdir as mkdir3, readdir as readdir3, rm as rm4, writeFile as writeFile2 } from "fs/promises";
|
|
1011
|
+
import path8 from "path";
|
|
967
1012
|
class DesktopServerStage {
|
|
968
1013
|
app;
|
|
969
1014
|
static uncarried = new Set([
|
|
@@ -980,23 +1025,25 @@ class DesktopServerStage {
|
|
|
980
1025
|
dir;
|
|
981
1026
|
constructor(app) {
|
|
982
1027
|
this.app = app;
|
|
983
|
-
this.dir =
|
|
1028
|
+
this.dir = path8.join(app.cwdPath, ".akan", "desktop", "server");
|
|
984
1029
|
}
|
|
985
1030
|
static assertCarriable(config) {
|
|
986
1031
|
if (!config.database.modes.includes("single"))
|
|
987
1032
|
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(", ")}.`);
|
|
988
1033
|
}
|
|
989
|
-
static env(config, environment) {
|
|
1034
|
+
static env(config, environment, { modules = [], env = {} } = {}) {
|
|
990
1035
|
const { PORT: _port, AKAN_LOG_TO_FILE: _fileLog, ...image } = config.getProductionEnv(environment);
|
|
991
1036
|
return {
|
|
992
1037
|
...image,
|
|
1038
|
+
...env,
|
|
993
1039
|
AKAN_PUBLIC_OPERATION_MODE: "edge",
|
|
994
1040
|
AKAN_DATABASE_MODE: "single",
|
|
995
1041
|
AKAN_DATABASE_MODES: "single",
|
|
996
1042
|
AKAN_SSR: "false",
|
|
997
1043
|
AKAN_CSR: "false",
|
|
998
1044
|
AKAN_MCP: "false",
|
|
999
|
-
AKAN_SHUTDOWN_TIMEOUT_MS: "1000"
|
|
1045
|
+
AKAN_SHUTDOWN_TIMEOUT_MS: "1000",
|
|
1046
|
+
...modules.length ? { AKAN_MODULES: modules.join(",") } : {}
|
|
1000
1047
|
};
|
|
1001
1048
|
}
|
|
1002
1049
|
static imageStepsNotice(config, carried) {
|
|
@@ -1020,7 +1067,7 @@ class DesktopServerStage {
|
|
|
1020
1067
|
static async dependentsOf(nodeModules, name) {
|
|
1021
1068
|
const dependents = new Set;
|
|
1022
1069
|
for await (const manifest of new Bun.Glob("**/package.json").scan({ cwd: nodeModules, onlyFiles: true })) {
|
|
1023
|
-
const json = await Bun.file(
|
|
1070
|
+
const json = await Bun.file(path8.join(nodeModules, manifest)).json().catch(() => null);
|
|
1024
1071
|
if (!json?.name || json.name === name)
|
|
1025
1072
|
continue;
|
|
1026
1073
|
const declared = { ...json.dependencies, ...json.optionalDependencies, ...json.peerDependencies };
|
|
@@ -1035,26 +1082,26 @@ class DesktopServerStage {
|
|
|
1035
1082
|
async install(arch, omit = []) {
|
|
1036
1083
|
const config = await this.app.getConfig();
|
|
1037
1084
|
DesktopServerStage.assertCarriable(config);
|
|
1038
|
-
await
|
|
1085
|
+
await rm4(this.dir, { recursive: true, force: true });
|
|
1039
1086
|
await mkdir3(this.dir, { recursive: true });
|
|
1040
1087
|
const built = config.getProductionPackageJson();
|
|
1041
1088
|
for (const name of omit.filter((name) => !(name in (built.dependencies ?? {}))))
|
|
1042
1089
|
this.app.logger.warn(`desktop.server.omit names ${name}, which the server does not depend on directly.`);
|
|
1043
|
-
await writeFile2(
|
|
1090
|
+
await writeFile2(path8.join(this.dir, "package.json"), JSON.stringify(DesktopServerStage.packageJson(config, built, omit), null, 2));
|
|
1044
1091
|
try {
|
|
1045
1092
|
await this.app.spawn(process.execPath, DesktopServerStage.installArgs(arch), { cwd: this.dir });
|
|
1046
1093
|
} catch (error) {
|
|
1047
1094
|
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.
|
|
1048
1095
|
${error instanceof Error ? error.message : String(error)}`, { cause: error });
|
|
1049
1096
|
}
|
|
1050
|
-
await
|
|
1097
|
+
await rm4(path8.join(this.dir, "node_modules", ".bin"), { recursive: true, force: true });
|
|
1051
1098
|
await this.#assertOmitted(omit);
|
|
1052
1099
|
}
|
|
1053
1100
|
async#assertOmitted(omit) {
|
|
1054
|
-
const nodeModules =
|
|
1101
|
+
const nodeModules = path8.join(this.dir, "node_modules");
|
|
1055
1102
|
const kept = [];
|
|
1056
1103
|
for (const name of omit) {
|
|
1057
|
-
if (!await Bun.file(
|
|
1104
|
+
if (!await Bun.file(path8.join(nodeModules, name, "package.json")).exists())
|
|
1058
1105
|
continue;
|
|
1059
1106
|
const dependents = await DesktopServerStage.dependentsOf(nodeModules, name);
|
|
1060
1107
|
kept.push(`${name} (needed by ${dependents.join(", ") || "a package that names it"})`);
|
|
@@ -1062,19 +1109,79 @@ ${error instanceof Error ? error.message : String(error)}`, { cause: error });
|
|
|
1062
1109
|
if (kept.length)
|
|
1063
1110
|
throw new Error(`desktop.server.omit leaves out packages another dependency still installs: ${kept.join("; ")}. Omit those too, or keep the package.`);
|
|
1064
1111
|
}
|
|
1065
|
-
async prepare(environment) {
|
|
1112
|
+
async prepare(environment, run = {}) {
|
|
1066
1113
|
const config = await this.app.getConfig();
|
|
1067
1114
|
const dist = this.app.dist.cwdPath;
|
|
1068
1115
|
await mkdir3(this.dir, { recursive: true });
|
|
1069
|
-
for (const name of await
|
|
1116
|
+
for (const name of await readdir3(dist))
|
|
1070
1117
|
if (!DesktopServerStage.uncarried.has(name))
|
|
1071
|
-
await cp2(
|
|
1118
|
+
await cp2(path8.join(dist, name), path8.join(this.dir, name), { recursive: true });
|
|
1072
1119
|
const scanInfo = this.app.getScanInfo({ allowEmpty: true }) ?? await this.app.scan({ write: false });
|
|
1073
1120
|
const carried = [...DesktopBin.select(config, scanInfo.getLibs()).keys()];
|
|
1074
1121
|
const notice = DesktopServerStage.imageStepsNotice(config, carried);
|
|
1075
1122
|
if (notice)
|
|
1076
1123
|
this.app.logger[notice.level](notice.message);
|
|
1077
|
-
return {
|
|
1124
|
+
return {
|
|
1125
|
+
dir: this.dir,
|
|
1126
|
+
entry: "main.js",
|
|
1127
|
+
env: DesktopServerStage.env(config, environment, run),
|
|
1128
|
+
...run.restart !== undefined ? { restart: run.restart } : {},
|
|
1129
|
+
...run.watchdog ? { watchdog: run.watchdog } : {}
|
|
1130
|
+
};
|
|
1131
|
+
}
|
|
1132
|
+
}
|
|
1133
|
+
// pkgs/@akanjs/devkit/mobile/updateUploader.ts
|
|
1134
|
+
import path9 from "path";
|
|
1135
|
+
|
|
1136
|
+
class UpdateUploader {
|
|
1137
|
+
bucket;
|
|
1138
|
+
prefix;
|
|
1139
|
+
static #manifest = /\.json(?:\.sig)?$/;
|
|
1140
|
+
static parseTarget(target) {
|
|
1141
|
+
const match = /^s3:\/\/([^/]+)\/?(.*)$/.exec(target.trim());
|
|
1142
|
+
if (!match?.[1])
|
|
1143
|
+
throw new Error(`--upload takes s3://<bucket>/<prefix>, not ${JSON.stringify(target)}.`);
|
|
1144
|
+
return { bucket: match[1], prefix: (match[2] ?? "").replace(/\/+$/, "") };
|
|
1145
|
+
}
|
|
1146
|
+
static order(files) {
|
|
1147
|
+
const manifests = files.filter((file) => UpdateUploader.#manifest.test(file));
|
|
1148
|
+
return {
|
|
1149
|
+
releases: files.filter((file) => !UpdateUploader.#manifest.test(file)).sort((a, b) => a.localeCompare(b)),
|
|
1150
|
+
manifests: manifests.sort((a, b) => Number(a.endsWith(".json")) - Number(b.endsWith(".json")) || a.localeCompare(b))
|
|
1151
|
+
};
|
|
1152
|
+
}
|
|
1153
|
+
constructor(bucket, prefix) {
|
|
1154
|
+
this.bucket = bucket;
|
|
1155
|
+
this.prefix = prefix;
|
|
1156
|
+
}
|
|
1157
|
+
static forTarget(target) {
|
|
1158
|
+
const { bucket, prefix } = UpdateUploader.parseTarget(target);
|
|
1159
|
+
return new UpdateUploader(new Bun.S3Client({ bucket }), prefix);
|
|
1160
|
+
}
|
|
1161
|
+
async upload(dir) {
|
|
1162
|
+
const files = [];
|
|
1163
|
+
for await (const file of new Bun.Glob("**/*").scan({ cwd: dir, onlyFiles: true })) {
|
|
1164
|
+
if (!file.endsWith(".tmp"))
|
|
1165
|
+
files.push(file.split(path9.sep).join("/"));
|
|
1166
|
+
}
|
|
1167
|
+
const { releases, manifests } = UpdateUploader.order(files);
|
|
1168
|
+
const keyOf = (file) => this.prefix ? `${this.prefix}/${file}` : file;
|
|
1169
|
+
let uploaded = 0;
|
|
1170
|
+
let skipped = 0;
|
|
1171
|
+
for (const file of releases) {
|
|
1172
|
+
if (await this.bucket.exists(keyOf(file))) {
|
|
1173
|
+
skipped++;
|
|
1174
|
+
continue;
|
|
1175
|
+
}
|
|
1176
|
+
await this.bucket.write(keyOf(file), Bun.file(path9.join(dir, file)));
|
|
1177
|
+
uploaded++;
|
|
1178
|
+
}
|
|
1179
|
+
for (const file of manifests) {
|
|
1180
|
+
const type = file.endsWith(".json") ? "application/json" : "text/plain";
|
|
1181
|
+
await this.bucket.write(keyOf(file), Bun.file(path9.join(dir, file)), { type });
|
|
1182
|
+
uploaded++;
|
|
1183
|
+
}
|
|
1184
|
+
return { uploaded, skipped };
|
|
1078
1185
|
}
|
|
1079
1186
|
}
|
|
1080
|
-
export { DesktopServerStage,
|
|
1187
|
+
export { DesktopServerStage, NativeApp, isPlaceholderAppId, UpdateUploader };
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
isScannedRootEntry,
|
|
11
11
|
AppExecutor,
|
|
12
12
|
LibExecutor
|
|
13
|
-
} from "./index-
|
|
13
|
+
} from "./index-55mynnqs.js";
|
|
14
14
|
import {
|
|
15
15
|
FileSys2
|
|
16
16
|
} from "./index-52w0fvh8.js";
|
|
@@ -23,7 +23,7 @@ import {
|
|
|
23
23
|
workflowPathsForPlan,
|
|
24
24
|
workflowRunArtifactPath,
|
|
25
25
|
workflowSyncDir
|
|
26
|
-
} from "./index-
|
|
26
|
+
} from "./index-05g3jayg.js";
|
|
27
27
|
|
|
28
28
|
// pkgs/@akanjs/devkit/akanContext.ts
|
|
29
29
|
import { readdir } from "fs/promises";
|
|
@@ -131,7 +131,7 @@ var generatedFiles = [
|
|
|
131
131
|
var validationCommands = [
|
|
132
132
|
"akan sync <app-or-lib>",
|
|
133
133
|
"akan lint <app-or-lib-or-pkg>",
|
|
134
|
-
"akan typecheck <app-
|
|
134
|
+
"akan typecheck <app-or-lib>",
|
|
135
135
|
"akan test <app-or-lib-or-pkg>",
|
|
136
136
|
"akan build <app-name>",
|
|
137
137
|
"akan doctor --strict --format json",
|