@anvil-works/anvil-cli 0.7.6 → 0.7.7

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/cli.js CHANGED
@@ -14388,274 +14388,6 @@ Promise.resolve(executeGitCredentialOperation(process.argv[2] || "get", {
14388
14388
  return true;
14389
14389
  }
14390
14390
  }
14391
- const ANVIL_DEPS_DIR = ".anvil/deps";
14392
- const ANVIL_DEPS_MANIFEST = "manifest.json";
14393
- const DEPS_MANIFEST_VERSION = 1;
14394
- function formatFetchError(prefix, status, errorBody) {
14395
- if (!errorBody) return `${prefix}: ${status}`;
14396
- try {
14397
- const parsed = JSON.parse(errorBody);
14398
- if ("string" == typeof parsed.error && parsed.error.trim()) return `${prefix}: ${parsed.error} (${status})`;
14399
- } catch {}
14400
- return `${prefix}: ${status} ${errorBody}`;
14401
- }
14402
- async function fetchAllDependencies(appId, anvilUrl = resolveAnvilUrl(), options = {}) {
14403
- const { commit, includeDocs = true, knownDependencies = [] } = options;
14404
- const token = await auth_getValidAuthToken(anvilUrl, options.username);
14405
- const body = {
14406
- include_docs: includeDocs,
14407
- known_dependencies: knownDependencies
14408
- };
14409
- if (commit) body.commit = commit;
14410
- const resp = await fetch(`${anvilUrl}/ide/api/_/apps/${appId}/resolved-dependencies`, {
14411
- method: "POST",
14412
- headers: {
14413
- Authorization: `Bearer ${token}`,
14414
- "Content-Type": "application/json"
14415
- },
14416
- body: JSON.stringify(body)
14417
- });
14418
- if (!resp.ok) {
14419
- const errorBody = await resp.text();
14420
- throw new Error(formatFetchError("Failed to fetch dependencies", resp.status, errorBody));
14421
- }
14422
- return await resp.json();
14423
- }
14424
- async function refreshDependencyCache(projectRoot, appId, anvilUrl = resolveAnvilUrl(), options = {}) {
14425
- const includeDocs = options.includeDocs ?? true;
14426
- const knownDependencies = await getKnownDependenciesForCache(projectRoot, includeDocs);
14427
- const response = await fetchAllDependencies(appId, anvilUrl, {
14428
- username: options.username,
14429
- commit: options.commit,
14430
- includeDocs,
14431
- knownDependencies
14432
- });
14433
- if (!response.resolved_dependencies.length) {
14434
- await clearDepsCache(projectRoot);
14435
- return {
14436
- response,
14437
- addedGitignoreEntry: false
14438
- };
14439
- }
14440
- const addedGitignoreEntry = await ensureProjectGitignoreIgnoresAnvilDir(projectRoot);
14441
- await writeAllDepsToCache(projectRoot, response, {
14442
- includeDocs
14443
- });
14444
- return {
14445
- response,
14446
- addedGitignoreEntry
14447
- };
14448
- }
14449
- function depsDir(projectRoot) {
14450
- return external_path_default().join(projectRoot, ANVIL_DEPS_DIR);
14451
- }
14452
- function depsManifestPath(projectRoot) {
14453
- return external_path_default().join(depsDir(projectRoot), ANVIL_DEPS_MANIFEST);
14454
- }
14455
- function isResolvedDependencyManifestEntry(value) {
14456
- if (!value || "object" != typeof value) return false;
14457
- const entry = value;
14458
- return "string" == typeof entry.app_id && entry.app_id.length > 0 && "string" == typeof entry.commit_id && entry.commit_id.length > 0 && (void 0 === entry.branch || "string" == typeof entry.branch);
14459
- }
14460
- function isCachedDependencyManifestEntry(value) {
14461
- if (!isResolvedDependencyManifestEntry(value)) return false;
14462
- const entry = value;
14463
- return "string" == typeof entry.package_name && entry.package_name.length > 0;
14464
- }
14465
- function isDepsCacheManifest(value) {
14466
- if (!value || "object" != typeof value) return false;
14467
- const manifest = value;
14468
- return manifest.version === DEPS_MANIFEST_VERSION && "boolean" == typeof manifest.includeDocs && Array.isArray(manifest.resolved_dependencies) && manifest.resolved_dependencies.every(isResolvedDependencyManifestEntry) && Array.isArray(manifest.cached_dependencies) && manifest.cached_dependencies.every(isCachedDependencyManifestEntry);
14469
- }
14470
- async function readDepsCacheManifest(projectRoot) {
14471
- try {
14472
- const raw = await promises_default().readFile(depsManifestPath(projectRoot), "utf-8");
14473
- const parsed = JSON.parse(raw);
14474
- return isDepsCacheManifest(parsed) ? parsed : void 0;
14475
- } catch {
14476
- return;
14477
- }
14478
- }
14479
- async function getKnownDependenciesForCache(projectRoot, includeDocs) {
14480
- const manifest = await readDepsCacheManifest(projectRoot);
14481
- if (!manifest || manifest.includeDocs !== includeDocs) return [];
14482
- return manifest.resolved_dependencies.map(({ app_id, commit_id })=>({
14483
- app_id,
14484
- commit_id
14485
- }));
14486
- }
14487
- function getDependencyWatchSubscriptionsFromManifest(manifest) {
14488
- if (!manifest) return [];
14489
- const cachedByAppId = new Map(manifest.cached_dependencies.map((dep)=>[
14490
- dep.app_id,
14491
- dep
14492
- ]));
14493
- return manifest.resolved_dependencies.flatMap((dep)=>{
14494
- if (!dep.branch) return [];
14495
- return [
14496
- {
14497
- appId: dep.app_id,
14498
- branch: dep.branch,
14499
- packageName: cachedByAppId.get(dep.app_id)?.package_name
14500
- }
14501
- ];
14502
- });
14503
- }
14504
- function normalizeAnvilYamlDependenciesFromContent(content) {
14505
- const parsed = external_js_yaml_default().load(content);
14506
- if (!parsed || "object" != typeof parsed || !Array.isArray(parsed.dependencies)) return [];
14507
- return normalizeAnvilYamlDependenciesValue(parsed.dependencies);
14508
- }
14509
- function normalizeAnvilYamlDependenciesValue(value) {
14510
- if (!Array.isArray(value)) return [];
14511
- return normalizeForComparison(value);
14512
- }
14513
- async function readAnvilYamlDependencies(projectRoot) {
14514
- try {
14515
- const content = await promises_default().readFile(external_path_default().join(projectRoot, "anvil.yaml"), "utf-8");
14516
- return normalizeAnvilYamlDependenciesFromContent(content);
14517
- } catch {
14518
- return [];
14519
- }
14520
- }
14521
- function anvilYamlDependenciesEqual(left, right) {
14522
- return deepEqual(left, right);
14523
- }
14524
- function normalizeForComparison(value) {
14525
- if (Array.isArray(value)) return value.map(normalizeForComparison);
14526
- if (value && "object" == typeof value) return Object.fromEntries(Object.entries(value).sort(([left], [right])=>left.localeCompare(right)).map(([key, entryValue])=>[
14527
- key,
14528
- normalizeForComparison(entryValue)
14529
- ]));
14530
- return value;
14531
- }
14532
- function cachedManifestEntry(dep, packageName) {
14533
- return {
14534
- app_id: dep.app_id,
14535
- commit_id: dep.commit_id,
14536
- ...dep.branch ? {
14537
- branch: dep.branch
14538
- } : {},
14539
- package_name: packageName
14540
- };
14541
- }
14542
- function safeDependencyPath(filePath, kind) {
14543
- const normalizedPath = external_path_default().normalize(filePath);
14544
- if (external_path_default().isAbsolute(normalizedPath) || normalizedPath.startsWith("..")) throw new Error(`Invalid dependency ${kind} path: ${filePath}`);
14545
- return normalizedPath;
14546
- }
14547
- async function writeDepToCache(projectRoot, dep) {
14548
- const depDir = external_path_default().join(projectRoot, ANVIL_DEPS_DIR, dep.package_name);
14549
- await promises_default().rm(depDir, {
14550
- recursive: true,
14551
- force: true
14552
- });
14553
- await promises_default().mkdir(external_path_default().join(depDir, "client_code"), {
14554
- recursive: true
14555
- });
14556
- await promises_default().mkdir(external_path_default().join(depDir, "server_code"), {
14557
- recursive: true
14558
- });
14559
- await promises_default().writeFile(external_path_default().join(depDir, "anvil.yaml"), external_js_yaml_default().dump({
14560
- package_name: dep.package_name
14561
- }));
14562
- for (const mod of dep.modules ?? [])if (mod.is_package) {
14563
- const modDir = external_path_default().join(depDir, "client_code", mod.name);
14564
- await promises_default().mkdir(modDir, {
14565
- recursive: true
14566
- });
14567
- await promises_default().writeFile(external_path_default().join(modDir, "__init__.py"), mod.code);
14568
- } else await promises_default().writeFile(external_path_default().join(depDir, "client_code", `${mod.name}.py`), mod.code);
14569
- for (const form of dep.forms ?? []){
14570
- const { code, class_name, is_package, ...formTemplate } = form;
14571
- if (is_package) {
14572
- const formDir = external_path_default().join(depDir, "client_code", class_name);
14573
- await promises_default().mkdir(formDir, {
14574
- recursive: true
14575
- });
14576
- await promises_default().writeFile(external_path_default().join(formDir, "__init__.py"), code);
14577
- await promises_default().writeFile(external_path_default().join(formDir, "form_template.yaml"), external_js_yaml_default().dump(formTemplate));
14578
- } else {
14579
- await promises_default().writeFile(external_path_default().join(depDir, "client_code", `${class_name}.py`), code);
14580
- await promises_default().writeFile(external_path_default().join(depDir, "client_code", `${class_name}.yaml`), external_js_yaml_default().dump(formTemplate));
14581
- }
14582
- }
14583
- for (const mod of dep.server_modules ?? [])if (mod.is_package) {
14584
- const modDir = external_path_default().join(depDir, "server_code", mod.name);
14585
- await promises_default().mkdir(modDir, {
14586
- recursive: true
14587
- });
14588
- await promises_default().writeFile(external_path_default().join(modDir, "__init__.py"), mod.code);
14589
- } else await promises_default().writeFile(external_path_default().join(depDir, "server_code", `${mod.name}.py`), mod.code);
14590
- for (const asset of dep.assets ?? []){
14591
- const targetPath = external_path_default().join(depDir, "theme", "assets", safeDependencyPath(asset.name, "asset"));
14592
- await promises_default().mkdir(external_path_default().dirname(targetPath), {
14593
- recursive: true
14594
- });
14595
- await promises_default().writeFile(targetPath, Buffer.from(asset.content, "base64"));
14596
- }
14597
- for (const doc of dep.docs ?? []){
14598
- const targetPath = external_path_default().join(depDir, safeDependencyPath(doc.path, "doc"));
14599
- await promises_default().mkdir(external_path_default().dirname(targetPath), {
14600
- recursive: true
14601
- });
14602
- await promises_default().writeFile(targetPath, doc.content);
14603
- }
14604
- }
14605
- async function writeAllDepsToCache(projectRoot, response, options = {}) {
14606
- const includeDocs = options.includeDocs ?? true;
14607
- const manifest = await readDepsCacheManifest(projectRoot);
14608
- const canReuseExistingCache = manifest?.includeDocs === includeDocs;
14609
- if (!canReuseExistingCache) await clearDepsCache(projectRoot);
14610
- const previousCached = canReuseExistingCache ? manifest.cached_dependencies : [];
14611
- const resolvedAppIds = new Set(response.resolved_dependencies.map((dep)=>dep.app_id));
14612
- for (const dep of previousCached)if (!resolvedAppIds.has(dep.app_id)) await promises_default().rm(external_path_default().join(projectRoot, ANVIL_DEPS_DIR, dep.package_name), {
14613
- recursive: true,
14614
- force: true
14615
- });
14616
- for (const dep of response.dependencies)await writeDepToCache(projectRoot, dep);
14617
- const returnedByAppId = new Map(response.dependencies.map((dep)=>[
14618
- dep.app_id,
14619
- dep
14620
- ]));
14621
- const previousByAppId = new Map(previousCached.map((dep)=>[
14622
- dep.app_id,
14623
- dep
14624
- ]));
14625
- const cachedDependencies = response.resolved_dependencies.map((dep)=>{
14626
- const returned = returnedByAppId.get(dep.app_id);
14627
- if (returned) return cachedManifestEntry(returned, returned.package_name);
14628
- const previous = previousByAppId.get(dep.app_id);
14629
- if (!previous) return;
14630
- return cachedManifestEntry(dep, previous.package_name);
14631
- }).filter((dep)=>void 0 !== dep);
14632
- const newManifest = {
14633
- version: DEPS_MANIFEST_VERSION,
14634
- includeDocs,
14635
- resolved_dependencies: response.resolved_dependencies,
14636
- cached_dependencies: cachedDependencies
14637
- };
14638
- await promises_default().mkdir(depsDir(projectRoot), {
14639
- recursive: true
14640
- });
14641
- await promises_default().writeFile(depsManifestPath(projectRoot), `${JSON.stringify(newManifest, null, 2)}\n`);
14642
- }
14643
- async function clearDepsCache(projectRoot) {
14644
- await promises_default().rm(depsDir(projectRoot), {
14645
- recursive: true,
14646
- force: true
14647
- });
14648
- }
14649
- async function getCachedPackages(projectRoot) {
14650
- try {
14651
- const entries = await promises_default().readdir(depsDir(projectRoot), {
14652
- withFileTypes: true
14653
- });
14654
- return entries.filter((entry)=>entry.isDirectory()).map((entry)=>entry.name);
14655
- } catch {
14656
- return [];
14657
- }
14658
- }
14659
14391
  const external_timers_promises_namespaceObject = require("timers/promises");
14660
14392
  const WANT_NOT_VALID_RE = /\bwant\s+[0-9a-f]{7,40}\s+not valid\b/i;
14661
14393
  const FETCH_RETRY_DELAY_MS = 200;
@@ -15020,90 +14752,359 @@ Promise.resolve(executeGitCredentialOperation(process.argv[2] || "get", {
15020
14752
  throw errors_createGitError.commandFailed("merge --ff-only", e.message);
15021
14753
  }
15022
14754
  }
15023
- async deleteRef(ref) {
15024
- try {
15025
- await this.git.raw([
15026
- "update-ref",
15027
- "-d",
15028
- ref
15029
- ]);
15030
- } catch (e) {}
14755
+ async deleteRef(ref) {
14756
+ try {
14757
+ await this.git.raw([
14758
+ "update-ref",
14759
+ "-d",
14760
+ ref
14761
+ ]);
14762
+ } catch (e) {}
14763
+ }
14764
+ async getRemotes() {
14765
+ try {
14766
+ const remotes = await this.git.getRemotes(true);
14767
+ return remotes.map((r)=>({
14768
+ name: r.name,
14769
+ fetchUrl: r.refs.fetch
14770
+ }));
14771
+ } catch (e) {
14772
+ throw errors_createGitError.commandFailed("remote", e.message);
14773
+ }
14774
+ }
14775
+ async isGitInitialized() {
14776
+ const gitPath = external_path_default().join(this.repoPath, ".git");
14777
+ return external_fs_.existsSync(gitPath);
14778
+ }
14779
+ async removeEmptyDirectories(removedFiles) {
14780
+ const directoriesToCheck = new Set();
14781
+ for (const file of removedFiles){
14782
+ let dir = external_path_default().dirname(file);
14783
+ while(dir && "." !== dir){
14784
+ directoriesToCheck.add(dir);
14785
+ const parentDir = external_path_default().dirname(dir);
14786
+ if (parentDir === dir) break;
14787
+ dir = parentDir;
14788
+ }
14789
+ }
14790
+ const sortedDirs = Array.from(directoriesToCheck).sort((a, b)=>b.split("/").length - a.split("/").length);
14791
+ for (const dir of sortedDirs)try {
14792
+ const dirPath = external_path_default().join(this.repoPath, dir);
14793
+ if (!external_fs_.existsSync(dirPath)) continue;
14794
+ const entries = await external_fs_.promises.readdir(dirPath);
14795
+ if (0 === entries.length) await external_fs_.promises.rmdir(dirPath);
14796
+ } catch (error) {}
14797
+ }
14798
+ }
14799
+ async function getStagedFileChanges(git, gitService) {
14800
+ try {
14801
+ const status = await git.status();
14802
+ const renames = status.renamed || [];
14803
+ const stagedFiles = status.files.filter((f)=>" " !== f.index && "?" !== f.index);
14804
+ const stagedModeOnlyPaths = new Set(gitService ? await gitService.discardStagedModeOnlyChanges(stagedFiles.filter((f)=>"R" !== f.index && "D" !== f.index).map((f)=>f.path)) : []);
14805
+ const stagedRenames = [];
14806
+ for (const f of stagedFiles)if ("R" === f.index) {
14807
+ const rename = renames.find((r)=>r.to === f.path);
14808
+ if (rename) stagedRenames.push({
14809
+ path: rename.to,
14810
+ type: "rename",
14811
+ from: rename.from
14812
+ });
14813
+ }
14814
+ const stagedRenamedFromPaths = new Set(stagedRenames.map((r)=>r.from));
14815
+ const stagedRenamedToPaths = new Set(stagedRenames.map((r)=>r.path));
14816
+ const changes = [];
14817
+ for (const f of stagedFiles){
14818
+ if ("R" !== f.index) {
14819
+ if (!stagedModeOnlyPaths.has(f.path)) {
14820
+ if (!(stagedRenamedFromPaths.has(f.path) || stagedRenamedToPaths.has(f.path))) if ("D" === f.index) changes.push({
14821
+ path: f.path,
14822
+ type: "unlink"
14823
+ });
14824
+ else if ("A" === f.index || "?" === f.index) changes.push({
14825
+ path: f.path,
14826
+ type: "add"
14827
+ });
14828
+ else changes.push({
14829
+ path: f.path,
14830
+ type: "change"
14831
+ });
14832
+ }
14833
+ }
14834
+ }
14835
+ changes.push(...stagedRenames);
14836
+ return changes;
14837
+ } catch (e) {
14838
+ throw errors_createGitError.commandFailed("status", e.message);
14839
+ }
14840
+ }
14841
+ const ANVIL_DEPS_DIR = ".anvil/deps";
14842
+ const ANVIL_DEPS_MANIFEST = "manifest.json";
14843
+ const DEPS_MANIFEST_VERSION = 1;
14844
+ function formatFetchError(prefix, status, errorBody) {
14845
+ if (!errorBody) return `${prefix}: ${status}`;
14846
+ try {
14847
+ const parsed = JSON.parse(errorBody);
14848
+ if ("string" == typeof parsed.error && parsed.error.trim()) return `${prefix}: ${parsed.error} (${status})`;
14849
+ } catch {}
14850
+ return `${prefix}: ${status} ${errorBody}`;
14851
+ }
14852
+ async function fetchAllDependencies(appId, anvilUrl = resolveAnvilUrl(), options = {}) {
14853
+ const { commit, includeDocs = true, knownDependencies = [] } = options;
14854
+ const token = await auth_getValidAuthToken(anvilUrl, options.username);
14855
+ const body = {
14856
+ include_docs: includeDocs,
14857
+ known_dependencies: knownDependencies
14858
+ };
14859
+ if (commit) body.commit = commit;
14860
+ const resp = await fetch(`${anvilUrl}/ide/api/_/apps/${appId}/resolved-dependencies`, {
14861
+ method: "POST",
14862
+ headers: {
14863
+ Authorization: `Bearer ${token}`,
14864
+ "Content-Type": "application/json"
14865
+ },
14866
+ body: JSON.stringify(body)
14867
+ });
14868
+ if (!resp.ok) {
14869
+ const errorBody = await resp.text();
14870
+ throw new Error(formatFetchError("Failed to fetch dependencies", resp.status, errorBody));
14871
+ }
14872
+ return await resp.json();
14873
+ }
14874
+ async function refreshDependencyCache(projectRoot, appId, anvilUrl = resolveAnvilUrl(), options = {}) {
14875
+ const includeDocs = options.includeDocs ?? true;
14876
+ const commit = options.commit ?? await new GitService(projectRoot).getCommitId();
14877
+ const knownDependencies = await getKnownDependenciesForCache(projectRoot, includeDocs);
14878
+ const response = await fetchAllDependencies(appId, anvilUrl, {
14879
+ username: options.username,
14880
+ commit,
14881
+ includeDocs,
14882
+ knownDependencies
14883
+ });
14884
+ if (!response.resolved_dependencies.length) {
14885
+ await clearDepsCache(projectRoot);
14886
+ return {
14887
+ response,
14888
+ addedGitignoreEntry: false
14889
+ };
14890
+ }
14891
+ const addedGitignoreEntry = await ensureProjectGitignoreIgnoresAnvilDir(projectRoot);
14892
+ await writeAllDepsToCache(projectRoot, response, {
14893
+ includeDocs
14894
+ });
14895
+ return {
14896
+ response,
14897
+ addedGitignoreEntry
14898
+ };
14899
+ }
14900
+ function depsDir(projectRoot) {
14901
+ return external_path_default().join(projectRoot, ANVIL_DEPS_DIR);
14902
+ }
14903
+ function depsManifestPath(projectRoot) {
14904
+ return external_path_default().join(depsDir(projectRoot), ANVIL_DEPS_MANIFEST);
14905
+ }
14906
+ function isResolvedDependencyManifestEntry(value) {
14907
+ if (!value || "object" != typeof value) return false;
14908
+ const entry = value;
14909
+ return "string" == typeof entry.app_id && entry.app_id.length > 0 && "string" == typeof entry.commit_id && entry.commit_id.length > 0 && (void 0 === entry.branch || "string" == typeof entry.branch);
14910
+ }
14911
+ function isCachedDependencyManifestEntry(value) {
14912
+ if (!isResolvedDependencyManifestEntry(value)) return false;
14913
+ const entry = value;
14914
+ return "string" == typeof entry.package_name && entry.package_name.length > 0;
14915
+ }
14916
+ function isDepsCacheManifest(value) {
14917
+ if (!value || "object" != typeof value) return false;
14918
+ const manifest = value;
14919
+ return manifest.version === DEPS_MANIFEST_VERSION && "boolean" == typeof manifest.includeDocs && Array.isArray(manifest.resolved_dependencies) && manifest.resolved_dependencies.every(isResolvedDependencyManifestEntry) && Array.isArray(manifest.cached_dependencies) && manifest.cached_dependencies.every(isCachedDependencyManifestEntry);
14920
+ }
14921
+ async function readDepsCacheManifest(projectRoot) {
14922
+ try {
14923
+ const raw = await promises_default().readFile(depsManifestPath(projectRoot), "utf-8");
14924
+ const parsed = JSON.parse(raw);
14925
+ return isDepsCacheManifest(parsed) ? parsed : void 0;
14926
+ } catch {
14927
+ return;
14928
+ }
14929
+ }
14930
+ async function getKnownDependenciesForCache(projectRoot, includeDocs) {
14931
+ const manifest = await readDepsCacheManifest(projectRoot);
14932
+ if (!manifest || manifest.includeDocs !== includeDocs) return [];
14933
+ return manifest.resolved_dependencies.map(({ app_id, commit_id })=>({
14934
+ app_id,
14935
+ commit_id
14936
+ }));
14937
+ }
14938
+ function getDependencyWatchSubscriptionsFromManifest(manifest) {
14939
+ if (!manifest) return [];
14940
+ const cachedByAppId = new Map(manifest.cached_dependencies.map((dep)=>[
14941
+ dep.app_id,
14942
+ dep
14943
+ ]));
14944
+ return manifest.resolved_dependencies.flatMap((dep)=>{
14945
+ if (!dep.branch) return [];
14946
+ return [
14947
+ {
14948
+ appId: dep.app_id,
14949
+ branch: dep.branch,
14950
+ packageName: cachedByAppId.get(dep.app_id)?.package_name
14951
+ }
14952
+ ];
14953
+ });
14954
+ }
14955
+ function normalizeAnvilYamlDependenciesFromContent(content) {
14956
+ const parsed = external_js_yaml_default().load(content);
14957
+ if (!parsed || "object" != typeof parsed || !Array.isArray(parsed.dependencies)) return [];
14958
+ return normalizeAnvilYamlDependenciesValue(parsed.dependencies);
14959
+ }
14960
+ function normalizeAnvilYamlDependenciesValue(value) {
14961
+ if (!Array.isArray(value)) return [];
14962
+ return normalizeForComparison(value);
14963
+ }
14964
+ async function readAnvilYamlDependencies(projectRoot) {
14965
+ try {
14966
+ const content = await promises_default().readFile(external_path_default().join(projectRoot, "anvil.yaml"), "utf-8");
14967
+ return normalizeAnvilYamlDependenciesFromContent(content);
14968
+ } catch {
14969
+ return [];
15031
14970
  }
15032
- async getRemotes() {
15033
- try {
15034
- const remotes = await this.git.getRemotes(true);
15035
- return remotes.map((r)=>({
15036
- name: r.name,
15037
- fetchUrl: r.refs.fetch
15038
- }));
15039
- } catch (e) {
15040
- throw errors_createGitError.commandFailed("remote", e.message);
14971
+ }
14972
+ function anvilYamlDependenciesEqual(left, right) {
14973
+ return deepEqual(left, right);
14974
+ }
14975
+ function normalizeForComparison(value) {
14976
+ if (Array.isArray(value)) return value.map(normalizeForComparison);
14977
+ if (value && "object" == typeof value) return Object.fromEntries(Object.entries(value).sort(([left], [right])=>left.localeCompare(right)).map(([key, entryValue])=>[
14978
+ key,
14979
+ normalizeForComparison(entryValue)
14980
+ ]));
14981
+ return value;
14982
+ }
14983
+ function cachedManifestEntry(dep, packageName) {
14984
+ return {
14985
+ app_id: dep.app_id,
14986
+ commit_id: dep.commit_id,
14987
+ ...dep.branch ? {
14988
+ branch: dep.branch
14989
+ } : {},
14990
+ package_name: packageName
14991
+ };
14992
+ }
14993
+ function safeDependencyPath(filePath, kind) {
14994
+ const normalizedPath = external_path_default().normalize(filePath);
14995
+ if (external_path_default().isAbsolute(normalizedPath) || normalizedPath.startsWith("..")) throw new Error(`Invalid dependency ${kind} path: ${filePath}`);
14996
+ return normalizedPath;
14997
+ }
14998
+ async function writeDepToCache(projectRoot, dep) {
14999
+ const depDir = external_path_default().join(projectRoot, ANVIL_DEPS_DIR, dep.package_name);
15000
+ await promises_default().rm(depDir, {
15001
+ recursive: true,
15002
+ force: true
15003
+ });
15004
+ await promises_default().mkdir(external_path_default().join(depDir, "client_code"), {
15005
+ recursive: true
15006
+ });
15007
+ await promises_default().mkdir(external_path_default().join(depDir, "server_code"), {
15008
+ recursive: true
15009
+ });
15010
+ await promises_default().writeFile(external_path_default().join(depDir, "anvil.yaml"), external_js_yaml_default().dump({
15011
+ package_name: dep.package_name
15012
+ }));
15013
+ for (const mod of dep.modules ?? [])if (mod.is_package) {
15014
+ const modDir = external_path_default().join(depDir, "client_code", mod.name);
15015
+ await promises_default().mkdir(modDir, {
15016
+ recursive: true
15017
+ });
15018
+ await promises_default().writeFile(external_path_default().join(modDir, "__init__.py"), mod.code);
15019
+ } else await promises_default().writeFile(external_path_default().join(depDir, "client_code", `${mod.name}.py`), mod.code);
15020
+ for (const form of dep.forms ?? []){
15021
+ const { code, class_name, is_package, ...formTemplate } = form;
15022
+ if (is_package) {
15023
+ const formDir = external_path_default().join(depDir, "client_code", class_name);
15024
+ await promises_default().mkdir(formDir, {
15025
+ recursive: true
15026
+ });
15027
+ await promises_default().writeFile(external_path_default().join(formDir, "__init__.py"), code);
15028
+ await promises_default().writeFile(external_path_default().join(formDir, "form_template.yaml"), external_js_yaml_default().dump(formTemplate));
15029
+ } else {
15030
+ await promises_default().writeFile(external_path_default().join(depDir, "client_code", `${class_name}.py`), code);
15031
+ await promises_default().writeFile(external_path_default().join(depDir, "client_code", `${class_name}.yaml`), external_js_yaml_default().dump(formTemplate));
15041
15032
  }
15042
15033
  }
15043
- async isGitInitialized() {
15044
- const gitPath = external_path_default().join(this.repoPath, ".git");
15045
- return external_fs_.existsSync(gitPath);
15034
+ for (const mod of dep.server_modules ?? [])if (mod.is_package) {
15035
+ const modDir = external_path_default().join(depDir, "server_code", mod.name);
15036
+ await promises_default().mkdir(modDir, {
15037
+ recursive: true
15038
+ });
15039
+ await promises_default().writeFile(external_path_default().join(modDir, "__init__.py"), mod.code);
15040
+ } else await promises_default().writeFile(external_path_default().join(depDir, "server_code", `${mod.name}.py`), mod.code);
15041
+ for (const asset of dep.assets ?? []){
15042
+ const targetPath = external_path_default().join(depDir, "theme", "assets", safeDependencyPath(asset.name, "asset"));
15043
+ await promises_default().mkdir(external_path_default().dirname(targetPath), {
15044
+ recursive: true
15045
+ });
15046
+ await promises_default().writeFile(targetPath, Buffer.from(asset.content, "base64"));
15046
15047
  }
15047
- async removeEmptyDirectories(removedFiles) {
15048
- const directoriesToCheck = new Set();
15049
- for (const file of removedFiles){
15050
- let dir = external_path_default().dirname(file);
15051
- while(dir && "." !== dir){
15052
- directoriesToCheck.add(dir);
15053
- const parentDir = external_path_default().dirname(dir);
15054
- if (parentDir === dir) break;
15055
- dir = parentDir;
15056
- }
15057
- }
15058
- const sortedDirs = Array.from(directoriesToCheck).sort((a, b)=>b.split("/").length - a.split("/").length);
15059
- for (const dir of sortedDirs)try {
15060
- const dirPath = external_path_default().join(this.repoPath, dir);
15061
- if (!external_fs_.existsSync(dirPath)) continue;
15062
- const entries = await external_fs_.promises.readdir(dirPath);
15063
- if (0 === entries.length) await external_fs_.promises.rmdir(dirPath);
15064
- } catch (error) {}
15048
+ for (const doc of dep.docs ?? []){
15049
+ const targetPath = external_path_default().join(depDir, safeDependencyPath(doc.path, "doc"));
15050
+ await promises_default().mkdir(external_path_default().dirname(targetPath), {
15051
+ recursive: true
15052
+ });
15053
+ await promises_default().writeFile(targetPath, doc.content);
15065
15054
  }
15066
15055
  }
15067
- async function getStagedFileChanges(git, gitService) {
15056
+ async function writeAllDepsToCache(projectRoot, response, options = {}) {
15057
+ const includeDocs = options.includeDocs ?? true;
15058
+ const manifest = await readDepsCacheManifest(projectRoot);
15059
+ const canReuseExistingCache = manifest?.includeDocs === includeDocs;
15060
+ if (!canReuseExistingCache) await clearDepsCache(projectRoot);
15061
+ const previousCached = canReuseExistingCache ? manifest.cached_dependencies : [];
15062
+ const resolvedAppIds = new Set(response.resolved_dependencies.map((dep)=>dep.app_id));
15063
+ for (const dep of previousCached)if (!resolvedAppIds.has(dep.app_id)) await promises_default().rm(external_path_default().join(projectRoot, ANVIL_DEPS_DIR, dep.package_name), {
15064
+ recursive: true,
15065
+ force: true
15066
+ });
15067
+ for (const dep of response.dependencies)await writeDepToCache(projectRoot, dep);
15068
+ const returnedByAppId = new Map(response.dependencies.map((dep)=>[
15069
+ dep.app_id,
15070
+ dep
15071
+ ]));
15072
+ const previousByAppId = new Map(previousCached.map((dep)=>[
15073
+ dep.app_id,
15074
+ dep
15075
+ ]));
15076
+ const cachedDependencies = response.resolved_dependencies.map((dep)=>{
15077
+ const returned = returnedByAppId.get(dep.app_id);
15078
+ if (returned) return cachedManifestEntry(returned, returned.package_name);
15079
+ const previous = previousByAppId.get(dep.app_id);
15080
+ if (!previous) return;
15081
+ return cachedManifestEntry(dep, previous.package_name);
15082
+ }).filter((dep)=>void 0 !== dep);
15083
+ const newManifest = {
15084
+ version: DEPS_MANIFEST_VERSION,
15085
+ includeDocs,
15086
+ resolved_dependencies: response.resolved_dependencies,
15087
+ cached_dependencies: cachedDependencies
15088
+ };
15089
+ await promises_default().mkdir(depsDir(projectRoot), {
15090
+ recursive: true
15091
+ });
15092
+ await promises_default().writeFile(depsManifestPath(projectRoot), `${JSON.stringify(newManifest, null, 2)}\n`);
15093
+ }
15094
+ async function clearDepsCache(projectRoot) {
15095
+ await promises_default().rm(depsDir(projectRoot), {
15096
+ recursive: true,
15097
+ force: true
15098
+ });
15099
+ }
15100
+ async function getCachedPackages(projectRoot) {
15068
15101
  try {
15069
- const status = await git.status();
15070
- const renames = status.renamed || [];
15071
- const stagedFiles = status.files.filter((f)=>" " !== f.index && "?" !== f.index);
15072
- const stagedModeOnlyPaths = new Set(gitService ? await gitService.discardStagedModeOnlyChanges(stagedFiles.filter((f)=>"R" !== f.index && "D" !== f.index).map((f)=>f.path)) : []);
15073
- const stagedRenames = [];
15074
- for (const f of stagedFiles)if ("R" === f.index) {
15075
- const rename = renames.find((r)=>r.to === f.path);
15076
- if (rename) stagedRenames.push({
15077
- path: rename.to,
15078
- type: "rename",
15079
- from: rename.from
15080
- });
15081
- }
15082
- const stagedRenamedFromPaths = new Set(stagedRenames.map((r)=>r.from));
15083
- const stagedRenamedToPaths = new Set(stagedRenames.map((r)=>r.path));
15084
- const changes = [];
15085
- for (const f of stagedFiles){
15086
- if ("R" !== f.index) {
15087
- if (!stagedModeOnlyPaths.has(f.path)) {
15088
- if (!(stagedRenamedFromPaths.has(f.path) || stagedRenamedToPaths.has(f.path))) if ("D" === f.index) changes.push({
15089
- path: f.path,
15090
- type: "unlink"
15091
- });
15092
- else if ("A" === f.index || "?" === f.index) changes.push({
15093
- path: f.path,
15094
- type: "add"
15095
- });
15096
- else changes.push({
15097
- path: f.path,
15098
- type: "change"
15099
- });
15100
- }
15101
- }
15102
- }
15103
- changes.push(...stagedRenames);
15104
- return changes;
15105
- } catch (e) {
15106
- throw errors_createGitError.commandFailed("status", e.message);
15102
+ const entries = await promises_default().readdir(depsDir(projectRoot), {
15103
+ withFileTypes: true
15104
+ });
15105
+ return entries.filter((entry)=>entry.isDirectory()).map((entry)=>entry.name);
15106
+ } catch {
15107
+ return [];
15107
15108
  }
15108
15109
  }
15109
15110
  const external_chokidar_namespaceObject = require("chokidar");
@@ -23060,7 +23061,7 @@ print(json.dumps({"issues": issues}))
23060
23061
  }
23061
23062
  function registerDepsCommand(program) {
23062
23063
  const deps = program.command("deps").description("Manage app dependencies");
23063
- deps.command("fetch").description("Fetch dependencies into .anvil/deps/").option("--commit <commit>", "Fetch deps at specific commit").option("--exclude-docs", "Exclude dependency docs from .anvil/deps/").action(async (options)=>{
23064
+ deps.command("fetch").description("Fetch dependencies for checkout HEAD into .anvil/deps/").option("--commit <commit>", "Fetch deps at a specific app commit instead of checkout HEAD").option("--exclude-docs", "Exclude dependency docs from .anvil/deps/").action(async (options)=>{
23064
23065
  try {
23065
23066
  const projectRoot = process.cwd();
23066
23067
  const anvilUrl = resolveAnvilUrl();