lecodes-cli 2.0.13 → 2.1.0
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/dist/index.js +245 -199
- package/package.json +8 -8
- package/runtime/sdk-types.json +1 -1
- package/runtime/web/VERSION.json +3 -3
- package/runtime/web/assets/creator-2d-CRsyKKtS.js +1 -0
- package/runtime/web/assets/creator-2d-Ci0AyJGg.wasm +0 -0
- package/runtime/web/assets/creator-full-DBIPcQIa.js +1 -0
- package/runtime/web/assets/{creator-full-Cla1R8N6.wasm → creator-full-vPAzqY1M.wasm} +0 -0
- package/runtime/web/assets/creator-full.js-CfRfMmmc.symbols +8198 -0
- package/runtime/web/assets/creator-ui-C7wLARUu.js +1 -0
- package/runtime/web/assets/creator-ui-CULpA-Fz.wasm +0 -0
- package/runtime/web/player.js +10 -10
- package/runtime/web-lite/lite.js +1 -1
- package/src/commands/app/android.ts +27 -11
- package/src/commands/app/index.ts +21 -32
- package/src/commands/app/shared.ts +27 -2
- package/src/commands/app/templates/android.ts +47 -12
- package/runtime/web/assets/creator-2d-BSJoqaE1.wasm +0 -0
- package/runtime/web/assets/creator-2d-DG2jJnoQ.js +0 -1
- package/runtime/web/assets/creator-full-DmfoULcB.js +0 -1
- package/runtime/web/assets/creator-full.js-D8SMHuCt.symbols +0 -8419
- package/runtime/web/assets/creator-ui-Bu2prHDl.wasm +0 -0
- package/runtime/web/assets/creator-ui-HDloYDcM.js +0 -1
package/dist/index.js
CHANGED
|
@@ -52,7 +52,7 @@ var package_default;
|
|
|
52
52
|
var init_package = __esm(() => {
|
|
53
53
|
package_default = {
|
|
54
54
|
name: "lecodes-cli",
|
|
55
|
-
version: "2.0
|
|
55
|
+
version: "2.1.0",
|
|
56
56
|
dependencies: {
|
|
57
57
|
"@letary/chisel": "^0.13.0",
|
|
58
58
|
jimp: "^1.6.1",
|
|
@@ -76,7 +76,7 @@ var init_package = __esm(() => {
|
|
|
76
76
|
},
|
|
77
77
|
peerDependencies: {
|
|
78
78
|
"lecodes-design": "^0.11.0",
|
|
79
|
-
"lecodes-headless": "^2.0
|
|
79
|
+
"lecodes-headless": "^2.1.0",
|
|
80
80
|
"lecodes-3d-editor": "^0.9.0",
|
|
81
81
|
"lecodes-assets": "^0.2.1",
|
|
82
82
|
"heic-decode": "^2.1.0",
|
|
@@ -146,7 +146,7 @@ var init_package = __esm(() => {
|
|
|
146
146
|
});
|
|
147
147
|
|
|
148
148
|
// ../../sdk/src/version.ts
|
|
149
|
-
var SDK_VERSION = "2.0
|
|
149
|
+
var SDK_VERSION = "2.1.0", sdkMajor = (version) => {
|
|
150
150
|
const m = version?.match(/^(\d+)\.\d+\.\d+/);
|
|
151
151
|
return m ? Number(m[1]) : null;
|
|
152
152
|
}, bundleSdkVersion = (head) => head.slice(0, 2000).match(/^\/\/ sdk: (\d+\.\d+\.\d+\S*)/m)?.[1] ?? null, bundleRunsOn = (bundleVersion, hostVersion) => sdkMajor(bundleVersion) !== null && sdkMajor(bundleVersion) === sdkMajor(hostVersion);
|
|
@@ -561,37 +561,6 @@ var init_manifest = __esm(() => {
|
|
|
561
561
|
init_cli();
|
|
562
562
|
});
|
|
563
563
|
|
|
564
|
-
// src/platform/config.ts
|
|
565
|
-
import { homedir } from "node:os";
|
|
566
|
-
import { join as join2 } from "node:path";
|
|
567
|
-
import { mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "node:fs";
|
|
568
|
-
var DEFAULT_API = "https://le.codes", CONFIG_DIR, CONFIG_FILE, loadConfig = () => {
|
|
569
|
-
let stored = {};
|
|
570
|
-
try {
|
|
571
|
-
stored = JSON.parse(readFileSync2(CONFIG_FILE, "utf8"));
|
|
572
|
-
} catch {}
|
|
573
|
-
return {
|
|
574
|
-
apiUrl: process.env.LECODES_API ?? stored.apiUrl,
|
|
575
|
-
token: process.env.LECODES_TOKEN ?? stored.token
|
|
576
|
-
};
|
|
577
|
-
}, saveConfig = (config) => {
|
|
578
|
-
mkdirSync2(CONFIG_DIR, { recursive: true });
|
|
579
|
-
writeFileSync2(CONFIG_FILE, JSON.stringify(config, null, 2));
|
|
580
|
-
}, normalizeApiUrl = (url) => url.trim().replace(/\/+$/, ""), requireToken = (config) => {
|
|
581
|
-
if (!config.token)
|
|
582
|
-
throw new CliError("Not logged in. Run `lecodes login` first.");
|
|
583
|
-
return config.token;
|
|
584
|
-
}, requireApiUrl = (config) => {
|
|
585
|
-
if (!config.apiUrl)
|
|
586
|
-
throw new CliError("No server URL configured. Run `lecodes login` first.");
|
|
587
|
-
return normalizeApiUrl(config.apiUrl);
|
|
588
|
-
};
|
|
589
|
-
var init_config = __esm(() => {
|
|
590
|
-
init_cli();
|
|
591
|
-
CONFIG_DIR = join2(homedir(), ".lecodes");
|
|
592
|
-
CONFIG_FILE = join2(CONFIG_DIR, "config.json");
|
|
593
|
-
});
|
|
594
|
-
|
|
595
564
|
// src/platform/api.ts
|
|
596
565
|
var errorMessage = (body, status) => {
|
|
597
566
|
if (typeof body === "string" && body)
|
|
@@ -700,8 +669,8 @@ var init_paths = __esm(() => {
|
|
|
700
669
|
});
|
|
701
670
|
|
|
702
671
|
// src/project/ignore.ts
|
|
703
|
-
import { existsSync as existsSync2, readFileSync as
|
|
704
|
-
import { join as
|
|
672
|
+
import { existsSync as existsSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "node:fs";
|
|
673
|
+
import { join as join2 } from "node:path";
|
|
705
674
|
var BUILTIN_PATTERNS, DEFAULT_IGNORE_FILE = `# Files matched here are NOT pushed to LeCodes.
|
|
706
675
|
# Only untracked files are affected — assets already in the project stay tracked
|
|
707
676
|
# even if they match a pattern. Built-in defaults (node_modules, _*, *.log, .artifacts)
|
|
@@ -782,18 +751,18 @@ design/comments/
|
|
|
782
751
|
return (path) => match(path.startsWith("/") ? path : "/" + path, false);
|
|
783
752
|
}, loadIgnore = (root) => {
|
|
784
753
|
const lines = [...BUILTIN_PATTERNS];
|
|
785
|
-
const file =
|
|
754
|
+
const file = join2(root, ".lecodesignore");
|
|
786
755
|
if (existsSync2(file)) {
|
|
787
756
|
try {
|
|
788
|
-
lines.push(...
|
|
757
|
+
lines.push(...readFileSync2(file, "utf8").split(/\r?\n/));
|
|
789
758
|
} catch {}
|
|
790
759
|
}
|
|
791
760
|
const rules = lines.map(compile).filter((r) => r !== null);
|
|
792
761
|
return buildMatcher(rules);
|
|
793
762
|
}, writeDefaultIgnore = (root) => {
|
|
794
|
-
const file =
|
|
763
|
+
const file = join2(root, ".lecodesignore");
|
|
795
764
|
if (!existsSync2(file))
|
|
796
|
-
|
|
765
|
+
writeFileSync2(file, DEFAULT_IGNORE_FILE);
|
|
797
766
|
}, DEFAULT_GITIGNORE = `# LeCodes per-checkout state: generated IDE types + this machine's sync cursor
|
|
798
767
|
# (.lecodes/manifest.json). Regenerate with \`lecodes update\` / re-attach with \`lecodes link\`.
|
|
799
768
|
.lecodes/
|
|
@@ -808,19 +777,19 @@ node_modules/
|
|
|
808
777
|
|
|
809
778
|
dist/
|
|
810
779
|
`, ensureGitignore = (root) => {
|
|
811
|
-
const file =
|
|
780
|
+
const file = join2(root, ".gitignore");
|
|
812
781
|
if (!existsSync2(file)) {
|
|
813
|
-
|
|
782
|
+
writeFileSync2(file, DEFAULT_GITIGNORE);
|
|
814
783
|
return "created";
|
|
815
784
|
}
|
|
816
|
-
const text =
|
|
785
|
+
const text = readFileSync2(file, "utf8");
|
|
817
786
|
const covered = text.split(/\r?\n/).some((l) => /^\/?\.lecodes\/?\s*$/.test(l.trim()));
|
|
818
787
|
if (covered)
|
|
819
788
|
return null;
|
|
820
789
|
const sep = text.length === 0 || text.endsWith(`
|
|
821
790
|
`) ? "" : `
|
|
822
791
|
`;
|
|
823
|
-
|
|
792
|
+
writeFileSync2(file, `${text}${sep}
|
|
824
793
|
# LeCodes per-checkout state (generated types + sync cursor) — not source
|
|
825
794
|
.lecodes/
|
|
826
795
|
`);
|
|
@@ -831,8 +800,8 @@ var init_ignore = __esm(() => {
|
|
|
831
800
|
});
|
|
832
801
|
|
|
833
802
|
// src/project/localFiles.ts
|
|
834
|
-
import { existsSync as existsSync3, readdirSync, readFileSync as
|
|
835
|
-
import { join as
|
|
803
|
+
import { existsSync as existsSync3, readdirSync, readFileSync as readFileSync3 } from "node:fs";
|
|
804
|
+
import { join as join3, relative } from "node:path";
|
|
836
805
|
var SKIP_DIRS, SKIP_FILES, TEXT_EXTS, ext = (name) => {
|
|
837
806
|
const i = name.lastIndexOf(".");
|
|
838
807
|
return i < 0 ? "" : name.slice(i + 1).toLowerCase();
|
|
@@ -860,7 +829,7 @@ var SKIP_DIRS, SKIP_FILES, TEXT_EXTS, ext = (name) => {
|
|
|
860
829
|
};
|
|
861
830
|
const walk = (dir, parentIgnored) => {
|
|
862
831
|
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
863
|
-
const abs =
|
|
832
|
+
const abs = join3(dir, entry.name);
|
|
864
833
|
const key = toKey(relative(root, abs));
|
|
865
834
|
if (entry.isDirectory()) {
|
|
866
835
|
if (SKIP_DIRS.has(entry.name))
|
|
@@ -880,14 +849,14 @@ var SKIP_DIRS, SKIP_FILES, TEXT_EXTS, ext = (name) => {
|
|
|
880
849
|
ignored.push(key);
|
|
881
850
|
continue;
|
|
882
851
|
}
|
|
883
|
-
files.push({ path: key, absPath: abs, sha: sha256(
|
|
852
|
+
files.push({ path: key, absPath: abs, sha: sha256(readFileSync3(abs)) });
|
|
884
853
|
}
|
|
885
854
|
}
|
|
886
855
|
};
|
|
887
856
|
walk(root, false);
|
|
888
857
|
return { files, dirs, ignored };
|
|
889
858
|
}, resolveLocalPath = (root, key) => {
|
|
890
|
-
const direct =
|
|
859
|
+
const direct = join3(root, key.slice(1));
|
|
891
860
|
if (existsSync3(direct))
|
|
892
861
|
return direct;
|
|
893
862
|
let dir = root;
|
|
@@ -896,7 +865,7 @@ var SKIP_DIRS, SKIP_FILES, TEXT_EXTS, ext = (name) => {
|
|
|
896
865
|
const match = readdirSync(dir).find((entry) => entry === name || sanitizeAssetName(entry) === name);
|
|
897
866
|
if (!match)
|
|
898
867
|
return null;
|
|
899
|
-
dir =
|
|
868
|
+
dir = join3(dir, match);
|
|
900
869
|
}
|
|
901
870
|
} catch {
|
|
902
871
|
return null;
|
|
@@ -956,32 +925,32 @@ var init_localFiles = __esm(() => {
|
|
|
956
925
|
|
|
957
926
|
// src/hosts/distRoot.ts
|
|
958
927
|
import { existsSync as existsSync4 } from "node:fs";
|
|
959
|
-
import { dirname as dirname3, join as
|
|
928
|
+
import { dirname as dirname3, join as join5 } from "node:path";
|
|
960
929
|
import { fileURLToPath } from "node:url";
|
|
961
930
|
var distRoot;
|
|
962
931
|
var init_distRoot = __esm(() => {
|
|
963
932
|
distRoot = (() => {
|
|
964
933
|
const here = dirname3(fileURLToPath(import.meta.url));
|
|
965
934
|
const candidates = [
|
|
966
|
-
|
|
967
|
-
|
|
935
|
+
join5(here, ".."),
|
|
936
|
+
join5(here, "..", ".."),
|
|
968
937
|
dirname3(process.execPath)
|
|
969
938
|
];
|
|
970
939
|
for (const c2 of candidates)
|
|
971
|
-
if (existsSync4(
|
|
940
|
+
if (existsSync4(join5(c2, "runtime")) || existsSync4(join5(c2, "package.json")))
|
|
972
941
|
return c2;
|
|
973
942
|
return candidates[0];
|
|
974
943
|
})();
|
|
975
944
|
});
|
|
976
945
|
|
|
977
946
|
// src/hosts/peers.ts
|
|
978
|
-
import { existsSync as existsSync5, readFileSync as
|
|
947
|
+
import { existsSync as existsSync5, readFileSync as readFileSync6 } from "node:fs";
|
|
979
948
|
import { createRequire as createRequire2 } from "node:module";
|
|
980
|
-
import { homedir
|
|
981
|
-
import { dirname as dirname4, join as
|
|
982
|
-
var OPTIONAL_PEERS, peersDir = () => process.env.LECODES_PEERS_DIR ? resolve(process.env.LECODES_PEERS_DIR) :
|
|
949
|
+
import { homedir } from "node:os";
|
|
950
|
+
import { dirname as dirname4, join as join6, resolve } from "node:path";
|
|
951
|
+
var OPTIONAL_PEERS, peersDir = () => process.env.LECODES_PEERS_DIR ? resolve(process.env.LECODES_PEERS_DIR) : join6(homedir(), ".lecodes", "peers"), peerDirInPrefix = (name) => join6(peersDir(), "node_modules", ...name.split("/")), readVersion = (dir) => {
|
|
983
952
|
try {
|
|
984
|
-
return JSON.parse(
|
|
953
|
+
return JSON.parse(readFileSync6(join6(dir, "package.json"), "utf8")).version ?? "unknown";
|
|
985
954
|
} catch {
|
|
986
955
|
return null;
|
|
987
956
|
}
|
|
@@ -990,9 +959,9 @@ var OPTIONAL_PEERS, peersDir = () => process.env.LECODES_PEERS_DIR ? resolve(pro
|
|
|
990
959
|
const require2 = createRequire2(import.meta.url);
|
|
991
960
|
let dir = dirname4(require2.resolve(probe));
|
|
992
961
|
for (let depth = 0;depth < 16; depth++) {
|
|
993
|
-
const manifest =
|
|
962
|
+
const manifest = join6(dir, "package.json");
|
|
994
963
|
if (existsSync5(manifest)) {
|
|
995
|
-
const parsed = JSON.parse(
|
|
964
|
+
const parsed = JSON.parse(readFileSync6(manifest, "utf8"));
|
|
996
965
|
if (parsed.name === name)
|
|
997
966
|
return parsed.version ?? "unknown";
|
|
998
967
|
}
|
|
@@ -1067,8 +1036,8 @@ var init_archives = __esm(() => {
|
|
|
1067
1036
|
|
|
1068
1037
|
// src/hosts/packageManager.ts
|
|
1069
1038
|
import { spawnSync } from "node:child_process";
|
|
1070
|
-
import { homedir as
|
|
1071
|
-
import { join as
|
|
1039
|
+
import { homedir as homedir2 } from "node:os";
|
|
1040
|
+
import { join as join7, resolve as resolve2, sep } from "node:path";
|
|
1072
1041
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
1073
1042
|
var onPath = (cmd) => {
|
|
1074
1043
|
try {
|
|
@@ -1076,7 +1045,7 @@ var onPath = (cmd) => {
|
|
|
1076
1045
|
} catch {
|
|
1077
1046
|
return false;
|
|
1078
1047
|
}
|
|
1079
|
-
}, bunGlobalDir = () =>
|
|
1048
|
+
}, bunGlobalDir = () => join7(process.env.BUN_INSTALL ? resolve2(process.env.BUN_INSTALL) : join7(homedir2(), ".bun"), "install", "global"), ownManager = (file = fileURLToPath2(import.meta.url)) => {
|
|
1080
1049
|
const norm = (p) => process.platform === "win32" ? p.toLowerCase() : p;
|
|
1081
1050
|
return norm(resolve2(file)).startsWith(norm(bunGlobalDir()) + sep) ? "bun" : "npm";
|
|
1082
1051
|
}, pickManager = () => {
|
|
@@ -1088,8 +1057,8 @@ var init_packageManager = () => {};
|
|
|
1088
1057
|
|
|
1089
1058
|
// src/hosts/peerInstall.ts
|
|
1090
1059
|
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
1091
|
-
import { existsSync as existsSync6, mkdirSync as
|
|
1092
|
-
import { join as
|
|
1060
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync3, readFileSync as readFileSync7, writeFileSync as writeFileSync4 } from "node:fs";
|
|
1061
|
+
import { join as join8 } from "node:path";
|
|
1093
1062
|
import { pathToFileURL } from "node:url";
|
|
1094
1063
|
async function loadPeer(name, subpath, opts) {
|
|
1095
1064
|
const spec = subpath ? `${name}/${subpath}` : name;
|
|
@@ -1100,7 +1069,7 @@ async function loadPeer(name, subpath, opts) {
|
|
|
1100
1069
|
throw e;
|
|
1101
1070
|
}
|
|
1102
1071
|
const dir = peerDirInPrefix(name);
|
|
1103
|
-
if (!existsSync6(
|
|
1072
|
+
if (!existsSync6(join8(dir, "package.json"))) {
|
|
1104
1073
|
if (opts.install === false)
|
|
1105
1074
|
return null;
|
|
1106
1075
|
if (peerVersionGlobal(name, spec) !== null)
|
|
@@ -1111,7 +1080,7 @@ async function loadPeer(name, subpath, opts) {
|
|
|
1111
1080
|
}
|
|
1112
1081
|
var CANVAS_RANGE_FALLBACK = "^0.1.65", readJson = (path) => {
|
|
1113
1082
|
try {
|
|
1114
|
-
return JSON.parse(
|
|
1083
|
+
return JSON.parse(readFileSync7(path, "utf8"));
|
|
1115
1084
|
} catch {
|
|
1116
1085
|
return null;
|
|
1117
1086
|
}
|
|
@@ -1120,7 +1089,7 @@ var CANVAS_RANGE_FALLBACK = "^0.1.65", readJson = (path) => {
|
|
|
1120
1089
|
if (archive)
|
|
1121
1090
|
return archive;
|
|
1122
1091
|
if (name === "@napi-rs/canvas") {
|
|
1123
|
-
const renderer = readJson(
|
|
1092
|
+
const renderer = readJson(join8(peerDirInPrefix("lecodes-headless"), "package.json"));
|
|
1124
1093
|
const range2 = renderer?.peerDependencies?.["@napi-rs/canvas"] ?? renderer?.optionalDependencies?.["@napi-rs/canvas"] ?? CANVAS_RANGE_FALLBACK;
|
|
1125
1094
|
return `@napi-rs/canvas@${range2}`;
|
|
1126
1095
|
}
|
|
@@ -1128,10 +1097,10 @@ var CANVAS_RANGE_FALLBACK = "^0.1.65", readJson = (path) => {
|
|
|
1128
1097
|
return range ? `${name}@${range}` : `${name}@latest`;
|
|
1129
1098
|
}, companions = (name) => name === "lecodes-headless" ? ["lecodes-headless", "@napi-rs/canvas"] : name === "sharp" ? ["sharp", "heic-decode"] : [name], manualCommand = (dir, specs) => commandLine(ownManager(), installIntoArgv(ownManager(), dir, specs)), installPeers = async (names, why, opts = {}) => {
|
|
1130
1099
|
const dir = peersDir();
|
|
1131
|
-
|
|
1132
|
-
const manifest =
|
|
1100
|
+
mkdirSync3(dir, { recursive: true });
|
|
1101
|
+
const manifest = join8(dir, "package.json");
|
|
1133
1102
|
if (!existsSync6(manifest)) {
|
|
1134
|
-
|
|
1103
|
+
writeFileSync4(manifest, JSON.stringify({
|
|
1135
1104
|
name: "lecodes-peers",
|
|
1136
1105
|
private: true,
|
|
1137
1106
|
description: "Optional lecodes-cli peers, installed on first use. Safe to delete — the CLI re-installs what a command needs."
|
|
@@ -1164,11 +1133,11 @@ var CANVAS_RANGE_FALLBACK = "^0.1.65", readJson = (path) => {
|
|
|
1164
1133
|
throw new CliError(`${tool} could not run: ${r.error.message}`);
|
|
1165
1134
|
if (r.status !== 0)
|
|
1166
1135
|
throw new CliError(`${tool} exited with ${r.status ?? "a signal"} while installing ${spec}. By hand: ${manualCommand(dir, [spec])}`);
|
|
1167
|
-
if (!existsSync6(
|
|
1136
|
+
if (!existsSync6(join8(peerDirInPrefix(name), "package.json")))
|
|
1168
1137
|
throw new CliError(`${name} did not land in ${dir} — the installer finished but the package is missing`);
|
|
1169
1138
|
}
|
|
1170
1139
|
}, resolveEntry = (pkgDir, subpath) => {
|
|
1171
|
-
const manifest = readJson(
|
|
1140
|
+
const manifest = readJson(join8(pkgDir, "package.json"));
|
|
1172
1141
|
if (!manifest)
|
|
1173
1142
|
throw new CliError(`${pkgDir} holds no package.json`);
|
|
1174
1143
|
const key = subpath ? `./${subpath}` : ".";
|
|
@@ -1196,7 +1165,7 @@ var CANVAS_RANGE_FALLBACK = "^0.1.65", readJson = (path) => {
|
|
|
1196
1165
|
rel = typeof manifest.main === "string" ? manifest.main : "index.js";
|
|
1197
1166
|
if (!rel)
|
|
1198
1167
|
throw new CliError(`${manifest.name} has no export "${key}"`);
|
|
1199
|
-
return
|
|
1168
|
+
return join8(pkgDir, rel);
|
|
1200
1169
|
}, isNotFound = (e, spec) => {
|
|
1201
1170
|
const code = e?.code;
|
|
1202
1171
|
const msg = e instanceof Error ? e.message : String(e);
|
|
@@ -1789,17 +1758,17 @@ var init_chisel = __esm(() => {
|
|
|
1789
1758
|
});
|
|
1790
1759
|
|
|
1791
1760
|
// ../../sdk/src/compile/bundler.ts
|
|
1792
|
-
import { existsSync as existsSync7, readdirSync as readdirSync2, readFileSync as
|
|
1761
|
+
import { existsSync as existsSync7, readdirSync as readdirSync2, readFileSync as readFileSync8, statSync } from "node:fs";
|
|
1793
1762
|
import { spawnSync as spawnSync3 } from "node:child_process";
|
|
1794
1763
|
import { createRequire as createRequire3 } from "node:module";
|
|
1795
|
-
import { dirname as dirname5, join as
|
|
1764
|
+
import { dirname as dirname5, join as join9, relative as relative2 } from "node:path";
|
|
1796
1765
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
1797
1766
|
var resolveInject = () => {
|
|
1798
1767
|
const override = process.env.LECODES_SDK_INJECT;
|
|
1799
1768
|
if (override && existsSync7(override))
|
|
1800
1769
|
return override;
|
|
1801
1770
|
const here = dirname5(fileURLToPath3(import.meta.url));
|
|
1802
|
-
const src =
|
|
1771
|
+
const src = join9(here, "..", "inject.ts");
|
|
1803
1772
|
if (existsSync7(src))
|
|
1804
1773
|
return src;
|
|
1805
1774
|
throw new Error("sdk: could not locate the inject source (LECODES_SDK_INJECT / src/inject.ts)");
|
|
@@ -1816,7 +1785,7 @@ var resolveInject = () => {
|
|
|
1816
1785
|
return "/" + parts.join("/");
|
|
1817
1786
|
}, CHISEL_DEFINE, CHISEL_KEEP, CLIENT_INJECT = "/__sdk/inject.ts", SERVER_INJECT = "/__sdk/server/inject.ts", EDITOR_INJECT = "/__sdk/inject.editor.ts", readInjectSources = () => {
|
|
1818
1787
|
const client = resolveInject();
|
|
1819
|
-
return { client:
|
|
1788
|
+
return { client: readFileSync8(client, "utf8"), server: readFileSync8(join9(dirname5(client), "server", "inject.ts"), "utf8") };
|
|
1820
1789
|
}, resolveSdkSrc = () => {
|
|
1821
1790
|
const inject = resolveInject();
|
|
1822
1791
|
if (!inject.endsWith("inject.ts")) {
|
|
@@ -1830,11 +1799,11 @@ var resolveInject = () => {
|
|
|
1830
1799
|
const out = {};
|
|
1831
1800
|
const walk = (dir) => {
|
|
1832
1801
|
for (const name of readdirSync2(dir)) {
|
|
1833
|
-
const p =
|
|
1802
|
+
const p = join9(dir, name);
|
|
1834
1803
|
if (statSync(p).isDirectory())
|
|
1835
1804
|
walk(p);
|
|
1836
1805
|
else if (isSdkSource(p))
|
|
1837
|
-
out["/__sdk/" + relative2(sdkSrc, p).split("\\").join("/")] =
|
|
1806
|
+
out["/__sdk/" + relative2(sdkSrc, p).split("\\").join("/")] = readFileSync8(p, "utf8");
|
|
1838
1807
|
}
|
|
1839
1808
|
};
|
|
1840
1809
|
walk(sdkSrc);
|
|
@@ -172261,12 +172230,12 @@ var init_compile = __esm(() => {
|
|
|
172261
172230
|
// src/compile/projectCompile.ts
|
|
172262
172231
|
import { existsSync as existsSync8 } from "node:fs";
|
|
172263
172232
|
import { createRequire as createRequire4 } from "node:module";
|
|
172264
|
-
import { dirname as dirname6, join as
|
|
172233
|
+
import { dirname as dirname6, join as join10 } from "node:path";
|
|
172265
172234
|
var resolveSdkInject = () => {
|
|
172266
172235
|
try {
|
|
172267
172236
|
return createRequire4(import.meta.url).resolve("lecodes-sdk/inject");
|
|
172268
172237
|
} catch {}
|
|
172269
|
-
const vendored =
|
|
172238
|
+
const vendored = join10(distRoot, "runtime", "sdk", "inject.ts");
|
|
172270
172239
|
return existsSync8(vendored) ? vendored : null;
|
|
172271
172240
|
}, sdkInject, resolveChiselBin2 = () => {
|
|
172272
172241
|
if (process.env.LECODES_CHISEL_BIN)
|
|
@@ -172276,7 +172245,7 @@ var resolveSdkInject = () => {
|
|
|
172276
172245
|
if (fromPkg)
|
|
172277
172246
|
return fromPkg;
|
|
172278
172247
|
} catch {}
|
|
172279
|
-
const local =
|
|
172248
|
+
const local = join10(dirname6(process.execPath), process.platform === "win32" ? "chisel.exe" : "chisel");
|
|
172280
172249
|
return existsSync8(local) ? local : null;
|
|
172281
172250
|
}, chiselBin, ambiguousEntryWarning = (entries) => {
|
|
172282
172251
|
const { entry, roots } = detectEntry(entries, scanModulesChisel);
|
|
@@ -172309,15 +172278,15 @@ var resolveSdkInject = () => {
|
|
|
172309
172278
|
};
|
|
172310
172279
|
const dir = peerDirInPrefix("typescript");
|
|
172311
172280
|
const peer = () => {
|
|
172312
|
-
if (!existsSync8(
|
|
172281
|
+
if (!existsSync8(join10(dir, "package.json")))
|
|
172313
172282
|
return null;
|
|
172314
172283
|
try {
|
|
172315
|
-
return api(createRequire4(import.meta.url)(
|
|
172284
|
+
return api(createRequire4(import.meta.url)(join10(dir, "lib", "typescript.js")));
|
|
172316
172285
|
} catch {
|
|
172317
172286
|
return null;
|
|
172318
172287
|
}
|
|
172319
172288
|
};
|
|
172320
|
-
const found = from(
|
|
172289
|
+
const found = from(join10(root, "package.json")) ?? from(import.meta.url) ?? peer();
|
|
172321
172290
|
if (found)
|
|
172322
172291
|
return found;
|
|
172323
172292
|
await installPeers(["typescript"], "The project's server code (*.server.ts)");
|
|
@@ -172344,8 +172313,8 @@ var init_projectCompile = __esm(() => {
|
|
|
172344
172313
|
|
|
172345
172314
|
// src/compile/fonts.ts
|
|
172346
172315
|
import { createHash as createHash2 } from "node:crypto";
|
|
172347
|
-
import { existsSync as existsSync9, mkdirSync as
|
|
172348
|
-
import { dirname as dirname7, join as
|
|
172316
|
+
import { existsSync as existsSync9, mkdirSync as mkdirSync4, readFileSync as readFileSync9, writeFileSync as writeFileSync5 } from "node:fs";
|
|
172317
|
+
import { dirname as dirname7, join as join11 } from "node:path";
|
|
172349
172318
|
var applyProjectFonts = async (entries, opts) => {
|
|
172350
172319
|
const registry = (opts.registry ?? fontRegistryBase()).replace(/\/+$/, "");
|
|
172351
172320
|
const byPath = () => new Map(entries.map((e) => [e.path.replace(/^\//, ""), e]));
|
|
@@ -172354,9 +172323,9 @@ var applyProjectFonts = async (entries, opts) => {
|
|
|
172354
172323
|
registryBase: registry,
|
|
172355
172324
|
readBytes: (entryPath) => {
|
|
172356
172325
|
const entry = byPath().get(entryPath);
|
|
172357
|
-
const abs = entry?.absPath ??
|
|
172326
|
+
const abs = entry?.absPath ?? join11(opts.root, entryPath);
|
|
172358
172327
|
try {
|
|
172359
|
-
return new Uint8Array(
|
|
172328
|
+
return new Uint8Array(readFileSync9(abs));
|
|
172360
172329
|
} catch {
|
|
172361
172330
|
return null;
|
|
172362
172331
|
}
|
|
@@ -172364,7 +172333,7 @@ var applyProjectFonts = async (entries, opts) => {
|
|
|
172364
172333
|
vendorFace: opts.mode === "vendor" ? async (face) => {
|
|
172365
172334
|
const vendorDir = opts.vendorDir ?? opts.root;
|
|
172366
172335
|
const rel = `assets/fonts/${face.fileName}`;
|
|
172367
|
-
const abs =
|
|
172336
|
+
const abs = join11(vendorDir, rel);
|
|
172368
172337
|
const entryPath = `${opts.vendorPrefix ?? ""}/${rel}`;
|
|
172369
172338
|
if (!existsSync9(abs))
|
|
172370
172339
|
await downloadFace(registry, vendorDir, face, rel, abs);
|
|
@@ -172388,13 +172357,13 @@ var applyProjectFonts = async (entries, opts) => {
|
|
|
172388
172357
|
return res.ok ? new Uint8Array(await res.arrayBuffer()) : null;
|
|
172389
172358
|
}
|
|
172390
172359
|
try {
|
|
172391
|
-
return new Uint8Array(
|
|
172360
|
+
return new Uint8Array(readFileSync9(join11(registry, rel)));
|
|
172392
172361
|
} catch {
|
|
172393
172362
|
return null;
|
|
172394
172363
|
}
|
|
172395
172364
|
}, readLock = (path) => {
|
|
172396
172365
|
try {
|
|
172397
|
-
const lock = JSON.parse(
|
|
172366
|
+
const lock = JSON.parse(readFileSync9(path, "utf8"));
|
|
172398
172367
|
if (lock && typeof lock === "object" && lock.faces && lock.families)
|
|
172399
172368
|
return lock;
|
|
172400
172369
|
} catch {}
|
|
@@ -172426,10 +172395,10 @@ var applyProjectFonts = async (entries, opts) => {
|
|
|
172426
172395
|
`);
|
|
172427
172396
|
}, writeIfChanged = (path, text) => {
|
|
172428
172397
|
try {
|
|
172429
|
-
if (
|
|
172398
|
+
if (readFileSync9(path, "utf8") === text)
|
|
172430
172399
|
return;
|
|
172431
172400
|
} catch {}
|
|
172432
|
-
|
|
172401
|
+
writeFileSync5(path, text);
|
|
172433
172402
|
}, downloadFace = async (registry, vendorDir, face, rel, abs) => {
|
|
172434
172403
|
const licRaw = await readRegistry(registry, "licenses.json");
|
|
172435
172404
|
if (!licRaw)
|
|
@@ -172441,15 +172410,15 @@ var applyProjectFonts = async (entries, opts) => {
|
|
|
172441
172410
|
const bytes = await readRegistry(registry, `${face.id}/${face.fileName}`);
|
|
172442
172411
|
if (!bytes)
|
|
172443
172412
|
throw new Error(`font("${face.id}"): face "${face.fileName}" not found in the registry (${registryHome(registry)}).`);
|
|
172444
|
-
|
|
172445
|
-
|
|
172446
|
-
const lockPath =
|
|
172413
|
+
mkdirSync4(dirname7(abs), { recursive: true });
|
|
172414
|
+
writeFileSync5(abs, bytes);
|
|
172415
|
+
const lockPath = join11(vendorDir, "fonts.lock.json");
|
|
172447
172416
|
const lock = readLock(lockPath) ?? { registry, faces: {}, families: {} };
|
|
172448
172417
|
lock.registry = registry;
|
|
172449
172418
|
lock.faces[`${face.id}@${face.weight}${face.italic ? "i" : ""}`] = { id: face.id, family: face.family, weight: face.weight, italic: face.italic, hash: sha12(bytes), spdx: lic.spdx, path: rel };
|
|
172450
172419
|
lock.families[face.id] = lic;
|
|
172451
172420
|
writeIfChanged(lockPath, serializeLock(lock));
|
|
172452
|
-
writeIfChanged(
|
|
172421
|
+
writeIfChanged(join11(vendorDir, "FONT-NOTICES.md"), renderNotices(lock));
|
|
172453
172422
|
};
|
|
172454
172423
|
var init_fonts = __esm(() => {
|
|
172455
172424
|
init_compile();
|
|
@@ -172457,9 +172426,9 @@ var init_fonts = __esm(() => {
|
|
|
172457
172426
|
|
|
172458
172427
|
// src/compile/assetIcons.ts
|
|
172459
172428
|
import { createHash as createHash5 } from "node:crypto";
|
|
172460
|
-
import { existsSync as existsSync13, mkdirSync as
|
|
172461
|
-
import { homedir as
|
|
172462
|
-
import { dirname as dirname8, join as
|
|
172429
|
+
import { existsSync as existsSync13, mkdirSync as mkdirSync7, readFileSync as readFileSync12, writeFileSync as writeFileSync8 } from "node:fs";
|
|
172430
|
+
import { homedir as homedir4 } from "node:os";
|
|
172431
|
+
import { dirname as dirname8, join as join15 } from "node:path";
|
|
172463
172432
|
var DEFAULT_REGISTRY, isHttp2 = (s) => /^https?:/i.test(s), sha122 = (s) => createHash5("sha256").update(s).digest("hex").slice(0, 12), readText = async (registry, rel) => {
|
|
172464
172433
|
if (isHttp2(registry)) {
|
|
172465
172434
|
try {
|
|
@@ -172470,13 +172439,13 @@ var DEFAULT_REGISTRY, isHttp2 = (s) => /^https?:/i.test(s), sha122 = (s) => crea
|
|
|
172470
172439
|
}
|
|
172471
172440
|
}
|
|
172472
172441
|
try {
|
|
172473
|
-
return
|
|
172442
|
+
return readFileSync12(join15(registry, rel), "utf8");
|
|
172474
172443
|
} catch {
|
|
172475
172444
|
return null;
|
|
172476
172445
|
}
|
|
172477
172446
|
}, safeRead = (path) => {
|
|
172478
172447
|
try {
|
|
172479
|
-
return
|
|
172448
|
+
return readFileSync12(path, "utf8");
|
|
172480
172449
|
} catch {
|
|
172481
172450
|
return null;
|
|
172482
172451
|
}
|
|
@@ -172484,7 +172453,7 @@ var DEFAULT_REGISTRY, isHttp2 = (s) => /^https?:/i.test(s), sha122 = (s) => crea
|
|
|
172484
172453
|
const current = existsSync13(path) ? safeRead(path) : null;
|
|
172485
172454
|
if (current === text)
|
|
172486
172455
|
return false;
|
|
172487
|
-
|
|
172456
|
+
writeFileSync8(path, text);
|
|
172488
172457
|
return true;
|
|
172489
172458
|
}, fetchIcon = async (registry, pack, name) => {
|
|
172490
172459
|
const svg = await readText(registry, `${pack}/${name}.svg`);
|
|
@@ -172498,7 +172467,7 @@ var DEFAULT_REGISTRY, isHttp2 = (s) => /^https?:/i.test(s), sha122 = (s) => crea
|
|
|
172498
172467
|
return JSON.parse(raw);
|
|
172499
172468
|
}, readLock2 = (path) => {
|
|
172500
172469
|
try {
|
|
172501
|
-
const lock = JSON.parse(
|
|
172470
|
+
const lock = JSON.parse(readFileSync12(path, "utf8"));
|
|
172502
172471
|
if (lock && typeof lock === "object" && lock.icons && lock.packs)
|
|
172503
172472
|
return lock;
|
|
172504
172473
|
} catch {}
|
|
@@ -172533,13 +172502,13 @@ var DEFAULT_REGISTRY, isHttp2 = (s) => /^https?:/i.test(s), sha122 = (s) => crea
|
|
|
172533
172502
|
`);
|
|
172534
172503
|
}, vendorIcons = async (designDir, ids, opts = {}) => {
|
|
172535
172504
|
const registry = (opts.registry ?? DEFAULT_REGISTRY).replace(/\/+$/, "");
|
|
172536
|
-
const lockPath =
|
|
172505
|
+
const lockPath = join15(designDir, "icons.lock.json");
|
|
172537
172506
|
const lock = readLock2(lockPath) ?? { registry, icons: {}, packs: {} };
|
|
172538
172507
|
const svgById = new Map;
|
|
172539
172508
|
const missing = [];
|
|
172540
172509
|
for (const id of ids) {
|
|
172541
172510
|
const li = lock.icons[id];
|
|
172542
|
-
const cached = li && safeRead(
|
|
172511
|
+
const cached = li && safeRead(join15(designDir, li.path));
|
|
172543
172512
|
if (cached)
|
|
172544
172513
|
svgById.set(id, cached);
|
|
172545
172514
|
else
|
|
@@ -172555,9 +172524,9 @@ var DEFAULT_REGISTRY, isHttp2 = (s) => /^https?:/i.test(s), sha122 = (s) => crea
|
|
|
172555
172524
|
throw new Error(`assetIcon: unknown icon pack "${pack}" (in "${id}"). Available packs: ${Object.keys(licenses).sort().join(", ")}.`);
|
|
172556
172525
|
const svg = await fetchIcon(registry, pack, name);
|
|
172557
172526
|
const rel = `assets/icons/${pack}/${name}.svg`;
|
|
172558
|
-
const abs =
|
|
172559
|
-
|
|
172560
|
-
|
|
172527
|
+
const abs = join15(designDir, rel);
|
|
172528
|
+
mkdirSync7(dirname8(abs), { recursive: true });
|
|
172529
|
+
writeFileSync8(abs, svg);
|
|
172561
172530
|
lock.icons[id] = { pack, name, hash: sha122(svg), spdx: lic.spdx, path: rel };
|
|
172562
172531
|
lock.packs[pack] = { spdx: lic.spdx, title: lic.title, url: lic.url, attribution: lic.attribution, modifiedNotice: lic.modifiedNotice };
|
|
172563
172532
|
svgById.set(id, svg);
|
|
@@ -172566,25 +172535,25 @@ var DEFAULT_REGISTRY, isHttp2 = (s) => /^https?:/i.test(s), sha122 = (s) => crea
|
|
|
172566
172535
|
writeIfChanged2(lockPath, serializeLock2(lock));
|
|
172567
172536
|
}
|
|
172568
172537
|
if (Object.keys(lock.icons).length)
|
|
172569
|
-
writeIfChanged2(
|
|
172538
|
+
writeIfChanged2(join15(designDir, "ICON-NOTICES.md"), renderNotices2(lock));
|
|
172570
172539
|
return svgById;
|
|
172571
172540
|
}, applyAssetIcons = async (entries, designDir, opts = {}) => {
|
|
172572
172541
|
await applyAssetIconMacro(entries, (ids) => vendorIcons(designDir, ids, opts));
|
|
172573
|
-
}, defaultCacheDir = () =>
|
|
172542
|
+
}, defaultCacheDir = () => join15(homedir4(), ".lecodes", "cache", "icons"), cacheIcons = async (ids, opts = {}) => {
|
|
172574
172543
|
const registry = (opts.registry ?? DEFAULT_REGISTRY).replace(/\/+$/, "");
|
|
172575
172544
|
const cacheDir = opts.cacheDir ?? defaultCacheDir();
|
|
172576
172545
|
const svgById = new Map;
|
|
172577
172546
|
for (const id of ids) {
|
|
172578
172547
|
const [, pack, name] = ICON_ID.exec(id);
|
|
172579
|
-
const cachePath =
|
|
172548
|
+
const cachePath = join15(cacheDir, pack, `${name}.svg`);
|
|
172580
172549
|
const cached = safeRead(cachePath);
|
|
172581
172550
|
if (cached) {
|
|
172582
172551
|
svgById.set(id, cached);
|
|
172583
172552
|
continue;
|
|
172584
172553
|
}
|
|
172585
172554
|
const svg = await fetchIcon(registry, pack, name);
|
|
172586
|
-
|
|
172587
|
-
|
|
172555
|
+
mkdirSync7(dirname8(cachePath), { recursive: true });
|
|
172556
|
+
writeFileSync8(cachePath, svg);
|
|
172588
172557
|
svgById.set(id, svg);
|
|
172589
172558
|
}
|
|
172590
172559
|
return svgById;
|
|
@@ -172602,6 +172571,37 @@ var init_assetIcons = __esm(() => {
|
|
|
172602
172571
|
DEFAULT_REGISTRY = process.env.LECODES_ICON_REGISTRY?.trim() || "https://icon-registry.jt3.ru/v1";
|
|
172603
172572
|
});
|
|
172604
172573
|
|
|
172574
|
+
// src/platform/config.ts
|
|
172575
|
+
import { homedir as homedir5 } from "node:os";
|
|
172576
|
+
import { join as join16 } from "node:path";
|
|
172577
|
+
import { mkdirSync as mkdirSync8, readFileSync as readFileSync13, writeFileSync as writeFileSync9 } from "node:fs";
|
|
172578
|
+
var DEFAULT_API = "https://le.codes", CONFIG_DIR, CONFIG_FILE, loadConfig = () => {
|
|
172579
|
+
let stored = {};
|
|
172580
|
+
try {
|
|
172581
|
+
stored = JSON.parse(readFileSync13(CONFIG_FILE, "utf8"));
|
|
172582
|
+
} catch {}
|
|
172583
|
+
return {
|
|
172584
|
+
apiUrl: process.env.LECODES_API ?? stored.apiUrl,
|
|
172585
|
+
token: process.env.LECODES_TOKEN ?? stored.token
|
|
172586
|
+
};
|
|
172587
|
+
}, saveConfig = (config) => {
|
|
172588
|
+
mkdirSync8(CONFIG_DIR, { recursive: true });
|
|
172589
|
+
writeFileSync9(CONFIG_FILE, JSON.stringify(config, null, 2));
|
|
172590
|
+
}, normalizeApiUrl = (url) => url.trim().replace(/\/+$/, ""), requireToken = (config) => {
|
|
172591
|
+
if (!config.token)
|
|
172592
|
+
throw new CliError("Not logged in. Run `lecodes login` first.");
|
|
172593
|
+
return config.token;
|
|
172594
|
+
}, requireApiUrl = (config) => {
|
|
172595
|
+
if (!config.apiUrl)
|
|
172596
|
+
throw new CliError("No server URL configured. Run `lecodes login` first.");
|
|
172597
|
+
return normalizeApiUrl(config.apiUrl);
|
|
172598
|
+
};
|
|
172599
|
+
var init_config = __esm(() => {
|
|
172600
|
+
init_cli();
|
|
172601
|
+
CONFIG_DIR = join16(homedir5(), ".lecodes");
|
|
172602
|
+
CONFIG_FILE = join16(CONFIG_DIR, "config.json");
|
|
172603
|
+
});
|
|
172604
|
+
|
|
172605
172605
|
// src/commands/shared.ts
|
|
172606
172606
|
import { basename as basename4 } from "node:path";
|
|
172607
172607
|
var bundleFlags, outFlag, bundleOptions = (flags) => ({
|
|
@@ -217173,7 +217173,6 @@ import { delimiter as delimiter3, dirname as dirname28, join as join59 } from "n
|
|
|
217173
217173
|
|
|
217174
217174
|
// src/commands/app/index.ts
|
|
217175
217175
|
init_manifest();
|
|
217176
|
-
init_config();
|
|
217177
217176
|
import { execFileSync as execFileSync6 } from "node:child_process";
|
|
217178
217177
|
import { cpSync as cpSync4, existsSync as existsSync23, mkdirSync as mkdirSync15, readFileSync as readFileSync20, readdirSync as readdirSync9, rmSync as rmSync7, statSync as statSync9, writeFileSync as writeFileSync18 } from "node:fs";
|
|
217179
217178
|
import { homedir as homedir10 } from "node:os";
|
|
@@ -217188,16 +217187,16 @@ import { basename as basename5, resolve as resolve5 } from "node:path";
|
|
|
217188
217187
|
// src/compile/collect.ts
|
|
217189
217188
|
init_localFiles();
|
|
217190
217189
|
init_ignore();
|
|
217191
|
-
import { readFileSync as
|
|
217190
|
+
import { readFileSync as readFileSync4 } from "node:fs";
|
|
217192
217191
|
import { basename } from "node:path";
|
|
217193
217192
|
|
|
217194
217193
|
// src/project/materialize.ts
|
|
217195
217194
|
init_api();
|
|
217196
217195
|
init_paths();
|
|
217197
|
-
import { dirname as dirname2, join as
|
|
217198
|
-
import { mkdirSync as
|
|
217196
|
+
import { dirname as dirname2, join as join4 } from "node:path";
|
|
217197
|
+
import { mkdirSync as mkdirSync2, writeFileSync as writeFileSync3 } from "node:fs";
|
|
217199
217198
|
var kindOf = (type) => type === "file" ? "resource" : type;
|
|
217200
|
-
var absOf = (root, key) =>
|
|
217199
|
+
var absOf = (root, key) => join4(root, ...key.replace(/^\//, "").split("/"));
|
|
217201
217200
|
var buildPaths = (assets) => {
|
|
217202
217201
|
const pathById = new Map;
|
|
217203
217202
|
for (const asset of assets) {
|
|
@@ -217215,20 +217214,20 @@ var materializeAssets = async (root, project, apiUrl, token, onProgress) => {
|
|
|
217215
217214
|
const key = pathById.get(asset.id);
|
|
217216
217215
|
const abs = absOf(root, key);
|
|
217217
217216
|
if (asset.type === "folder") {
|
|
217218
|
-
|
|
217217
|
+
mkdirSync2(abs, { recursive: true });
|
|
217219
217218
|
continue;
|
|
217220
217219
|
}
|
|
217221
|
-
|
|
217220
|
+
mkdirSync2(dirname2(abs), { recursive: true });
|
|
217222
217221
|
let sha;
|
|
217223
217222
|
if (asset.type === "file") {
|
|
217224
217223
|
if (!asset.file?.src)
|
|
217225
217224
|
continue;
|
|
217226
217225
|
const bytes = await downloadBlob(apiUrl, asset.file.src);
|
|
217227
|
-
|
|
217226
|
+
writeFileSync3(abs, bytes);
|
|
217228
217227
|
sha = sha256(bytes);
|
|
217229
217228
|
} else {
|
|
217230
217229
|
const text = asset.text ?? "";
|
|
217231
|
-
|
|
217230
|
+
writeFileSync3(abs, text);
|
|
217232
217231
|
sha = sha256(text);
|
|
217233
217232
|
}
|
|
217234
217233
|
files[key] = { id: asset.id, type: kindOf(asset.type), sha };
|
|
@@ -217262,7 +217261,7 @@ var collectEntries = (root, manifest, project, opts = {}) => {
|
|
|
217262
217261
|
warnings.push(`No compiled shader for ${file.path} — push it to compile the shader.`);
|
|
217263
217262
|
entries.push({ path: file.path, type, shaders, absPath: file.absPath });
|
|
217264
217263
|
} else {
|
|
217265
|
-
entries.push({ path: file.path, type, text:
|
|
217264
|
+
entries.push({ path: file.path, type, text: readFileSync4(file.absPath, "utf8") });
|
|
217266
217265
|
}
|
|
217267
217266
|
}
|
|
217268
217267
|
return { entries, warnings };
|
|
@@ -217271,7 +217270,7 @@ var collectEntries = (root, manifest, project, opts = {}) => {
|
|
|
217271
217270
|
// src/compile/collectLocal.ts
|
|
217272
217271
|
init_localFiles();
|
|
217273
217272
|
init_ignore();
|
|
217274
|
-
import { readFileSync as
|
|
217273
|
+
import { readFileSync as readFileSync5 } from "node:fs";
|
|
217275
217274
|
import { basename as basename2 } from "node:path";
|
|
217276
217275
|
var collectLocalEntries = (root, manifest, assetBase, opts = {}) => {
|
|
217277
217276
|
const entries = [];
|
|
@@ -217287,7 +217286,7 @@ var collectLocalEntries = (root, manifest, assetBase, opts = {}) => {
|
|
|
217287
217286
|
}
|
|
217288
217287
|
entries.push({ path: file.path, type, shaders: [], absPath: file.absPath });
|
|
217289
217288
|
} else {
|
|
217290
|
-
entries.push({ path: file.path, type, text:
|
|
217289
|
+
entries.push({ path: file.path, type, text: readFileSync5(file.absPath, "utf8") });
|
|
217291
217290
|
}
|
|
217292
217291
|
}
|
|
217293
217292
|
return { entries, warnings };
|
|
@@ -217301,26 +217300,26 @@ init_fonts();
|
|
|
217301
217300
|
init_compile();
|
|
217302
217301
|
import { execFile } from "node:child_process";
|
|
217303
217302
|
import { createHash as createHash4, randomBytes } from "node:crypto";
|
|
217304
|
-
import { existsSync as existsSync12, mkdirSync as
|
|
217303
|
+
import { existsSync as existsSync12, mkdirSync as mkdirSync6, readFileSync as readFileSync11, rmSync as rmSync2, statSync as statSync2, writeFileSync as writeFileSync7 } from "node:fs";
|
|
217305
217304
|
import { tmpdir } from "node:os";
|
|
217306
|
-
import { basename as basename3, join as
|
|
217305
|
+
import { basename as basename3, join as join14 } from "node:path";
|
|
217307
217306
|
|
|
217308
217307
|
// src/hosts/matcTool.ts
|
|
217309
217308
|
import { existsSync as existsSync11, readdirSync as readdirSync3 } from "node:fs";
|
|
217310
|
-
import { homedir as
|
|
217311
|
-
import { join as
|
|
217309
|
+
import { homedir as homedir3 } from "node:os";
|
|
217310
|
+
import { join as join13, resolve as resolve3 } from "node:path";
|
|
217312
217311
|
|
|
217313
217312
|
// src/hosts/releases.ts
|
|
217314
217313
|
init_cli();
|
|
217315
217314
|
import { createHash as createHash3 } from "node:crypto";
|
|
217316
|
-
import { existsSync as existsSync10, mkdirSync as
|
|
217317
|
-
import { join as
|
|
217315
|
+
import { existsSync as existsSync10, mkdirSync as mkdirSync5, mkdtempSync, readFileSync as readFileSync10, renameSync, rmSync, writeFileSync as writeFileSync6 } from "node:fs";
|
|
217316
|
+
import { join as join12 } from "node:path";
|
|
217318
217317
|
import { spawnSync as spawnSync4 } from "node:child_process";
|
|
217319
217318
|
var MANIFEST_URL = "https://storage.yandexcloud.net/le-codes/releases/versions.json";
|
|
217320
217319
|
var ARTIFACT_STAMP = ".lecodes-artifact.json";
|
|
217321
217320
|
var cachedArtifactSha = (dir) => {
|
|
217322
217321
|
try {
|
|
217323
|
-
return JSON.parse(
|
|
217322
|
+
return JSON.parse(readFileSync10(join12(dir, ARTIFACT_STAMP), "utf8")).sha256 ?? null;
|
|
217324
217323
|
} catch {
|
|
217325
217324
|
return null;
|
|
217326
217325
|
}
|
|
@@ -217366,16 +217365,16 @@ var downloadReleaseArchive = async (file, opts) => {
|
|
|
217366
217365
|
const sha = createHash3("sha256").update(bytes).digest("hex");
|
|
217367
217366
|
if (sha !== file.sha256)
|
|
217368
217367
|
throw new CliError(`Downloaded archive is corrupt (sha256 mismatch) — try again.`);
|
|
217369
|
-
|
|
217370
|
-
const stage = mkdtempSync(
|
|
217368
|
+
mkdirSync5(opts.cacheRoot, { recursive: true });
|
|
217369
|
+
const stage = mkdtempSync(join12(opts.cacheRoot, "dl-"));
|
|
217371
217370
|
try {
|
|
217372
217371
|
const archiveName = file.url.split("/").pop() ?? "archive";
|
|
217373
|
-
const archive =
|
|
217374
|
-
|
|
217372
|
+
const archive = join12(stage, archiveName);
|
|
217373
|
+
writeFileSync6(archive, bytes);
|
|
217375
217374
|
if (opts.extract) {
|
|
217376
217375
|
opts.extract(archive, stage);
|
|
217377
217376
|
} else {
|
|
217378
|
-
const winTar =
|
|
217377
|
+
const winTar = join12(process.env.SystemRoot ?? "C:\\Windows", "System32", "tar.exe");
|
|
217379
217378
|
const tarExe = process.platform === "win32" && existsSync10(winTar) ? winTar : "tar";
|
|
217380
217379
|
const tar = spawnSync4(tarExe, ["-xf", archiveName], { cwd: stage, stdio: ["ignore", "ignore", "pipe"] });
|
|
217381
217380
|
if (tar.status !== 0)
|
|
@@ -217385,7 +217384,7 @@ var downloadReleaseArchive = async (file, opts) => {
|
|
|
217385
217384
|
const bad = opts.validate(stage);
|
|
217386
217385
|
if (bad)
|
|
217387
217386
|
throw new CliError(bad);
|
|
217388
|
-
|
|
217387
|
+
writeFileSync6(join12(stage, ARTIFACT_STAMP), JSON.stringify({ sha256: file.sha256, url: file.url }, null, 2));
|
|
217389
217388
|
try {
|
|
217390
217389
|
renameSync(stage, opts.dest);
|
|
217391
217390
|
} catch {
|
|
@@ -217415,11 +217414,11 @@ var platformKey = () => {
|
|
|
217415
217414
|
return macPlatformKey();
|
|
217416
217415
|
throw new CliError(`matc isn't published for ${process.platform}.`);
|
|
217417
217416
|
};
|
|
217418
|
-
var cacheRoot = () =>
|
|
217417
|
+
var cacheRoot = () => join13(homedir3(), ".lecodes", "matc");
|
|
217419
217418
|
var findMatc = (dir) => {
|
|
217420
217419
|
const name = matcName();
|
|
217421
217420
|
for (const entry of readdirSync3(dir, { withFileTypes: true })) {
|
|
217422
|
-
const p =
|
|
217421
|
+
const p = join13(dir, entry.name);
|
|
217423
217422
|
if (entry.isFile() && entry.name === name)
|
|
217424
217423
|
return p;
|
|
217425
217424
|
if (entry.isDirectory()) {
|
|
@@ -217432,7 +217431,7 @@ var findMatc = (dir) => {
|
|
|
217432
217431
|
};
|
|
217433
217432
|
var installedMatcVersions = () => {
|
|
217434
217433
|
try {
|
|
217435
|
-
return readdirSync3(cacheRoot(), { withFileTypes: true }).filter((e) => e.isDirectory() && e.name.startsWith("matc-")).map((e) => e.name.slice("matc-".length)).filter((v) => findMatc(
|
|
217434
|
+
return readdirSync3(cacheRoot(), { withFileTypes: true }).filter((e) => e.isDirectory() && e.name.startsWith("matc-")).map((e) => e.name.slice("matc-".length)).filter((v) => findMatc(join13(cacheRoot(), `matc-${v}`)) !== null).sort(semverNewer);
|
|
217436
217435
|
} catch {
|
|
217437
217436
|
return [];
|
|
217438
217437
|
}
|
|
@@ -217462,7 +217461,7 @@ var resolveMatcExeCached = () => {
|
|
|
217462
217461
|
return resolve3(override);
|
|
217463
217462
|
}
|
|
217464
217463
|
const cached = installedMatcVersions()[0];
|
|
217465
|
-
return cached ? findMatc(
|
|
217464
|
+
return cached ? findMatc(join13(cacheRoot(), `matc-${cached}`)) : null;
|
|
217466
217465
|
};
|
|
217467
217466
|
var resolveMatcExe = async () => {
|
|
217468
217467
|
const cached = resolveMatcExeCached();
|
|
@@ -217470,7 +217469,7 @@ var resolveMatcExe = async () => {
|
|
|
217470
217469
|
return cached;
|
|
217471
217470
|
const { versions } = await fetchMatcManifest();
|
|
217472
217471
|
const { version, file } = pickMatc(versions);
|
|
217473
|
-
const dir =
|
|
217472
|
+
const dir = join13(cacheRoot(), `matc-${version}`);
|
|
217474
217473
|
await downloadMatc(version, file, dir);
|
|
217475
217474
|
const exe = findMatc(dir);
|
|
217476
217475
|
if (!exe)
|
|
@@ -217481,7 +217480,7 @@ var updateMatc = async () => {
|
|
|
217481
217480
|
const { versions } = await fetchMatcManifest();
|
|
217482
217481
|
const { version, file } = pickMatc(versions);
|
|
217483
217482
|
const before = installedMatcVersions()[0];
|
|
217484
|
-
const dir =
|
|
217483
|
+
const dir = join13(cacheRoot(), `matc-${version}`);
|
|
217485
217484
|
if (existsSync11(dir) && findMatc(dir) !== null) {
|
|
217486
217485
|
note(`Already up to date: matc ${version}.`);
|
|
217487
217486
|
return;
|
|
@@ -217494,7 +217493,7 @@ var updateMatc = async () => {
|
|
|
217494
217493
|
init_manifest();
|
|
217495
217494
|
init_cli();
|
|
217496
217495
|
var SHADERS_DIR = "shaders";
|
|
217497
|
-
var shadersCacheDir = (root) =>
|
|
217496
|
+
var shadersCacheDir = (root) => join14(root, LECODES_DIR, SHADERS_DIR);
|
|
217498
217497
|
var isShaderArtifactName = (name) => /^[0-9a-f]{16}_[a-z-]+\.filamat$/.test(name);
|
|
217499
217498
|
var matcIdentity = (exe) => {
|
|
217500
217499
|
const stat = statSync2(exe);
|
|
@@ -217504,17 +217503,17 @@ var shaderHash = (text, matcId) => createHash4("sha256").update(`${TARGET_MATRIX
|
|
|
217504
217503
|
var artifactFamily = () => [...MATC_PLATFORMS, ...LOCAL_ONLY_PLATFORMS, "webgl-lite"];
|
|
217505
217504
|
var shaderIsCompiled = (root, text, matcId) => {
|
|
217506
217505
|
const hash = shaderHash(text, matcId);
|
|
217507
|
-
return artifactFamily().every((p) => existsSync12(
|
|
217506
|
+
return artifactFamily().every((p) => existsSync12(join14(shadersCacheDir(root), `${hash}_${p}.filamat`)));
|
|
217508
217507
|
};
|
|
217509
217508
|
var shaderCompiledPlatforms = (root, text, matcId) => {
|
|
217510
217509
|
const hash = shaderHash(text, matcId);
|
|
217511
|
-
return artifactFamily().filter((p) => existsSync12(
|
|
217510
|
+
return artifactFamily().filter((p) => existsSync12(join14(shadersCacheDir(root), `${hash}_${p}.filamat`)));
|
|
217512
217511
|
};
|
|
217513
217512
|
var localShaderArtifact = (root, text, platform) => {
|
|
217514
217513
|
const matcExe = resolveMatcExeCached();
|
|
217515
217514
|
if (!matcExe)
|
|
217516
217515
|
return null;
|
|
217517
|
-
const file =
|
|
217516
|
+
const file = join14(shadersCacheDir(root), `${shaderHash(text, matcIdentity(matcExe))}_${platform}.filamat`);
|
|
217518
217517
|
return existsSync12(file) ? file : null;
|
|
217519
217518
|
};
|
|
217520
217519
|
var allShaderPlatforms = () => [...Object.keys(ALL_PLATFORM_BUILD), "webgl-lite"];
|
|
@@ -217548,25 +217547,25 @@ var applyLocalShaders = async (entries, opts) => {
|
|
|
217548
217547
|
}
|
|
217549
217548
|
const matcId = matcIdentity(matcExe);
|
|
217550
217549
|
const cacheDir = shadersCacheDir(opts.root);
|
|
217551
|
-
|
|
217550
|
+
mkdirSync6(cacheDir, { recursive: true });
|
|
217552
217551
|
const artifacts = [];
|
|
217553
217552
|
for (const entry of shaderEntries) {
|
|
217554
|
-
const text =
|
|
217553
|
+
const text = readFileSync11(entry.absPath, "utf8");
|
|
217555
217554
|
const hash = shaderHash(text, matcId);
|
|
217556
217555
|
const family = artifactFamily();
|
|
217557
217556
|
const artifact = (platform) => `${hash}_${platform}.filamat`;
|
|
217558
|
-
const has = (platform) => existsSync12(
|
|
217557
|
+
const has = (platform) => existsSync12(join14(cacheDir, artifact(platform)));
|
|
217559
217558
|
const wanted = opts.platforms ? family.filter((p) => opts.platforms.includes(p)) : family;
|
|
217560
217559
|
const missing = wanted.filter((p) => !has(p));
|
|
217561
217560
|
const matcMissing = missing.filter((p) => p !== "webgl-lite");
|
|
217562
217561
|
if (matcMissing.length > 0) {
|
|
217563
217562
|
const tempName = `lecodes-matc-${randomBytes(6).toString("hex")}.mat`;
|
|
217564
|
-
const tempFile =
|
|
217563
|
+
const tempFile = join14(tmpdir(), tempName);
|
|
217565
217564
|
try {
|
|
217566
217565
|
for (const platform of matcMissing) {
|
|
217567
217566
|
const build = ALL_PLATFORM_BUILD[platform];
|
|
217568
|
-
|
|
217569
|
-
const out =
|
|
217567
|
+
writeFileSync7(tempFile, build.replaceExternal ? text.replace(externalSamplerReg, "type:sampler2d") : text);
|
|
217568
|
+
const out = join14(cacheDir, artifact(platform));
|
|
217570
217569
|
const { ok, stderr } = await runMatc(matcExe, matcArgs(platform, out, tempFile));
|
|
217571
217570
|
if (!ok) {
|
|
217572
217571
|
rmSync2(out, { force: true });
|
|
@@ -217579,7 +217578,7 @@ ${cleanMatcOutput(stderr, tempName, basename3(entry.path))}`);
|
|
|
217579
217578
|
}
|
|
217580
217579
|
}
|
|
217581
217580
|
if (missing.includes("webgl-lite")) {
|
|
217582
|
-
|
|
217581
|
+
writeFileSync7(join14(cacheDir, artifact("webgl-lite")), JSON.stringify(buildLiteMaterialArtifact(text)));
|
|
217583
217582
|
}
|
|
217584
217583
|
if (opts.local) {
|
|
217585
217584
|
const staged = opts.platforms ? ALL_NATIVE_PLATFORMS.filter((p) => opts.platforms.includes(p)) : NATIVE_PLATFORMS;
|
|
@@ -217589,7 +217588,7 @@ ${cleanMatcOutput(stderr, tempName, basename3(entry.path))}`);
|
|
|
217589
217588
|
];
|
|
217590
217589
|
entry.shadersLocal = true;
|
|
217591
217590
|
for (const platform of staged) {
|
|
217592
|
-
artifacts.push({ name: artifact(platform), absPath:
|
|
217591
|
+
artifacts.push({ name: artifact(platform), absPath: join14(cacheDir, artifact(platform)) });
|
|
217593
217592
|
}
|
|
217594
217593
|
} else {
|
|
217595
217594
|
const order = ["opengl", "metal", "desktop", "webgl-lite"];
|
|
@@ -217605,6 +217604,7 @@ init_ignore();
|
|
|
217605
217604
|
|
|
217606
217605
|
// src/commands/app/shared.ts
|
|
217607
217606
|
init_manifest();
|
|
217607
|
+
init_config();
|
|
217608
217608
|
init_ignore();
|
|
217609
217609
|
init_cli();
|
|
217610
217610
|
import { execFileSync } from "node:child_process";
|
|
@@ -217812,6 +217812,26 @@ var desktopDefaultViewport = (root) => {
|
|
|
217812
217812
|
};
|
|
217813
217813
|
var swiftPackageName = (url) => (url.replace(/\/+$/, "").split("/").pop() ?? url).replace(/\.git$/, "");
|
|
217814
217814
|
var isLocalPluginSpec = (spec) => spec.startsWith("./") || spec.startsWith("../") || spec.startsWith("/") || spec.startsWith("file:");
|
|
217815
|
+
var resolveUpdateUrl = (update, root, sdkVersion) => {
|
|
217816
|
+
if (update === false)
|
|
217817
|
+
return;
|
|
217818
|
+
let url = typeof update === "string" ? update : typeof update === "object" ? update.url : undefined;
|
|
217819
|
+
if (!url) {
|
|
217820
|
+
let manifest = null;
|
|
217821
|
+
try {
|
|
217822
|
+
manifest = readManifest(root);
|
|
217823
|
+
} catch {}
|
|
217824
|
+
if (!manifest) {
|
|
217825
|
+
if (update)
|
|
217826
|
+
warn(`app.json "update": true needs a server-backed project — use "update": {"url": "https://…/app.js"} for custom hosting. OTA left unconfigured.`);
|
|
217827
|
+
return;
|
|
217828
|
+
}
|
|
217829
|
+
url = `${requireApiUrl(loadConfig())}/code/${manifest.uuid}.js?sdk=${sdkVersion}`;
|
|
217830
|
+
}
|
|
217831
|
+
if (!url.startsWith("https://"))
|
|
217832
|
+
warn(`update URL is not https — the phones block it on device (ATS / cleartext policy): ${url}`);
|
|
217833
|
+
return url;
|
|
217834
|
+
};
|
|
217815
217835
|
var sdkSatisfies = (version, range) => {
|
|
217816
217836
|
const op = range.startsWith("^") ? "^" : range.startsWith("~") ? "~" : "";
|
|
217817
217837
|
const want = range.slice(op.length);
|
|
@@ -219833,14 +219853,14 @@ android {
|
|
|
219833
219853
|
minSdk = ${MIN_SDK}
|
|
219834
219854
|
}
|
|
219835
219855
|
compileOptions {
|
|
219836
|
-
sourceCompatibility = JavaVersion.
|
|
219837
|
-
targetCompatibility = JavaVersion.
|
|
219856
|
+
sourceCompatibility = JavaVersion.VERSION_17
|
|
219857
|
+
targetCompatibility = JavaVersion.VERSION_17
|
|
219838
219858
|
}
|
|
219839
219859
|
}
|
|
219840
219860
|
|
|
219841
219861
|
kotlin {
|
|
219842
219862
|
compilerOptions {
|
|
219843
|
-
jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.
|
|
219863
|
+
jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17)
|
|
219844
219864
|
}
|
|
219845
219865
|
}
|
|
219846
219866
|
|
|
@@ -219853,6 +219873,15 @@ dependencies {
|
|
|
219853
219873
|
}
|
|
219854
219874
|
`;
|
|
219855
219875
|
};
|
|
219876
|
+
var runtimeManifestTemplate = (updateUrl) => `<?xml version="1.0" encoding="utf-8"?>
|
|
219877
|
+
<!-- Generated by \`lecodes app sync\` — regenerated on every sync, do not edit. -->
|
|
219878
|
+
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
|
219879
|
+
<application>${updateUrl ? `
|
|
219880
|
+
<!-- lecodes:update — native OTA source (app.json "update"); LeCodesShell.boot checks it at launch -->
|
|
219881
|
+
<meta-data android:name="io.letary.lecodes.UPDATE_URL" android:value="${updateUrl.replace(/&/g, "&")}" />` : ""}
|
|
219882
|
+
</application>
|
|
219883
|
+
</manifest>
|
|
219884
|
+
`;
|
|
219856
219885
|
var runtimeKotlinTemplate = (plugins, projectUuid) => {
|
|
219857
219886
|
const identity = projectUuid ? ` engine.bootProjectUuid = "${projectUuid}"
|
|
219858
219887
|
` : "";
|
|
@@ -219915,14 +219944,14 @@ android {
|
|
|
219915
219944
|
minSdk = ${MIN_SDK}${consumer}
|
|
219916
219945
|
}
|
|
219917
219946
|
compileOptions {
|
|
219918
|
-
sourceCompatibility = JavaVersion.
|
|
219919
|
-
targetCompatibility = JavaVersion.
|
|
219947
|
+
sourceCompatibility = JavaVersion.VERSION_17
|
|
219948
|
+
targetCompatibility = JavaVersion.VERSION_17
|
|
219920
219949
|
}
|
|
219921
219950
|
}
|
|
219922
219951
|
|
|
219923
219952
|
kotlin {
|
|
219924
219953
|
compilerOptions {
|
|
219925
|
-
jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.
|
|
219954
|
+
jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17)
|
|
219926
219955
|
}
|
|
219927
219956
|
}
|
|
219928
219957
|
|
|
@@ -220036,7 +220065,22 @@ the init-time placeholder.
|
|
|
220036
220065
|
|
|
220037
220066
|
## Notes
|
|
220038
220067
|
|
|
220039
|
-
|
|
220068
|
+
|
|
220069
|
+
## Over-the-air updates
|
|
220070
|
+
|
|
220071
|
+
Native, before the JS engine: \`LeCodesShell.boot(engine, context)\` in \`AppViewModel\` asks the
|
|
220072
|
+
source for newer code (a conditional GET; offline is a no-op), stores it and runs it on the NEXT
|
|
220073
|
+
launch — the current launch runs the newest LOCAL bundle. Configured in \`../app.json\`:
|
|
220074
|
+
\`"update": true\` = the project's live bundle on the platform (a pushed project), \`"update":
|
|
220075
|
+
{ "url": "https://…/app.js" }\` = your own hosting, \`false\` = off. Sync writes the URL into
|
|
220076
|
+
\`lecodes-runtime/src/main/AndroidManifest.xml\` (meta-data \`io.letary.lecodes.UPDATE_URL\`). A
|
|
220077
|
+
download runs only when it is of this shell's SDK major and not older than the bundle running; a
|
|
220078
|
+
store update always wins over a downloaded bundle. A shell generated before OTA existed reads
|
|
220079
|
+
\`assets/app.js\` itself — replace that with:
|
|
220080
|
+
|
|
220081
|
+
\`\`\`kotlin
|
|
220082
|
+
if (!LeCodesShell.boot(engine, getApplication<Application>())) Log.e("LeCodes", "bundled app.js is missing — run \`lecodes app sync\`")
|
|
220083
|
+
\`\`\`
|
|
220040
220084
|
- Push: the notification service needs \`app/google-services.json\` from the le.codes Firebase
|
|
220041
220085
|
project; \`lecodes app sync\` applies the google-services Gradle plugin only when that file
|
|
220042
220086
|
exists (and warns otherwise).
|
|
@@ -220096,14 +220140,14 @@ android {
|
|
|
220096
220140
|
}
|
|
220097
220141
|
}
|
|
220098
220142
|
compileOptions {
|
|
220099
|
-
sourceCompatibility = JavaVersion.
|
|
220100
|
-
targetCompatibility = JavaVersion.
|
|
220143
|
+
sourceCompatibility = JavaVersion.VERSION_17
|
|
220144
|
+
targetCompatibility = JavaVersion.VERSION_17
|
|
220101
220145
|
}
|
|
220102
220146
|
}
|
|
220103
220147
|
|
|
220104
220148
|
kotlin {
|
|
220105
220149
|
compilerOptions {
|
|
220106
|
-
jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.
|
|
220150
|
+
jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17)
|
|
220107
220151
|
}
|
|
220108
220152
|
}
|
|
220109
220153
|
|
|
@@ -220243,6 +220287,7 @@ package ${pkg}
|
|
|
220243
220287
|
import android.app.Activity
|
|
220244
220288
|
import android.app.Application
|
|
220245
220289
|
import androidx.lifecycle.AndroidViewModel
|
|
220290
|
+
import io.letary.lecodes.LeCodesShell
|
|
220246
220291
|
import io.letary.lecodes.LecodesEngine
|
|
220247
220292
|
|
|
220248
220293
|
class AppViewModel(application: Application) : AndroidViewModel(application) {
|
|
@@ -220266,8 +220311,14 @@ class AppViewModel(application: Application) : AndroidViewModel(application) {
|
|
|
220266
220311
|
fun onActivityCreate(activity: Activity) {
|
|
220267
220312
|
engine.attach(activity)
|
|
220268
220313
|
if (!isCreated) {
|
|
220269
|
-
|
|
220270
|
-
|
|
220314
|
+
// OTA (native, no JS involved): LeCodesShell.boot starts the check FIRST — even a bundle
|
|
220315
|
+
// that crashes on boot can never block its own fix — then runs the newest local bundle
|
|
220316
|
+
// (a downloaded update, or the embedded assets/app.js). New code applies on the next
|
|
220317
|
+
// launch. Configured via app.json "update" (→ the runtime module's manifest meta-data);
|
|
220318
|
+
// offline: no-op.
|
|
220319
|
+
if (!LeCodesShell.boot(engine, getApplication<Application>())) {
|
|
220320
|
+
android.util.Log.e("LeCodes", "bundled app.js is missing — run \`lecodes app sync\`")
|
|
220321
|
+
}
|
|
220271
220322
|
isCreated = true
|
|
220272
220323
|
}
|
|
220273
220324
|
}
|
|
@@ -220667,7 +220718,7 @@ if "%OS%"=="Windows_NT" endlocal
|
|
|
220667
220718
|
|
|
220668
220719
|
// src/commands/app/android.ts
|
|
220669
220720
|
var ANDROID_SDK_REPO = "letary/lecodes-android-sdk";
|
|
220670
|
-
var ANDROID_SDK_MIN_VERSION = "
|
|
220721
|
+
var ANDROID_SDK_MIN_VERSION = "2.0.13";
|
|
220671
220722
|
var ANDROID_SDK_VARIANTS_MIN = "1.3.0";
|
|
220672
220723
|
var variantArtifact = (variant) => variant === "full" ? "lecodes-sdk" : `lecodes-sdk-${variant}`;
|
|
220673
220724
|
var ANDROID_SDK_URL_DEFAULT = `https://github.com/${ANDROID_SDK_REPO}/releases/download`;
|
|
@@ -220931,6 +220982,10 @@ var syncAndroid = async (root, config, prior) => {
|
|
|
220931
220982
|
const runtimeKt = join21(androidDir, "lecodes-runtime", "src", "main", "kotlin", "io", "letary", "lecodes", "runtime", "LeCodesRuntime.kt");
|
|
220932
220983
|
mkdirSync11(dirname12(runtimeKt), { recursive: true });
|
|
220933
220984
|
writeFileSync14(runtimeKt, runtimeKotlinTemplate(runtimePlugins, shellUuid));
|
|
220985
|
+
const updateUrl = resolveUpdateUrl(config.update, root, sdkVersion);
|
|
220986
|
+
writeFileSync14(join21(androidDir, "lecodes-runtime", "src", "main", "AndroidManifest.xml"), runtimeManifestTemplate(updateUrl));
|
|
220987
|
+
if (updateUrl)
|
|
220988
|
+
log(`${c.green("update")} OTA source ${updateUrl} — checked natively at launch (LeCodesShell.boot), new code applies next launch`);
|
|
220934
220989
|
if (pluginSpecs.length > 0 || existsSync19(lockPath))
|
|
220935
220990
|
writeJson(lockPath, { plugins: newLock });
|
|
220936
220991
|
restampAppBuildGradle(androidDir, config, gradlePlugins);
|
|
@@ -220938,9 +220993,6 @@ var syncAndroid = async (root, config, prior) => {
|
|
|
220938
220993
|
await writeAndroidIcons(androidDir, await loadIconMaster(root, config));
|
|
220939
220994
|
restampManifestIcon(androidDir);
|
|
220940
220995
|
}
|
|
220941
|
-
if (config.update) {
|
|
220942
|
-
note(`app.json "update" is iOS-only for now — the Android runtime has no native updater, so it's ignored for this shell.`);
|
|
220943
|
-
}
|
|
220944
220996
|
log("");
|
|
220945
220997
|
success(`Android shell synced: app.js + ${runtimePlugins.length} plugin(s), SDK ${sdkVersion}, v${config.version ?? "1.0.0"} (${config.android?.versionCode ?? 1}).`);
|
|
220946
220998
|
note("Build & run from Android Studio (`lecodes app open android`) — user files in android/app/ were not touched.");
|
|
@@ -221467,6 +221519,14 @@ var doctorAndroidChecks = async (root, config, check) => {
|
|
|
221467
221519
|
check(cmpVersion(linked, latest) >= 0, `SDK is the latest release (${latest})`, "run `lecodes app update`", false);
|
|
221468
221520
|
} catch {}
|
|
221469
221521
|
}
|
|
221522
|
+
if (config.update) {
|
|
221523
|
+
const manifest = join21(androidDir, "lecodes-runtime", "src", "main", "AndroidManifest.xml");
|
|
221524
|
+
check(existsSync19(manifest) && readFileSync18(manifest, "utf8").includes("io.letary.lecodes.UPDATE_URL"), "OTA update URL in the runtime manifest", "run `lecodes app sync`", false);
|
|
221525
|
+
const appSrc = join21(androidDir, "app", "src", "main", "java");
|
|
221526
|
+
const kt = existsSync19(appSrc) ? readdirSync5(appSrc, { recursive: true }).map(String).filter((f5) => f5.endsWith(".kt")) : [];
|
|
221527
|
+
const boots = kt.some((f5) => readFileSync18(join21(appSrc, f5), "utf8").includes("LeCodesShell.boot"));
|
|
221528
|
+
check(boots, "App code launches via the updater (LeCodesShell.boot)", 'no app/src/main/java/**/*.kt calls LeCodesShell.boot(engine, context) — downloaded updates would never run; see "Over-the-air updates" in android/README.md for the AppViewModel snippet', false);
|
|
221529
|
+
}
|
|
221470
221530
|
const signing = resolveAndroidSigning(androidDir);
|
|
221471
221531
|
check(!!signing, "Release signing key", "`lecodes app keystore create` or `keystore use <file>` (release builds are refused without one)", false);
|
|
221472
221532
|
if (signing) {
|
|
@@ -222779,26 +222839,6 @@ var init = defineCommand({
|
|
|
222779
222839
|
throw new UsageError("Which platform? `lecodes app init <ios|android|desktop>`");
|
|
222780
222840
|
}
|
|
222781
222841
|
});
|
|
222782
|
-
var resolveUpdateUrl = (update, root, sdkVersion) => {
|
|
222783
|
-
if (update === false)
|
|
222784
|
-
return;
|
|
222785
|
-
let url = typeof update === "string" ? update : typeof update === "object" ? update.url : undefined;
|
|
222786
|
-
if (!url) {
|
|
222787
|
-
let manifest = null;
|
|
222788
|
-
try {
|
|
222789
|
-
manifest = readManifest(root);
|
|
222790
|
-
} catch {}
|
|
222791
|
-
if (!manifest) {
|
|
222792
|
-
if (update)
|
|
222793
|
-
warn(`app.json "update": true needs a server-backed project — use "update": {"url": "https://…/app.js"} for custom hosting. OTA left unconfigured.`);
|
|
222794
|
-
return;
|
|
222795
|
-
}
|
|
222796
|
-
url = `${requireApiUrl(loadConfig())}/code/${manifest.uuid}.js?sdk=${sdkVersion}`;
|
|
222797
|
-
}
|
|
222798
|
-
if (!url.startsWith("https://"))
|
|
222799
|
-
warn(`update URL is not https — iOS blocks it on device (ATS): ${url}`);
|
|
222800
|
-
return url;
|
|
222801
|
-
};
|
|
222802
222842
|
var runSync = async (platformArg) => {
|
|
222803
222843
|
const root = projectRoot();
|
|
222804
222844
|
const config = readJson2(join25(root, "app.json"));
|
|
@@ -223005,7 +223045,7 @@ var sync2 = defineCommand({
|
|
|
223005
223045
|
usage: "[ios|android]",
|
|
223006
223046
|
description: `Compiles the bundle and embeds the assets it references (ios/App/Resources/ resp. the android assets/; offline by default, also for server projects — app.json "resources": {"remote": [globs]} keeps heavy assets streaming), picks the SDK variant from the bundle's engine use, vendors the app.json plugins (pinned in plugins.lock.json) and regenerates the CLI-owned shell files. User files (ios/App/*.swift, android/app/) are never touched.
|
|
223007
223047
|
|
|
223008
|
-
Native OTA JS updates at launch (
|
|
223048
|
+
Native OTA JS updates at launch (both shells; the current launch runs the newest LOCAL bundle, a download applies on the next): on by default for a pushed project (its bundle on the platform, the launcher's own URL), app.json "update": {"url": …} for custom hosting, "update": false turns it off. A download runs only when it is of the shell's SDK major and not older than the bundle running. app.json "icon" (a square ≥1024px image) renders the app icons every sync ("iconBackground": the adaptive / flatten backdrop). No platform = every shell that exists; a dual-shell project compiles once.`,
|
|
223009
223049
|
examples: ["lecodes app sync", "lecodes app sync android"],
|
|
223010
223050
|
run: ({ args }) => runSync(args[0])
|
|
223011
223051
|
});
|
|
@@ -223256,8 +223296,14 @@ var doctorIosChecks = async (root, config, check) => {
|
|
|
223256
223296
|
const plist = readFileSync20(join25(root, "ios", "App", "Info.plist"), "utf8");
|
|
223257
223297
|
check(plist.includes("<key>LeCodesUpdateURL</key>"), "OTA update URL in Info.plist", "run `lecodes app sync`", false);
|
|
223258
223298
|
const appDir = join25(root, "ios", "App");
|
|
223259
|
-
const
|
|
223260
|
-
|
|
223299
|
+
const swift = readdirSync9(appDir).filter((f5) => f5.endsWith(".swift")).map((f5) => readFileSync20(join25(appDir, f5), "utf8"));
|
|
223300
|
+
const launchesViaUpdater = swift.some((t2) => t2.includes("LeCodesAppViewController") || t2.includes("LeCodesUpdater.activeBundle"));
|
|
223301
|
+
check(launchesViaUpdater, "App code launches via the updater", `no App/*.swift subclasses LeCodesAppViewController or calls LeCodesUpdater.activeBundle() — downloaded updates would never run; see "Over-the-air updates" in ios/README.md`, false);
|
|
223302
|
+
}
|
|
223303
|
+
{
|
|
223304
|
+
const vc = join25(root, "ios", "App", "ViewController.swift");
|
|
223305
|
+
const is2x = !existsSync23(vc) || readFileSync20(vc, "utf8").includes("LeCodesAppViewController");
|
|
223306
|
+
check(is2x, "App/ViewController.swift is the 2.x shell (LeCodesAppViewController)", "a 1.x shell's App/*.swift: replace AppDelegate.swift, SceneDelegate.swift and ViewController.swift with the ones `lecodes app init ios` writes today (your own code goes into ViewController.configure(_:))", false);
|
|
223261
223307
|
}
|
|
223262
223308
|
const asc = readJson2(join25(homedir10(), ".lecodes", "asc.json"));
|
|
223263
223309
|
check(!!asc?.keyPath, "App Store Connect API key configured", "pass --key-path/--key-id/--issuer-id to `lecodes app upload` once to store it", false);
|