lecodes-cli 2.0.13 → 2.1.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/dist/index.js +273 -212
- 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-DLHrdeSK.js +1 -0
- package/runtime/web/assets/creator-2d-Snuv_nNZ.wasm +0 -0
- package/runtime/web/assets/{creator-full-Cla1R8N6.wasm → creator-full-Bvx6gAyw.wasm} +0 -0
- package/runtime/web/assets/creator-full-CgGclZfm.js +1 -0
- package/runtime/web/assets/creator-full.js-6Drlco-f.symbols +8209 -0
- package/runtime/web/assets/creator-ui-BnClYxZP.wasm +0 -0
- package/runtime/web/assets/creator-ui-DWJK-phk.js +1 -0
- package/runtime/web/player.js +10 -10
- package/runtime/web-lite/lite.js +1 -1
- package/src/commands/app/android.ts +45 -30
- package/src/commands/app/index.ts +22 -33
- package/src/commands/app/shared.ts +56 -3
- package/src/commands/app/templates/android.ts +51 -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.
|
|
55
|
+
version: "2.1.1",
|
|
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.
|
|
79
|
+
"lecodes-headless": "^2.1.1",
|
|
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.
|
|
149
|
+
var SDK_VERSION = "2.1.1", 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) => ({
|
|
@@ -215995,8 +215995,10 @@ var describeField = (f5) => {
|
|
|
215995
215995
|
const fields = Object.entries(m.fields).filter(([k]) => !shown[name] || shown[name].includes(k));
|
|
215996
215996
|
const described = Object.fromEntries(fields.map(([k, f5]) => [k, describeField(f5)]));
|
|
215997
215997
|
for (const f5 of files) {
|
|
215998
|
-
if (f5.model
|
|
215999
|
-
|
|
215998
|
+
if (f5.model !== name)
|
|
215999
|
+
continue;
|
|
216000
|
+
const declared = db.$given[name]?.fields?.[f5.key]?.def;
|
|
216001
|
+
described[f5.key] = { kind: "file", optional: declared?.optional ?? false, unique: false, index: false, hasDefault: false, array: f5.array, isId: false, ...f5.image ? { image: f5.image } : {} };
|
|
216000
216002
|
}
|
|
216001
216003
|
models[name] = { fields: described, ...name === FILE_MODEL ? { files: true } : shown[name] ? { auth: true } : {} };
|
|
216002
216004
|
}
|
|
@@ -217173,7 +217175,6 @@ import { delimiter as delimiter3, dirname as dirname28, join as join59 } from "n
|
|
|
217173
217175
|
|
|
217174
217176
|
// src/commands/app/index.ts
|
|
217175
217177
|
init_manifest();
|
|
217176
|
-
init_config();
|
|
217177
217178
|
import { execFileSync as execFileSync6 } from "node:child_process";
|
|
217178
217179
|
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
217180
|
import { homedir as homedir10 } from "node:os";
|
|
@@ -217188,16 +217189,16 @@ import { basename as basename5, resolve as resolve5 } from "node:path";
|
|
|
217188
217189
|
// src/compile/collect.ts
|
|
217189
217190
|
init_localFiles();
|
|
217190
217191
|
init_ignore();
|
|
217191
|
-
import { readFileSync as
|
|
217192
|
+
import { readFileSync as readFileSync4 } from "node:fs";
|
|
217192
217193
|
import { basename } from "node:path";
|
|
217193
217194
|
|
|
217194
217195
|
// src/project/materialize.ts
|
|
217195
217196
|
init_api();
|
|
217196
217197
|
init_paths();
|
|
217197
|
-
import { dirname as dirname2, join as
|
|
217198
|
-
import { mkdirSync as
|
|
217198
|
+
import { dirname as dirname2, join as join4 } from "node:path";
|
|
217199
|
+
import { mkdirSync as mkdirSync2, writeFileSync as writeFileSync3 } from "node:fs";
|
|
217199
217200
|
var kindOf = (type) => type === "file" ? "resource" : type;
|
|
217200
|
-
var absOf = (root, key) =>
|
|
217201
|
+
var absOf = (root, key) => join4(root, ...key.replace(/^\//, "").split("/"));
|
|
217201
217202
|
var buildPaths = (assets) => {
|
|
217202
217203
|
const pathById = new Map;
|
|
217203
217204
|
for (const asset of assets) {
|
|
@@ -217215,20 +217216,20 @@ var materializeAssets = async (root, project, apiUrl, token, onProgress) => {
|
|
|
217215
217216
|
const key = pathById.get(asset.id);
|
|
217216
217217
|
const abs = absOf(root, key);
|
|
217217
217218
|
if (asset.type === "folder") {
|
|
217218
|
-
|
|
217219
|
+
mkdirSync2(abs, { recursive: true });
|
|
217219
217220
|
continue;
|
|
217220
217221
|
}
|
|
217221
|
-
|
|
217222
|
+
mkdirSync2(dirname2(abs), { recursive: true });
|
|
217222
217223
|
let sha;
|
|
217223
217224
|
if (asset.type === "file") {
|
|
217224
217225
|
if (!asset.file?.src)
|
|
217225
217226
|
continue;
|
|
217226
217227
|
const bytes = await downloadBlob(apiUrl, asset.file.src);
|
|
217227
|
-
|
|
217228
|
+
writeFileSync3(abs, bytes);
|
|
217228
217229
|
sha = sha256(bytes);
|
|
217229
217230
|
} else {
|
|
217230
217231
|
const text = asset.text ?? "";
|
|
217231
|
-
|
|
217232
|
+
writeFileSync3(abs, text);
|
|
217232
217233
|
sha = sha256(text);
|
|
217233
217234
|
}
|
|
217234
217235
|
files[key] = { id: asset.id, type: kindOf(asset.type), sha };
|
|
@@ -217262,7 +217263,7 @@ var collectEntries = (root, manifest, project, opts = {}) => {
|
|
|
217262
217263
|
warnings.push(`No compiled shader for ${file.path} — push it to compile the shader.`);
|
|
217263
217264
|
entries.push({ path: file.path, type, shaders, absPath: file.absPath });
|
|
217264
217265
|
} else {
|
|
217265
|
-
entries.push({ path: file.path, type, text:
|
|
217266
|
+
entries.push({ path: file.path, type, text: readFileSync4(file.absPath, "utf8") });
|
|
217266
217267
|
}
|
|
217267
217268
|
}
|
|
217268
217269
|
return { entries, warnings };
|
|
@@ -217271,7 +217272,7 @@ var collectEntries = (root, manifest, project, opts = {}) => {
|
|
|
217271
217272
|
// src/compile/collectLocal.ts
|
|
217272
217273
|
init_localFiles();
|
|
217273
217274
|
init_ignore();
|
|
217274
|
-
import { readFileSync as
|
|
217275
|
+
import { readFileSync as readFileSync5 } from "node:fs";
|
|
217275
217276
|
import { basename as basename2 } from "node:path";
|
|
217276
217277
|
var collectLocalEntries = (root, manifest, assetBase, opts = {}) => {
|
|
217277
217278
|
const entries = [];
|
|
@@ -217287,7 +217288,7 @@ var collectLocalEntries = (root, manifest, assetBase, opts = {}) => {
|
|
|
217287
217288
|
}
|
|
217288
217289
|
entries.push({ path: file.path, type, shaders: [], absPath: file.absPath });
|
|
217289
217290
|
} else {
|
|
217290
|
-
entries.push({ path: file.path, type, text:
|
|
217291
|
+
entries.push({ path: file.path, type, text: readFileSync5(file.absPath, "utf8") });
|
|
217291
217292
|
}
|
|
217292
217293
|
}
|
|
217293
217294
|
return { entries, warnings };
|
|
@@ -217301,26 +217302,26 @@ init_fonts();
|
|
|
217301
217302
|
init_compile();
|
|
217302
217303
|
import { execFile } from "node:child_process";
|
|
217303
217304
|
import { createHash as createHash4, randomBytes } from "node:crypto";
|
|
217304
|
-
import { existsSync as existsSync12, mkdirSync as
|
|
217305
|
+
import { existsSync as existsSync12, mkdirSync as mkdirSync6, readFileSync as readFileSync11, rmSync as rmSync2, statSync as statSync2, writeFileSync as writeFileSync7 } from "node:fs";
|
|
217305
217306
|
import { tmpdir } from "node:os";
|
|
217306
|
-
import { basename as basename3, join as
|
|
217307
|
+
import { basename as basename3, join as join14 } from "node:path";
|
|
217307
217308
|
|
|
217308
217309
|
// src/hosts/matcTool.ts
|
|
217309
217310
|
import { existsSync as existsSync11, readdirSync as readdirSync3 } from "node:fs";
|
|
217310
|
-
import { homedir as
|
|
217311
|
-
import { join as
|
|
217311
|
+
import { homedir as homedir3 } from "node:os";
|
|
217312
|
+
import { join as join13, resolve as resolve3 } from "node:path";
|
|
217312
217313
|
|
|
217313
217314
|
// src/hosts/releases.ts
|
|
217314
217315
|
init_cli();
|
|
217315
217316
|
import { createHash as createHash3 } from "node:crypto";
|
|
217316
|
-
import { existsSync as existsSync10, mkdirSync as
|
|
217317
|
-
import { join as
|
|
217317
|
+
import { existsSync as existsSync10, mkdirSync as mkdirSync5, mkdtempSync, readFileSync as readFileSync10, renameSync, rmSync, writeFileSync as writeFileSync6 } from "node:fs";
|
|
217318
|
+
import { join as join12 } from "node:path";
|
|
217318
217319
|
import { spawnSync as spawnSync4 } from "node:child_process";
|
|
217319
217320
|
var MANIFEST_URL = "https://storage.yandexcloud.net/le-codes/releases/versions.json";
|
|
217320
217321
|
var ARTIFACT_STAMP = ".lecodes-artifact.json";
|
|
217321
217322
|
var cachedArtifactSha = (dir) => {
|
|
217322
217323
|
try {
|
|
217323
|
-
return JSON.parse(
|
|
217324
|
+
return JSON.parse(readFileSync10(join12(dir, ARTIFACT_STAMP), "utf8")).sha256 ?? null;
|
|
217324
217325
|
} catch {
|
|
217325
217326
|
return null;
|
|
217326
217327
|
}
|
|
@@ -217366,16 +217367,16 @@ var downloadReleaseArchive = async (file, opts) => {
|
|
|
217366
217367
|
const sha = createHash3("sha256").update(bytes).digest("hex");
|
|
217367
217368
|
if (sha !== file.sha256)
|
|
217368
217369
|
throw new CliError(`Downloaded archive is corrupt (sha256 mismatch) — try again.`);
|
|
217369
|
-
|
|
217370
|
-
const stage = mkdtempSync(
|
|
217370
|
+
mkdirSync5(opts.cacheRoot, { recursive: true });
|
|
217371
|
+
const stage = mkdtempSync(join12(opts.cacheRoot, "dl-"));
|
|
217371
217372
|
try {
|
|
217372
217373
|
const archiveName = file.url.split("/").pop() ?? "archive";
|
|
217373
|
-
const archive =
|
|
217374
|
-
|
|
217374
|
+
const archive = join12(stage, archiveName);
|
|
217375
|
+
writeFileSync6(archive, bytes);
|
|
217375
217376
|
if (opts.extract) {
|
|
217376
217377
|
opts.extract(archive, stage);
|
|
217377
217378
|
} else {
|
|
217378
|
-
const winTar =
|
|
217379
|
+
const winTar = join12(process.env.SystemRoot ?? "C:\\Windows", "System32", "tar.exe");
|
|
217379
217380
|
const tarExe = process.platform === "win32" && existsSync10(winTar) ? winTar : "tar";
|
|
217380
217381
|
const tar = spawnSync4(tarExe, ["-xf", archiveName], { cwd: stage, stdio: ["ignore", "ignore", "pipe"] });
|
|
217381
217382
|
if (tar.status !== 0)
|
|
@@ -217385,7 +217386,7 @@ var downloadReleaseArchive = async (file, opts) => {
|
|
|
217385
217386
|
const bad = opts.validate(stage);
|
|
217386
217387
|
if (bad)
|
|
217387
217388
|
throw new CliError(bad);
|
|
217388
|
-
|
|
217389
|
+
writeFileSync6(join12(stage, ARTIFACT_STAMP), JSON.stringify({ sha256: file.sha256, url: file.url }, null, 2));
|
|
217389
217390
|
try {
|
|
217390
217391
|
renameSync(stage, opts.dest);
|
|
217391
217392
|
} catch {
|
|
@@ -217415,11 +217416,11 @@ var platformKey = () => {
|
|
|
217415
217416
|
return macPlatformKey();
|
|
217416
217417
|
throw new CliError(`matc isn't published for ${process.platform}.`);
|
|
217417
217418
|
};
|
|
217418
|
-
var cacheRoot = () =>
|
|
217419
|
+
var cacheRoot = () => join13(homedir3(), ".lecodes", "matc");
|
|
217419
217420
|
var findMatc = (dir) => {
|
|
217420
217421
|
const name = matcName();
|
|
217421
217422
|
for (const entry of readdirSync3(dir, { withFileTypes: true })) {
|
|
217422
|
-
const p =
|
|
217423
|
+
const p = join13(dir, entry.name);
|
|
217423
217424
|
if (entry.isFile() && entry.name === name)
|
|
217424
217425
|
return p;
|
|
217425
217426
|
if (entry.isDirectory()) {
|
|
@@ -217432,7 +217433,7 @@ var findMatc = (dir) => {
|
|
|
217432
217433
|
};
|
|
217433
217434
|
var installedMatcVersions = () => {
|
|
217434
217435
|
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(
|
|
217436
|
+
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
217437
|
} catch {
|
|
217437
217438
|
return [];
|
|
217438
217439
|
}
|
|
@@ -217462,7 +217463,7 @@ var resolveMatcExeCached = () => {
|
|
|
217462
217463
|
return resolve3(override);
|
|
217463
217464
|
}
|
|
217464
217465
|
const cached = installedMatcVersions()[0];
|
|
217465
|
-
return cached ? findMatc(
|
|
217466
|
+
return cached ? findMatc(join13(cacheRoot(), `matc-${cached}`)) : null;
|
|
217466
217467
|
};
|
|
217467
217468
|
var resolveMatcExe = async () => {
|
|
217468
217469
|
const cached = resolveMatcExeCached();
|
|
@@ -217470,7 +217471,7 @@ var resolveMatcExe = async () => {
|
|
|
217470
217471
|
return cached;
|
|
217471
217472
|
const { versions } = await fetchMatcManifest();
|
|
217472
217473
|
const { version, file } = pickMatc(versions);
|
|
217473
|
-
const dir =
|
|
217474
|
+
const dir = join13(cacheRoot(), `matc-${version}`);
|
|
217474
217475
|
await downloadMatc(version, file, dir);
|
|
217475
217476
|
const exe = findMatc(dir);
|
|
217476
217477
|
if (!exe)
|
|
@@ -217481,7 +217482,7 @@ var updateMatc = async () => {
|
|
|
217481
217482
|
const { versions } = await fetchMatcManifest();
|
|
217482
217483
|
const { version, file } = pickMatc(versions);
|
|
217483
217484
|
const before = installedMatcVersions()[0];
|
|
217484
|
-
const dir =
|
|
217485
|
+
const dir = join13(cacheRoot(), `matc-${version}`);
|
|
217485
217486
|
if (existsSync11(dir) && findMatc(dir) !== null) {
|
|
217486
217487
|
note(`Already up to date: matc ${version}.`);
|
|
217487
217488
|
return;
|
|
@@ -217494,7 +217495,7 @@ var updateMatc = async () => {
|
|
|
217494
217495
|
init_manifest();
|
|
217495
217496
|
init_cli();
|
|
217496
217497
|
var SHADERS_DIR = "shaders";
|
|
217497
|
-
var shadersCacheDir = (root) =>
|
|
217498
|
+
var shadersCacheDir = (root) => join14(root, LECODES_DIR, SHADERS_DIR);
|
|
217498
217499
|
var isShaderArtifactName = (name) => /^[0-9a-f]{16}_[a-z-]+\.filamat$/.test(name);
|
|
217499
217500
|
var matcIdentity = (exe) => {
|
|
217500
217501
|
const stat = statSync2(exe);
|
|
@@ -217504,17 +217505,17 @@ var shaderHash = (text, matcId) => createHash4("sha256").update(`${TARGET_MATRIX
|
|
|
217504
217505
|
var artifactFamily = () => [...MATC_PLATFORMS, ...LOCAL_ONLY_PLATFORMS, "webgl-lite"];
|
|
217505
217506
|
var shaderIsCompiled = (root, text, matcId) => {
|
|
217506
217507
|
const hash = shaderHash(text, matcId);
|
|
217507
|
-
return artifactFamily().every((p) => existsSync12(
|
|
217508
|
+
return artifactFamily().every((p) => existsSync12(join14(shadersCacheDir(root), `${hash}_${p}.filamat`)));
|
|
217508
217509
|
};
|
|
217509
217510
|
var shaderCompiledPlatforms = (root, text, matcId) => {
|
|
217510
217511
|
const hash = shaderHash(text, matcId);
|
|
217511
|
-
return artifactFamily().filter((p) => existsSync12(
|
|
217512
|
+
return artifactFamily().filter((p) => existsSync12(join14(shadersCacheDir(root), `${hash}_${p}.filamat`)));
|
|
217512
217513
|
};
|
|
217513
217514
|
var localShaderArtifact = (root, text, platform) => {
|
|
217514
217515
|
const matcExe = resolveMatcExeCached();
|
|
217515
217516
|
if (!matcExe)
|
|
217516
217517
|
return null;
|
|
217517
|
-
const file =
|
|
217518
|
+
const file = join14(shadersCacheDir(root), `${shaderHash(text, matcIdentity(matcExe))}_${platform}.filamat`);
|
|
217518
217519
|
return existsSync12(file) ? file : null;
|
|
217519
217520
|
};
|
|
217520
217521
|
var allShaderPlatforms = () => [...Object.keys(ALL_PLATFORM_BUILD), "webgl-lite"];
|
|
@@ -217548,25 +217549,25 @@ var applyLocalShaders = async (entries, opts) => {
|
|
|
217548
217549
|
}
|
|
217549
217550
|
const matcId = matcIdentity(matcExe);
|
|
217550
217551
|
const cacheDir = shadersCacheDir(opts.root);
|
|
217551
|
-
|
|
217552
|
+
mkdirSync6(cacheDir, { recursive: true });
|
|
217552
217553
|
const artifacts = [];
|
|
217553
217554
|
for (const entry of shaderEntries) {
|
|
217554
|
-
const text =
|
|
217555
|
+
const text = readFileSync11(entry.absPath, "utf8");
|
|
217555
217556
|
const hash = shaderHash(text, matcId);
|
|
217556
217557
|
const family = artifactFamily();
|
|
217557
217558
|
const artifact = (platform) => `${hash}_${platform}.filamat`;
|
|
217558
|
-
const has = (platform) => existsSync12(
|
|
217559
|
+
const has = (platform) => existsSync12(join14(cacheDir, artifact(platform)));
|
|
217559
217560
|
const wanted = opts.platforms ? family.filter((p) => opts.platforms.includes(p)) : family;
|
|
217560
217561
|
const missing = wanted.filter((p) => !has(p));
|
|
217561
217562
|
const matcMissing = missing.filter((p) => p !== "webgl-lite");
|
|
217562
217563
|
if (matcMissing.length > 0) {
|
|
217563
217564
|
const tempName = `lecodes-matc-${randomBytes(6).toString("hex")}.mat`;
|
|
217564
|
-
const tempFile =
|
|
217565
|
+
const tempFile = join14(tmpdir(), tempName);
|
|
217565
217566
|
try {
|
|
217566
217567
|
for (const platform of matcMissing) {
|
|
217567
217568
|
const build = ALL_PLATFORM_BUILD[platform];
|
|
217568
|
-
|
|
217569
|
-
const out =
|
|
217569
|
+
writeFileSync7(tempFile, build.replaceExternal ? text.replace(externalSamplerReg, "type:sampler2d") : text);
|
|
217570
|
+
const out = join14(cacheDir, artifact(platform));
|
|
217570
217571
|
const { ok, stderr } = await runMatc(matcExe, matcArgs(platform, out, tempFile));
|
|
217571
217572
|
if (!ok) {
|
|
217572
217573
|
rmSync2(out, { force: true });
|
|
@@ -217579,7 +217580,7 @@ ${cleanMatcOutput(stderr, tempName, basename3(entry.path))}`);
|
|
|
217579
217580
|
}
|
|
217580
217581
|
}
|
|
217581
217582
|
if (missing.includes("webgl-lite")) {
|
|
217582
|
-
|
|
217583
|
+
writeFileSync7(join14(cacheDir, artifact("webgl-lite")), JSON.stringify(buildLiteMaterialArtifact(text)));
|
|
217583
217584
|
}
|
|
217584
217585
|
if (opts.local) {
|
|
217585
217586
|
const staged = opts.platforms ? ALL_NATIVE_PLATFORMS.filter((p) => opts.platforms.includes(p)) : NATIVE_PLATFORMS;
|
|
@@ -217589,7 +217590,7 @@ ${cleanMatcOutput(stderr, tempName, basename3(entry.path))}`);
|
|
|
217589
217590
|
];
|
|
217590
217591
|
entry.shadersLocal = true;
|
|
217591
217592
|
for (const platform of staged) {
|
|
217592
|
-
artifacts.push({ name: artifact(platform), absPath:
|
|
217593
|
+
artifacts.push({ name: artifact(platform), absPath: join14(cacheDir, artifact(platform)) });
|
|
217593
217594
|
}
|
|
217594
217595
|
} else {
|
|
217595
217596
|
const order = ["opengl", "metal", "desktop", "webgl-lite"];
|
|
@@ -217605,6 +217606,7 @@ init_ignore();
|
|
|
217605
217606
|
|
|
217606
217607
|
// src/commands/app/shared.ts
|
|
217607
217608
|
init_manifest();
|
|
217609
|
+
init_config();
|
|
217608
217610
|
init_ignore();
|
|
217609
217611
|
init_cli();
|
|
217610
217612
|
import { execFileSync } from "node:child_process";
|
|
@@ -217812,6 +217814,26 @@ var desktopDefaultViewport = (root) => {
|
|
|
217812
217814
|
};
|
|
217813
217815
|
var swiftPackageName = (url) => (url.replace(/\/+$/, "").split("/").pop() ?? url).replace(/\.git$/, "");
|
|
217814
217816
|
var isLocalPluginSpec = (spec) => spec.startsWith("./") || spec.startsWith("../") || spec.startsWith("/") || spec.startsWith("file:");
|
|
217817
|
+
var resolveUpdateUrl = (update, root, sdkVersion) => {
|
|
217818
|
+
if (update === false)
|
|
217819
|
+
return;
|
|
217820
|
+
let url = typeof update === "string" ? update : typeof update === "object" ? update.url : undefined;
|
|
217821
|
+
if (!url) {
|
|
217822
|
+
let manifest = null;
|
|
217823
|
+
try {
|
|
217824
|
+
manifest = readManifest(root);
|
|
217825
|
+
} catch {}
|
|
217826
|
+
if (!manifest) {
|
|
217827
|
+
if (update)
|
|
217828
|
+
warn(`app.json "update": true needs a server-backed project — use "update": {"url": "https://…/app.js"} for custom hosting. OTA left unconfigured.`);
|
|
217829
|
+
return;
|
|
217830
|
+
}
|
|
217831
|
+
url = `${requireApiUrl(loadConfig())}/code/${manifest.uuid}.js?sdk=${sdkVersion}`;
|
|
217832
|
+
}
|
|
217833
|
+
if (!url.startsWith("https://"))
|
|
217834
|
+
warn(`update URL is not https — the phones block it on device (ATS / cleartext policy): ${url}`);
|
|
217835
|
+
return url;
|
|
217836
|
+
};
|
|
217815
217837
|
var sdkSatisfies = (version, range) => {
|
|
217816
217838
|
const op = range.startsWith("^") ? "^" : range.startsWith("~") ? "~" : "";
|
|
217817
217839
|
const want = range.slice(op.length);
|
|
@@ -217901,6 +217923,15 @@ var resolveVariant = (requested, bundleJs, configKey = "ios.variant") => {
|
|
|
217901
217923
|
}
|
|
217902
217924
|
return { variant, detected };
|
|
217903
217925
|
};
|
|
217926
|
+
var ANDROID_SDK_VARIANTS = [...SDK_VARIANTS, "vulkan"];
|
|
217927
|
+
var ANDROID_SDK_VULKAN_MIN = "2.1.0";
|
|
217928
|
+
var androidSdkArtifact = (variant, sdkVersion) => {
|
|
217929
|
+
if (variant === "vulkan" && sdkVersion && cmpVersion(sdkVersion, ANDROID_SDK_VULKAN_MIN) < 0) {
|
|
217930
|
+
throw new CliError(`android.variant "vulkan" needs lecodes-android-sdk >= ${ANDROID_SDK_VULKAN_MIN} (pinned: ${sdkVersion}) — run \`lecodes app update\` or set android.sdk.`);
|
|
217931
|
+
}
|
|
217932
|
+
return variant === "full" ? "lecodes-sdk" : `lecodes-sdk-${variant}`;
|
|
217933
|
+
};
|
|
217934
|
+
var androidVariantEngines = (variant) => variant === "vulkan" ? "full" : variant;
|
|
217904
217935
|
var VERSION_RE = /^\d+\.\d+\.\d+$/;
|
|
217905
217936
|
var cmpVersion = (a, b) => {
|
|
217906
217937
|
const pa = a.split(".").map(Number);
|
|
@@ -219833,14 +219864,14 @@ android {
|
|
|
219833
219864
|
minSdk = ${MIN_SDK}
|
|
219834
219865
|
}
|
|
219835
219866
|
compileOptions {
|
|
219836
|
-
sourceCompatibility = JavaVersion.
|
|
219837
|
-
targetCompatibility = JavaVersion.
|
|
219867
|
+
sourceCompatibility = JavaVersion.VERSION_17
|
|
219868
|
+
targetCompatibility = JavaVersion.VERSION_17
|
|
219838
219869
|
}
|
|
219839
219870
|
}
|
|
219840
219871
|
|
|
219841
219872
|
kotlin {
|
|
219842
219873
|
compilerOptions {
|
|
219843
|
-
jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.
|
|
219874
|
+
jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17)
|
|
219844
219875
|
}
|
|
219845
219876
|
}
|
|
219846
219877
|
|
|
@@ -219853,6 +219884,15 @@ dependencies {
|
|
|
219853
219884
|
}
|
|
219854
219885
|
`;
|
|
219855
219886
|
};
|
|
219887
|
+
var runtimeManifestTemplate = (updateUrl) => `<?xml version="1.0" encoding="utf-8"?>
|
|
219888
|
+
<!-- Generated by \`lecodes app sync\` — regenerated on every sync, do not edit. -->
|
|
219889
|
+
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
|
219890
|
+
<application>${updateUrl ? `
|
|
219891
|
+
<!-- lecodes:update — native OTA source (app.json "update"); LeCodesShell.boot checks it at launch -->
|
|
219892
|
+
<meta-data android:name="io.letary.lecodes.UPDATE_URL" android:value="${updateUrl.replace(/&/g, "&")}" />` : ""}
|
|
219893
|
+
</application>
|
|
219894
|
+
</manifest>
|
|
219895
|
+
`;
|
|
219856
219896
|
var runtimeKotlinTemplate = (plugins, projectUuid) => {
|
|
219857
219897
|
const identity = projectUuid ? ` engine.bootProjectUuid = "${projectUuid}"
|
|
219858
219898
|
` : "";
|
|
@@ -219915,14 +219955,14 @@ android {
|
|
|
219915
219955
|
minSdk = ${MIN_SDK}${consumer}
|
|
219916
219956
|
}
|
|
219917
219957
|
compileOptions {
|
|
219918
|
-
sourceCompatibility = JavaVersion.
|
|
219919
|
-
targetCompatibility = JavaVersion.
|
|
219958
|
+
sourceCompatibility = JavaVersion.VERSION_17
|
|
219959
|
+
targetCompatibility = JavaVersion.VERSION_17
|
|
219920
219960
|
}
|
|
219921
219961
|
}
|
|
219922
219962
|
|
|
219923
219963
|
kotlin {
|
|
219924
219964
|
compilerOptions {
|
|
219925
|
-
jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.
|
|
219965
|
+
jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17)
|
|
219926
219966
|
}
|
|
219927
219967
|
}
|
|
219928
219968
|
|
|
@@ -219976,6 +220016,10 @@ engines the bundle uses and cannot see those, so a project using \`Physics\`,
|
|
|
219976
220016
|
\`CharacterController\`, \`NavMesh\` or networking must set \`android.variant: "full"\` in
|
|
219977
220017
|
\`../app.json\` — otherwise those aspects silently do nothing on device.
|
|
219978
220018
|
|
|
220019
|
+
\`android.variant: "vulkan"\` links \`lecodes-sdk-vulkan\`: the full engine set with Filament on
|
|
220020
|
+
Vulkan (OpenGL ES where the device has no Vulkan 1.1), \`arm64-v8a\` only — for 3D games. Never
|
|
220021
|
+
auto-selected; needs lecodes-android-sdk >= 2.1.0.
|
|
220022
|
+
|
|
219979
220023
|
## App-local native code
|
|
219980
220024
|
|
|
219981
220025
|
Drop Kotlin files into \`app/src/main/java/\` and register them in \`MainActivity\` after the
|
|
@@ -220036,7 +220080,22 @@ the init-time placeholder.
|
|
|
220036
220080
|
|
|
220037
220081
|
## Notes
|
|
220038
220082
|
|
|
220039
|
-
|
|
220083
|
+
|
|
220084
|
+
## Over-the-air updates
|
|
220085
|
+
|
|
220086
|
+
Native, before the JS engine: \`LeCodesShell.boot(engine, context)\` in \`AppViewModel\` asks the
|
|
220087
|
+
source for newer code (a conditional GET; offline is a no-op), stores it and runs it on the NEXT
|
|
220088
|
+
launch — the current launch runs the newest LOCAL bundle. Configured in \`../app.json\`:
|
|
220089
|
+
\`"update": true\` = the project's live bundle on the platform (a pushed project), \`"update":
|
|
220090
|
+
{ "url": "https://…/app.js" }\` = your own hosting, \`false\` = off. Sync writes the URL into
|
|
220091
|
+
\`lecodes-runtime/src/main/AndroidManifest.xml\` (meta-data \`io.letary.lecodes.UPDATE_URL\`). A
|
|
220092
|
+
download runs only when it is of this shell's SDK major and not older than the bundle running; a
|
|
220093
|
+
store update always wins over a downloaded bundle. A shell generated before OTA existed reads
|
|
220094
|
+
\`assets/app.js\` itself — replace that with:
|
|
220095
|
+
|
|
220096
|
+
\`\`\`kotlin
|
|
220097
|
+
if (!LeCodesShell.boot(engine, getApplication<Application>())) Log.e("LeCodes", "bundled app.js is missing — run \`lecodes app sync\`")
|
|
220098
|
+
\`\`\`
|
|
220040
220099
|
- Push: the notification service needs \`app/google-services.json\` from the le.codes Firebase
|
|
220041
220100
|
project; \`lecodes app sync\` applies the google-services Gradle plugin only when that file
|
|
220042
220101
|
exists (and warns otherwise).
|
|
@@ -220096,14 +220155,14 @@ android {
|
|
|
220096
220155
|
}
|
|
220097
220156
|
}
|
|
220098
220157
|
compileOptions {
|
|
220099
|
-
sourceCompatibility = JavaVersion.
|
|
220100
|
-
targetCompatibility = JavaVersion.
|
|
220158
|
+
sourceCompatibility = JavaVersion.VERSION_17
|
|
220159
|
+
targetCompatibility = JavaVersion.VERSION_17
|
|
220101
220160
|
}
|
|
220102
220161
|
}
|
|
220103
220162
|
|
|
220104
220163
|
kotlin {
|
|
220105
220164
|
compilerOptions {
|
|
220106
|
-
jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.
|
|
220165
|
+
jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17)
|
|
220107
220166
|
}
|
|
220108
220167
|
}
|
|
220109
220168
|
|
|
@@ -220243,6 +220302,7 @@ package ${pkg}
|
|
|
220243
220302
|
import android.app.Activity
|
|
220244
220303
|
import android.app.Application
|
|
220245
220304
|
import androidx.lifecycle.AndroidViewModel
|
|
220305
|
+
import io.letary.lecodes.LeCodesShell
|
|
220246
220306
|
import io.letary.lecodes.LecodesEngine
|
|
220247
220307
|
|
|
220248
220308
|
class AppViewModel(application: Application) : AndroidViewModel(application) {
|
|
@@ -220266,8 +220326,14 @@ class AppViewModel(application: Application) : AndroidViewModel(application) {
|
|
|
220266
220326
|
fun onActivityCreate(activity: Activity) {
|
|
220267
220327
|
engine.attach(activity)
|
|
220268
220328
|
if (!isCreated) {
|
|
220269
|
-
|
|
220270
|
-
|
|
220329
|
+
// OTA (native, no JS involved): LeCodesShell.boot starts the check FIRST — even a bundle
|
|
220330
|
+
// that crashes on boot can never block its own fix — then runs the newest local bundle
|
|
220331
|
+
// (a downloaded update, or the embedded assets/app.js). New code applies on the next
|
|
220332
|
+
// launch. Configured via app.json "update" (→ the runtime module's manifest meta-data);
|
|
220333
|
+
// offline: no-op.
|
|
220334
|
+
if (!LeCodesShell.boot(engine, getApplication<Application>())) {
|
|
220335
|
+
android.util.Log.e("LeCodes", "bundled app.js is missing — run \`lecodes app sync\`")
|
|
220336
|
+
}
|
|
220271
220337
|
isCreated = true
|
|
220272
220338
|
}
|
|
220273
220339
|
}
|
|
@@ -220667,9 +220733,8 @@ if "%OS%"=="Windows_NT" endlocal
|
|
|
220667
220733
|
|
|
220668
220734
|
// src/commands/app/android.ts
|
|
220669
220735
|
var ANDROID_SDK_REPO = "letary/lecodes-android-sdk";
|
|
220670
|
-
var ANDROID_SDK_MIN_VERSION = "
|
|
220736
|
+
var ANDROID_SDK_MIN_VERSION = "2.0.13";
|
|
220671
220737
|
var ANDROID_SDK_VARIANTS_MIN = "1.3.0";
|
|
220672
|
-
var variantArtifact = (variant) => variant === "full" ? "lecodes-sdk" : `lecodes-sdk-${variant}`;
|
|
220673
220738
|
var ANDROID_SDK_URL_DEFAULT = `https://github.com/${ANDROID_SDK_REPO}/releases/download`;
|
|
220674
220739
|
var sdkRepoUrl = () => process.env.LECODES_ANDROID_SDK_URL ?? ANDROID_SDK_URL_DEFAULT;
|
|
220675
220740
|
var ANDROID_SDK_OPTS = { repo: ANDROID_SDK_REPO, minVersion: ANDROID_SDK_MIN_VERSION, configKey: "android.sdk" };
|
|
@@ -220711,8 +220776,8 @@ var initAndroid = async (opts) => {
|
|
|
220711
220776
|
}
|
|
220712
220777
|
const existing = readJson2(join21(root, "app.json")) ?? {};
|
|
220713
220778
|
const variant = opts.variant ?? existing.android?.variant ?? "auto";
|
|
220714
|
-
if (variant !== "auto" && !
|
|
220715
|
-
throw new CliError(`--variant must be auto, ${
|
|
220779
|
+
if (variant !== "auto" && !ANDROID_SDK_VARIANTS.includes(variant)) {
|
|
220780
|
+
throw new CliError(`--variant must be auto, ${ANDROID_SDK_VARIANTS.join(", ")} (got "${variant}")`);
|
|
220716
220781
|
}
|
|
220717
220782
|
const sdkFlag = opts.sdk ?? existing.android?.sdk;
|
|
220718
220783
|
const sdkVersion = await resolveSdkVersion(process.env.LECODES_ANDROID_SDK ?? sdkFlag ?? "latest", null, ANDROID_SDK_OPTS);
|
|
@@ -220773,7 +220838,7 @@ var initAndroid = async (opts) => {
|
|
|
220773
220838
|
write("app/src/main/res/values/strings.xml", stringsXmlTemplate(name));
|
|
220774
220839
|
write("app/src/main/res/drawable/ic_launcher.xml", IC_LAUNCHER_XML);
|
|
220775
220840
|
const initVariant = variant === "auto" ? "core" : variant;
|
|
220776
|
-
const initArtifact = cmpVersion(sdkVersion, ANDROID_SDK_VARIANTS_MIN) >= 0 ?
|
|
220841
|
+
const initArtifact = cmpVersion(sdkVersion, ANDROID_SDK_VARIANTS_MIN) >= 0 ? androidSdkArtifact(initVariant, sdkVersion) : "lecodes-sdk";
|
|
220777
220842
|
write("lecodes-runtime/build.gradle.kts", runtimeBuildGradleTemplate(sdkVersion, [], initArtifact));
|
|
220778
220843
|
write("lecodes-runtime/src/main/kotlin/io/letary/lecodes/runtime/LeCodesRuntime.kt", runtimeKotlinTemplate([]));
|
|
220779
220844
|
log("");
|
|
@@ -220817,11 +220882,12 @@ var syncAndroid = async (root, config, prior) => {
|
|
|
220817
220882
|
bundleJs = readFileSync18(join21(assetsDir, "app.js"), "utf8");
|
|
220818
220883
|
}
|
|
220819
220884
|
const requestedVariant = config.android?.variant ?? "auto";
|
|
220820
|
-
if (requestedVariant !== "auto" && !
|
|
220821
|
-
throw new CliError(`app.json android.variant must be "auto" or one of ${
|
|
220885
|
+
if (requestedVariant !== "auto" && !ANDROID_SDK_VARIANTS.includes(requestedVariant)) {
|
|
220886
|
+
throw new CliError(`app.json android.variant must be "auto" or one of ${ANDROID_SDK_VARIANTS.join(", ")} (got "${requestedVariant}").`);
|
|
220822
220887
|
}
|
|
220823
|
-
const { variant, detected } = resolveVariant(requestedVariant, bundleJs, "android.variant");
|
|
220824
|
-
|
|
220888
|
+
const { variant: covered, detected } = resolveVariant(requestedVariant === "auto" ? "auto" : androidVariantEngines(requestedVariant), bundleJs, "android.variant");
|
|
220889
|
+
const variant = requestedVariant === "vulkan" ? "vulkan" : covered;
|
|
220890
|
+
let sdkArtifact = androidSdkArtifact(variant, sdkVersion);
|
|
220825
220891
|
if (sdkArtifact !== "lecodes-sdk" && cmpVersion(sdkVersion, ANDROID_SDK_VARIANTS_MIN) < 0) {
|
|
220826
220892
|
warn(`lecodes-android-sdk ${sdkVersion} predates the variant matrix (${ANDROID_SDK_VARIANTS_MIN}) — linking the full lecodes-sdk AAR; run \`lecodes app update\` to pick up variant builds.`);
|
|
220827
220893
|
sdkArtifact = "lecodes-sdk";
|
|
@@ -220931,6 +220997,10 @@ var syncAndroid = async (root, config, prior) => {
|
|
|
220931
220997
|
const runtimeKt = join21(androidDir, "lecodes-runtime", "src", "main", "kotlin", "io", "letary", "lecodes", "runtime", "LeCodesRuntime.kt");
|
|
220932
220998
|
mkdirSync11(dirname12(runtimeKt), { recursive: true });
|
|
220933
220999
|
writeFileSync14(runtimeKt, runtimeKotlinTemplate(runtimePlugins, shellUuid));
|
|
221000
|
+
const updateUrl = resolveUpdateUrl(config.update, root, sdkVersion);
|
|
221001
|
+
writeFileSync14(join21(androidDir, "lecodes-runtime", "src", "main", "AndroidManifest.xml"), runtimeManifestTemplate(updateUrl));
|
|
221002
|
+
if (updateUrl)
|
|
221003
|
+
log(`${c.green("update")} OTA source ${updateUrl} — checked natively at launch (LeCodesShell.boot), new code applies next launch`);
|
|
220934
221004
|
if (pluginSpecs.length > 0 || existsSync19(lockPath))
|
|
220935
221005
|
writeJson(lockPath, { plugins: newLock });
|
|
220936
221006
|
restampAppBuildGradle(androidDir, config, gradlePlugins);
|
|
@@ -220938,9 +221008,6 @@ var syncAndroid = async (root, config, prior) => {
|
|
|
220938
221008
|
await writeAndroidIcons(androidDir, await loadIconMaster(root, config));
|
|
220939
221009
|
restampManifestIcon(androidDir);
|
|
220940
221010
|
}
|
|
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
221011
|
log("");
|
|
220945
221012
|
success(`Android shell synced: app.js + ${runtimePlugins.length} plugin(s), SDK ${sdkVersion}, v${config.version ?? "1.0.0"} (${config.android?.versionCode ?? 1}).`);
|
|
220946
221013
|
note("Build & run from Android Studio (`lecodes app open android`) — user files in android/app/ were not touched.");
|
|
@@ -221450,8 +221517,8 @@ var doctorAndroidChecks = async (root, config, check) => {
|
|
|
221450
221517
|
const hasSdkHome = !!(process.env.ANDROID_HOME ?? process.env.ANDROID_SDK_ROOT) || existsSync19(join21(androidDir, "local.properties"));
|
|
221451
221518
|
check(hasSdkHome, "Android SDK location", "set ANDROID_HOME or create android/local.properties");
|
|
221452
221519
|
const requestedVariant = config.android?.variant ?? "auto";
|
|
221453
|
-
const validVariant = requestedVariant === "auto" ||
|
|
221454
|
-
check(validVariant, `android.variant "${requestedVariant}"`, `use "auto" or one of ${
|
|
221520
|
+
const validVariant = requestedVariant === "auto" || ANDROID_SDK_VARIANTS.includes(requestedVariant);
|
|
221521
|
+
check(validVariant, `android.variant "${requestedVariant}"`, `use "auto" or one of ${ANDROID_SDK_VARIANTS.join(", ")}`);
|
|
221455
221522
|
const wrapperJar = join21(androidDir, "gradle", "wrapper", "gradle-wrapper.jar");
|
|
221456
221523
|
check(existsSync19(wrapperJar) && statSync5(wrapperJar).size > 0, "Gradle wrapper jar", `run \`gradle wrapper --gradle-version ${GRADLE_VERSION}\` in android/ (or let Android Studio repair it)`);
|
|
221457
221524
|
check(existsSync19(join21(androidDir, "app", "src", "main", "assets", "app.js")), "Bundled app.js", "run `lecodes app sync`");
|
|
@@ -221467,6 +221534,14 @@ var doctorAndroidChecks = async (root, config, check) => {
|
|
|
221467
221534
|
check(cmpVersion(linked, latest) >= 0, `SDK is the latest release (${latest})`, "run `lecodes app update`", false);
|
|
221468
221535
|
} catch {}
|
|
221469
221536
|
}
|
|
221537
|
+
if (config.update) {
|
|
221538
|
+
const manifest = join21(androidDir, "lecodes-runtime", "src", "main", "AndroidManifest.xml");
|
|
221539
|
+
check(existsSync19(manifest) && readFileSync18(manifest, "utf8").includes("io.letary.lecodes.UPDATE_URL"), "OTA update URL in the runtime manifest", "run `lecodes app sync`", false);
|
|
221540
|
+
const appSrc = join21(androidDir, "app", "src", "main", "java");
|
|
221541
|
+
const kt = existsSync19(appSrc) ? readdirSync5(appSrc, { recursive: true }).map(String).filter((f5) => f5.endsWith(".kt")) : [];
|
|
221542
|
+
const boots = kt.some((f5) => readFileSync18(join21(appSrc, f5), "utf8").includes("LeCodesShell.boot"));
|
|
221543
|
+
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);
|
|
221544
|
+
}
|
|
221470
221545
|
const signing = resolveAndroidSigning(androidDir);
|
|
221471
221546
|
check(!!signing, "Release signing key", "`lecodes app keystore create` or `keystore use <file>` (release builds are refused without one)", false);
|
|
221472
221547
|
if (signing) {
|
|
@@ -222761,7 +222836,7 @@ var init = defineCommand({
|
|
|
222761
222836
|
"bundle-id": str("iOS bundle identifier (default com.example.<slug>)", { value: "<id>" }),
|
|
222762
222837
|
"app-id": str("Android applicationId (default com.example.<slug>)", { value: "<id>" }),
|
|
222763
222838
|
team: str("Apple development team id", { value: "<id>" }),
|
|
222764
|
-
variant: str("SDK variant: auto (default — sync picks the smallest of core/2d/3d/full covering what the bundle uses), or one of those to pin", { value: "<v>" }),
|
|
222839
|
+
variant: str("SDK variant: auto (default — sync picks the smallest of core/2d/3d/full covering what the bundle uses), or one of those to pin; Android also vulkan (full on Vulkan, arm64 only)", { value: "<v>" }),
|
|
222765
222840
|
sdk: str("native SDK release (default: the newest one, pinned in app.json); 'latest' floats it — every sync re-resolves", { value: "<v|latest>" }),
|
|
222766
222841
|
fullscreen: bool("(desktop) borderless fullscreen on the primary monitor — kiosks")
|
|
222767
222842
|
},
|
|
@@ -222779,26 +222854,6 @@ var init = defineCommand({
|
|
|
222779
222854
|
throw new UsageError("Which platform? `lecodes app init <ios|android|desktop>`");
|
|
222780
222855
|
}
|
|
222781
222856
|
});
|
|
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
222857
|
var runSync = async (platformArg) => {
|
|
222803
222858
|
const root = projectRoot();
|
|
222804
222859
|
const config = readJson2(join25(root, "app.json"));
|
|
@@ -223005,7 +223060,7 @@ var sync2 = defineCommand({
|
|
|
223005
223060
|
usage: "[ios|android]",
|
|
223006
223061
|
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
223062
|
|
|
223008
|
-
Native OTA JS updates at launch (
|
|
223063
|
+
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
223064
|
examples: ["lecodes app sync", "lecodes app sync android"],
|
|
223010
223065
|
run: ({ args }) => runSync(args[0])
|
|
223011
223066
|
});
|
|
@@ -223256,8 +223311,14 @@ var doctorIosChecks = async (root, config, check) => {
|
|
|
223256
223311
|
const plist = readFileSync20(join25(root, "ios", "App", "Info.plist"), "utf8");
|
|
223257
223312
|
check(plist.includes("<key>LeCodesUpdateURL</key>"), "OTA update URL in Info.plist", "run `lecodes app sync`", false);
|
|
223258
223313
|
const appDir = join25(root, "ios", "App");
|
|
223259
|
-
const
|
|
223260
|
-
|
|
223314
|
+
const swift = readdirSync9(appDir).filter((f5) => f5.endsWith(".swift")).map((f5) => readFileSync20(join25(appDir, f5), "utf8"));
|
|
223315
|
+
const launchesViaUpdater = swift.some((t2) => t2.includes("LeCodesAppViewController") || t2.includes("LeCodesUpdater.activeBundle"));
|
|
223316
|
+
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);
|
|
223317
|
+
}
|
|
223318
|
+
{
|
|
223319
|
+
const vc = join25(root, "ios", "App", "ViewController.swift");
|
|
223320
|
+
const is2x = !existsSync23(vc) || readFileSync20(vc, "utf8").includes("LeCodesAppViewController");
|
|
223321
|
+
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
223322
|
}
|
|
223262
223323
|
const asc = readJson2(join25(homedir10(), ".lecodes", "asc.json"));
|
|
223263
223324
|
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);
|