@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 +347 -346
- package/dist/index.js +311 -310
- package/dist/services/deps.d.ts.map +1 -1
- package/package.json +1 -1
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
|
-
|
|
15033
|
-
|
|
15034
|
-
|
|
15035
|
-
|
|
15036
|
-
|
|
15037
|
-
|
|
15038
|
-
|
|
15039
|
-
|
|
15040
|
-
|
|
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
|
-
|
|
15044
|
-
const
|
|
15045
|
-
|
|
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
|
-
|
|
15048
|
-
const
|
|
15049
|
-
|
|
15050
|
-
|
|
15051
|
-
|
|
15052
|
-
|
|
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
|
|
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
|
|
15070
|
-
|
|
15071
|
-
|
|
15072
|
-
|
|
15073
|
-
|
|
15074
|
-
|
|
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();
|