@anvil-works/anvil-cli 0.8.0-canary.2 → 0.8.0-canary.21
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/README.md +3 -1
- package/dist/SavePathRouter.d.ts.map +1 -1
- package/dist/WatchSession.d.ts +2 -0
- package/dist/WatchSession.d.ts.map +1 -1
- package/dist/api.d.ts +7 -2
- package/dist/api.d.ts.map +1 -1
- package/dist/cli.js +2255 -1086
- package/dist/commands/checkout-picker.d.ts +1 -2
- package/dist/commands/checkout-picker.d.ts.map +1 -1
- package/dist/commands/configure.d.ts.map +1 -1
- package/dist/commands/db.d.ts +3 -0
- package/dist/commands/db.d.ts.map +1 -0
- package/dist/commands/deps.d.ts.map +1 -1
- package/dist/commands/env.d.ts +8 -0
- package/dist/commands/env.d.ts.map +1 -0
- package/dist/commands/gitAuth.d.ts +5 -1
- package/dist/commands/gitAuth.d.ts.map +1 -1
- package/dist/commands/index.d.ts +4 -1
- package/dist/commands/index.d.ts.map +1 -1
- package/dist/commands/login.d.ts.map +1 -1
- package/dist/commands/modelToken.d.ts +33 -0
- package/dist/commands/modelToken.d.ts.map +1 -0
- package/dist/commands/repl.d.ts +3 -0
- package/dist/commands/repl.d.ts.map +1 -0
- package/dist/commands/secret.d.ts +3 -0
- package/dist/commands/secret.d.ts.map +1 -0
- package/dist/commands/watch.d.ts.map +1 -1
- package/dist/componentTypeSpec.d.ts +1 -1
- package/dist/componentTypeSpec.d.ts.map +1 -1
- package/dist/errors.d.ts +11 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/formTemplateValidation.d.ts.map +1 -1
- package/dist/index.js +1555 -1030
- package/dist/program.d.ts +5 -14
- package/dist/program.d.ts.map +1 -1
- package/dist/services/anvil-api.d.ts.map +1 -1
- package/dist/services/auth.d.ts +42 -3
- package/dist/services/auth.d.ts.map +1 -1
- package/dist/services/db.d.ts +7 -0
- package/dist/services/db.d.ts.map +1 -0
- package/dist/services/deps.d.ts +9 -4
- package/dist/services/deps.d.ts.map +1 -1
- package/dist/services/environment.d.ts +16 -0
- package/dist/services/environment.d.ts.map +1 -0
- package/dist/services/git-auth.d.ts +38 -1
- package/dist/services/git-auth.d.ts.map +1 -1
- package/dist/services/git.d.ts +1 -0
- package/dist/services/git.d.ts.map +1 -1
- package/dist/services/http.d.ts +2 -0
- package/dist/services/http.d.ts.map +1 -0
- package/dist/services/repl.d.ts +18 -0
- package/dist/services/repl.d.ts.map +1 -0
- package/dist/services/secret.d.ts +39 -0
- package/dist/services/secret.d.ts.map +1 -0
- package/dist/validateFormTemplateHtml.d.ts +6 -2
- package/dist/validateFormTemplateHtml.d.ts.map +1 -1
- package/dist/validatePython.d.ts +29 -3
- package/dist/validatePython.d.ts.map +1 -1
- package/dist/validators.d.ts +5 -1
- package/dist/validators.d.ts.map +1 -1
- package/dist/watch/SaveProcessor.d.ts +2 -0
- package/dist/watch/SaveProcessor.d.ts.map +1 -1
- package/package.json +3 -2
- package/dist/commands/tables.d.ts +0 -7
- package/dist/commands/tables.d.ts.map +0 -1
- package/dist/services/tables.d.ts +0 -27
- package/dist/services/tables.d.ts.map +0 -1
package/dist/index.js
CHANGED
|
@@ -7376,27 +7376,29 @@ var __webpack_exports__ = {};
|
|
|
7376
7376
|
validateCheckoutDestination: ()=>validateCheckoutDestination,
|
|
7377
7377
|
DeviceAuthorizationResponse: ()=>oauth_login_namespaceObject.DeviceAuthorizationResponse,
|
|
7378
7378
|
OAuthTokenResponse: ()=>oauth_login_namespaceObject.OAuthTokenResponse,
|
|
7379
|
+
REFRESH_REQUEST_TIMEOUT_MS: ()=>REFRESH_REQUEST_TIMEOUT_MS,
|
|
7379
7380
|
WatchSession: ()=>WatchSession_WatchSession,
|
|
7380
7381
|
parseConfigSetValue: ()=>parseConfigSetValue,
|
|
7381
|
-
requestDeviceAuthorization: ()=>requestDeviceAuthorization,
|
|
7382
7382
|
getGitPushUrl: ()=>anvil_api_getGitPushUrl,
|
|
7383
|
+
requestDeviceAuthorization: ()=>requestDeviceAuthorization,
|
|
7383
7384
|
requireEnvironmentBranch: ()=>requireEnvironmentBranch,
|
|
7384
7385
|
setAuthTokensForAccount: ()=>setAccountTokens,
|
|
7385
|
-
validateAnvilApp: ()=>validation_validateAnvilApp,
|
|
7386
7386
|
ValidateAppIdResponse: ()=>anvil_api_namespaceObject.ValidateAppIdResponse,
|
|
7387
|
-
|
|
7387
|
+
getModelToken: ()=>getModelToken,
|
|
7388
7388
|
formatValidationPath: ()=>formatValidationPath,
|
|
7389
7389
|
resolveAnvilUrl: ()=>resolveAnvilUrl,
|
|
7390
|
+
createCheckoutPickerSource: ()=>createCheckoutPickerSource,
|
|
7390
7391
|
buildGitAuthDoctorReport: ()=>buildGitAuthDoctorReport,
|
|
7391
7392
|
parseCheckoutInput: ()=>parseCheckoutInput,
|
|
7392
7393
|
formatGitAuthDoctorReport: ()=>formatGitAuthDoctorReport,
|
|
7393
7394
|
resetConfig: ()=>resetConfig,
|
|
7395
|
+
MODEL_TOKEN_HELPER_FILENAME: ()=>MODEL_TOKEN_HELPER_FILENAME,
|
|
7394
7396
|
FormTemplateValidationResult: ()=>validators_namespaceObject.FormTemplateValidationResult,
|
|
7395
7397
|
createWatchDiagnostic: ()=>createWatchDiagnostic,
|
|
7396
7398
|
ValidationTarget: ()=>validators_namespaceObject.ValidationTarget,
|
|
7397
7399
|
detectAppIdsByCommitLookup: ()=>anvil_api_detectAppIdsByCommitLookup,
|
|
7398
7400
|
sanitizeDirectoryName: ()=>sanitizeDirectoryName,
|
|
7399
|
-
|
|
7401
|
+
validateAnvilApp: ()=>validation_validateAnvilApp,
|
|
7400
7402
|
LoginResult: ()=>auth_namespaceObject.LoginResult,
|
|
7401
7403
|
listAppsForCheckout: ()=>listAppsForCheckout,
|
|
7402
7404
|
getInstalledPreferredEditors: ()=>getInstalledPreferredEditors,
|
|
@@ -7405,8 +7407,11 @@ var __webpack_exports__ = {};
|
|
|
7405
7407
|
CheckoutExecutionDeps: ()=>checkout_namespaceObject.CheckoutExecutionDeps,
|
|
7406
7408
|
validateFormTemplateHtml: ()=>validateFormTemplateHtml,
|
|
7407
7409
|
ValidateBranchSyncStatusOptions: ()=>validation_namespaceObject.ValidateBranchSyncStatusOptions,
|
|
7410
|
+
validatePath: ()=>validatePath,
|
|
7411
|
+
withRepoAuthFileLock: ()=>withRepoAuthFileLock,
|
|
7408
7412
|
BranchSyncStatus: ()=>validation_namespaceObject.BranchSyncStatus,
|
|
7409
7413
|
getLatestVersion: ()=>getLatestVersion,
|
|
7414
|
+
getModelTokenHelperScript: ()=>getModelTokenHelperScript,
|
|
7410
7415
|
isDirectoryNonEmpty: ()=>isDirectoryNonEmpty,
|
|
7411
7416
|
syncToLatest: ()=>src_api_syncToLatest,
|
|
7412
7417
|
filterCandidates: ()=>anvil_api_filterCandidates,
|
|
@@ -7415,7 +7420,7 @@ var __webpack_exports__ = {};
|
|
|
7415
7420
|
isPathInsideGitRepo: ()=>isPathInsideGitRepo,
|
|
7416
7421
|
normalizeAnvilUrl: ()=>config_normalizeAnvilUrl,
|
|
7417
7422
|
ValidationIssue: ()=>validators_namespaceObject.ValidationIssue,
|
|
7418
|
-
|
|
7423
|
+
verifyAndStoreTokens: ()=>auth_verifyAndStoreTokens,
|
|
7419
7424
|
readEnvironmentPid: ()=>environment_readEnvironmentPid,
|
|
7420
7425
|
validateFormTemplate: ()=>validateFormTemplate,
|
|
7421
7426
|
preferredEditors: ()=>preferredEditors,
|
|
@@ -7427,12 +7432,14 @@ var __webpack_exports__ = {};
|
|
|
7427
7432
|
validateBranchSyncStatus: ()=>validation_validateBranchSyncStatus,
|
|
7428
7433
|
checkUncommittedChanges: ()=>api_checkUncommittedChanges,
|
|
7429
7434
|
hasTokensForUrl: ()=>auth_hasTokensForUrl,
|
|
7435
|
+
writeEnvironmentPid: ()=>writeEnvironmentPid,
|
|
7430
7436
|
AnvilYamlValidationResult: ()=>validators_namespaceObject.AnvilYamlValidationResult,
|
|
7431
7437
|
ValidatePathResult: ()=>validators_namespaceObject.ValidatePathResult,
|
|
7432
7438
|
getSettableConfigKeys: ()=>getSettableConfigKeys,
|
|
7433
7439
|
logout: ()=>logout,
|
|
7434
7440
|
TokenData: ()=>auth_namespaceObject.TokenData,
|
|
7435
7441
|
detectAppIdsFromAllRemotes: ()=>anvil_api_detectAppIdsFromAllRemotes,
|
|
7442
|
+
executeModelTokenOperation: ()=>executeModelTokenOperation,
|
|
7436
7443
|
getWebSocketUrl: ()=>getWebSocketUrl,
|
|
7437
7444
|
getAllConfig: ()=>getAllConfig,
|
|
7438
7445
|
isCommandAvailable: ()=>config_isCommandAvailable,
|
|
@@ -7442,6 +7449,7 @@ var __webpack_exports__ = {};
|
|
|
7442
7449
|
getValidAuthToken: ()=>auth_getValidAuthToken,
|
|
7443
7450
|
watch: ()=>src_api_watch,
|
|
7444
7451
|
deleteConfig: ()=>deleteConfig,
|
|
7452
|
+
installModelTokenHelperScript: ()=>installModelTokenHelperScript,
|
|
7445
7453
|
getGitFetchUrl: ()=>anvil_api_getGitFetchUrl,
|
|
7446
7454
|
PreferredEditor: ()=>config_namespaceObject.PreferredEditor,
|
|
7447
7455
|
getAvailableAnvilUrls: ()=>getAvailableAnvilUrls,
|
|
@@ -7454,8 +7462,11 @@ var __webpack_exports__ = {};
|
|
|
7454
7462
|
CheckoutResult: ()=>checkout_namespaceObject.CheckoutResult,
|
|
7455
7463
|
ParsedCheckoutInput: ()=>checkout_namespaceObject.ParsedCheckoutInput,
|
|
7456
7464
|
parseCredentialHelperCommand: ()=>parseCredentialHelperCommand,
|
|
7465
|
+
MODEL_TOKEN_LOCK_TIMEOUT_MS: ()=>MODEL_TOKEN_LOCK_TIMEOUT_MS,
|
|
7466
|
+
GetValidAuthTokenOptions: ()=>auth_namespaceObject.GetValidAuthTokenOptions,
|
|
7457
7467
|
clearInMemoryTokens: ()=>clearInMemoryTokens,
|
|
7458
7468
|
getDefaultDestinationDirectory: ()=>getDefaultDestinationDirectory,
|
|
7469
|
+
AUTH_FILE_LOCK_DEFAULTS: ()=>AUTH_FILE_LOCK_DEFAULTS,
|
|
7459
7470
|
lookupByCommit: ()=>lookupByCommit,
|
|
7460
7471
|
AppIdCandidate: ()=>anvil_api_namespaceObject.AppIdCandidate,
|
|
7461
7472
|
verifyAuth: ()=>auth_verifyAuth
|
|
@@ -7484,12 +7495,14 @@ var __webpack_exports__ = {};
|
|
|
7484
7495
|
__webpack_require__.r(auth_namespaceObject);
|
|
7485
7496
|
__webpack_require__.d(auth_namespaceObject, {
|
|
7486
7497
|
fg: ()=>ANVIL_SYNC_CLIENT_ID,
|
|
7498
|
+
ru: ()=>REFRESH_REQUEST_TIMEOUT_MS,
|
|
7487
7499
|
Ou: ()=>clearInMemoryTokens,
|
|
7488
7500
|
Fr: ()=>auth_getAccountsForUrl,
|
|
7489
7501
|
u5: ()=>getAvailableAnvilUrls,
|
|
7490
7502
|
_p: ()=>auth_getValidAuthToken,
|
|
7491
7503
|
Fi: ()=>auth_hasTokensForUrl,
|
|
7492
7504
|
ri: ()=>logout,
|
|
7505
|
+
UE: ()=>auth_resolveAuthAnvilUrl,
|
|
7493
7506
|
f5: ()=>setInMemoryTokens,
|
|
7494
7507
|
iT: ()=>auth_setRepoContext,
|
|
7495
7508
|
CQ: ()=>auth_verifyAndStoreTokens,
|
|
@@ -13364,9 +13377,10 @@ var __webpack_exports__ = {};
|
|
|
13364
13377
|
type: "auth_invalid",
|
|
13365
13378
|
message
|
|
13366
13379
|
}),
|
|
13367
|
-
refreshFailed: (message)=>({
|
|
13380
|
+
refreshFailed: (message, status)=>({
|
|
13368
13381
|
type: "token_refresh_failed",
|
|
13369
|
-
message
|
|
13382
|
+
message,
|
|
13383
|
+
status
|
|
13370
13384
|
})
|
|
13371
13385
|
};
|
|
13372
13386
|
const createAppError = {
|
|
@@ -13582,6 +13596,9 @@ var __webpack_exports__ = {};
|
|
|
13582
13596
|
}
|
|
13583
13597
|
const external_module_namespaceObject = require("module");
|
|
13584
13598
|
const requireFromHere = (0, external_module_namespaceObject.createRequire)(__filename);
|
|
13599
|
+
const GIT_CREDENTIAL_HELPER_FILENAME = "anvil-credential-helper.cjs";
|
|
13600
|
+
const MODEL_TOKEN_HELPER_FILENAME = "anvil-model-token-helper.cjs";
|
|
13601
|
+
const GIT_CREDENTIAL_HELPER_LOCATOR = `$(git rev-parse --git-common-dir)/${GIT_CREDENTIAL_HELPER_FILENAME}`;
|
|
13585
13602
|
function getUrlConfigKey(appId) {
|
|
13586
13603
|
return `anvil.auth.${appId}.url`;
|
|
13587
13604
|
}
|
|
@@ -13598,12 +13615,14 @@ var __webpack_exports__ = {};
|
|
|
13598
13615
|
const platform = options?.platform ?? process.platform;
|
|
13599
13616
|
const execPath = options?.execPath ?? process.execPath;
|
|
13600
13617
|
const helperPath = options.helperPath;
|
|
13618
|
+
const dynamicHelperPath = `"${GIT_CREDENTIAL_HELPER_LOCATOR}"`;
|
|
13601
13619
|
if ("win32" === platform) {
|
|
13602
13620
|
const resolvedExecPath = external_path_default().win32.resolve(execPath);
|
|
13603
|
-
const
|
|
13604
|
-
return `!${shellQuoteDouble(resolvedExecPath)} ${
|
|
13621
|
+
const helperArgument = helperPath ? shellQuoteDouble(external_path_default().win32.resolve(helperPath)) : dynamicHelperPath;
|
|
13622
|
+
return `!${shellQuoteDouble(resolvedExecPath)} ${helperArgument}`;
|
|
13605
13623
|
}
|
|
13606
|
-
|
|
13624
|
+
const helperArgument = helperPath ? shellQuote(external_path_default().resolve(helperPath)) : dynamicHelperPath;
|
|
13625
|
+
return `!${shellQuote(external_path_default().resolve(execPath))} ${helperArgument}`;
|
|
13607
13626
|
}
|
|
13608
13627
|
async function getLocalConfigValue(repoPath, key) {
|
|
13609
13628
|
try {
|
|
@@ -13652,21 +13671,14 @@ var __webpack_exports__ = {};
|
|
|
13652
13671
|
return `#!/usr/bin/env node
|
|
13653
13672
|
"use strict";
|
|
13654
13673
|
|
|
13655
|
-
const
|
|
13656
|
-
const path = require("path");
|
|
13674
|
+
const { execFileSync } = require("child_process");
|
|
13657
13675
|
const { executeGitCredentialOperation } = require(${JSON.stringify(modulePath)});
|
|
13658
13676
|
|
|
13659
13677
|
function resolveRepoPath() {
|
|
13660
|
-
|
|
13661
|
-
|
|
13662
|
-
|
|
13663
|
-
|
|
13664
|
-
const gitDir = __dirname;
|
|
13665
|
-
const linkedWorktreeGitdir = path.join(gitDir, "gitdir");
|
|
13666
|
-
if (fs.existsSync(linkedWorktreeGitdir)) {
|
|
13667
|
-
return path.dirname(path.resolve(gitDir, fs.readFileSync(linkedWorktreeGitdir, "utf8").trim()));
|
|
13668
|
-
}
|
|
13669
|
-
return path.dirname(gitDir);
|
|
13678
|
+
return execFileSync("git", ["rev-parse", "--show-toplevel"], {
|
|
13679
|
+
cwd: process.cwd(),
|
|
13680
|
+
encoding: "utf8",
|
|
13681
|
+
}).trim();
|
|
13670
13682
|
}
|
|
13671
13683
|
|
|
13672
13684
|
Promise.resolve(executeGitCredentialOperation(process.argv[2] || "get", {
|
|
@@ -13676,6 +13688,38 @@ Promise.resolve(executeGitCredentialOperation(process.argv[2] || "get", {
|
|
|
13676
13688
|
});
|
|
13677
13689
|
`;
|
|
13678
13690
|
}
|
|
13691
|
+
function getModelTokenHelperScript(modulePath) {
|
|
13692
|
+
return `#!/usr/bin/env node
|
|
13693
|
+
"use strict";
|
|
13694
|
+
|
|
13695
|
+
const { execFileSync } = require("child_process");
|
|
13696
|
+
const { executeModelTokenOperation } = require(${JSON.stringify(modulePath)});
|
|
13697
|
+
|
|
13698
|
+
function resolveRepoPath() {
|
|
13699
|
+
return execFileSync("git", ["rev-parse", "--show-toplevel"], {
|
|
13700
|
+
cwd: process.cwd(),
|
|
13701
|
+
encoding: "utf8",
|
|
13702
|
+
}).trim();
|
|
13703
|
+
}
|
|
13704
|
+
|
|
13705
|
+
Promise.resolve()
|
|
13706
|
+
.then(() => executeModelTokenOperation({ repoPath: resolveRepoPath() }))
|
|
13707
|
+
.catch((e) => {
|
|
13708
|
+
process.stderr.write("anvil model token helper failed: " + ((e && e.message) || e) + "\\n");
|
|
13709
|
+
process.exit(1);
|
|
13710
|
+
});
|
|
13711
|
+
`;
|
|
13712
|
+
}
|
|
13713
|
+
async function installModelTokenHelperScript(helperPath, options) {
|
|
13714
|
+
await external_fs_.promises.mkdir(external_path_default().dirname(helperPath), {
|
|
13715
|
+
recursive: true
|
|
13716
|
+
});
|
|
13717
|
+
await external_fs_.promises.writeFile(helperPath, getModelTokenHelperScript(options?.modulePath ?? getCredentialHelperModulePath()), {
|
|
13718
|
+
mode: 448
|
|
13719
|
+
});
|
|
13720
|
+
await external_fs_.promises.chmod(helperPath, 448);
|
|
13721
|
+
return helperPath;
|
|
13722
|
+
}
|
|
13679
13723
|
function findPackageRoot(startPath) {
|
|
13680
13724
|
let dir = external_fs_default().existsSync(startPath) && external_fs_default().statSync(startPath).isDirectory() ? startPath : external_path_default().dirname(startPath);
|
|
13681
13725
|
while(true){
|
|
@@ -13701,16 +13745,22 @@ Promise.resolve(executeGitCredentialOperation(process.argv[2] || "get", {
|
|
|
13701
13745
|
if (isAnvilCliPackageFile(currentModulePath) && "cli.js" !== external_path_default().basename(currentModulePath)) return currentModulePath;
|
|
13702
13746
|
return (options?.resolvePackageEntry ?? (()=>requireFromHere.resolve("@anvil-works/anvil-cli")))();
|
|
13703
13747
|
}
|
|
13704
|
-
async function
|
|
13705
|
-
const
|
|
13748
|
+
async function getRepositoryGitPath(repoPath, revParseArg, git = esm_default(repoPath)) {
|
|
13749
|
+
const rawGitPath = (await git.raw([
|
|
13706
13750
|
"rev-parse",
|
|
13707
|
-
|
|
13751
|
+
revParseArg
|
|
13708
13752
|
])).trim();
|
|
13709
|
-
return external_path_default().isAbsolute(
|
|
13753
|
+
return external_path_default().isAbsolute(rawGitPath) ? rawGitPath : external_path_default().resolve(repoPath, rawGitPath);
|
|
13754
|
+
}
|
|
13755
|
+
async function getRepositoryGitDir(repoPath, git = esm_default(repoPath)) {
|
|
13756
|
+
return getRepositoryGitPath(repoPath, "--git-dir", git);
|
|
13757
|
+
}
|
|
13758
|
+
async function getRepositoryCommonGitDir(repoPath, git = esm_default(repoPath)) {
|
|
13759
|
+
return getRepositoryGitPath(repoPath, "--git-common-dir", git);
|
|
13710
13760
|
}
|
|
13711
13761
|
async function writeCredentialHelperScript(options) {
|
|
13712
|
-
const
|
|
13713
|
-
const helperPath = external_path_default().join(
|
|
13762
|
+
const gitCommonDir = await getRepositoryCommonGitDir(options.repoPath, options.git);
|
|
13763
|
+
const helperPath = external_path_default().join(gitCommonDir, GIT_CREDENTIAL_HELPER_FILENAME);
|
|
13714
13764
|
await external_fs_.promises.mkdir(external_path_default().dirname(helperPath), {
|
|
13715
13765
|
recursive: true
|
|
13716
13766
|
});
|
|
@@ -13725,7 +13775,7 @@ Promise.resolve(executeGitCredentialOperation(process.argv[2] || "get", {
|
|
|
13725
13775
|
const url = new URL(normalized);
|
|
13726
13776
|
const scope = `${url.protocol}//${url.host}`;
|
|
13727
13777
|
const git = esm_default(repoPath);
|
|
13728
|
-
|
|
13778
|
+
await writeCredentialHelperScript({
|
|
13729
13779
|
repoPath,
|
|
13730
13780
|
git,
|
|
13731
13781
|
modulePath: options?.modulePath
|
|
@@ -13752,7 +13802,6 @@ Promise.resolve(executeGitCredentialOperation(process.argv[2] || "get", {
|
|
|
13752
13802
|
`credential.${scope}.helper`,
|
|
13753
13803
|
getGitCredentialHelperCommand({
|
|
13754
13804
|
execPath: options?.execPath,
|
|
13755
|
-
helperPath,
|
|
13756
13805
|
platform: options?.platform
|
|
13757
13806
|
})
|
|
13758
13807
|
]);
|
|
@@ -13798,6 +13847,76 @@ Promise.resolve(executeGitCredentialOperation(process.argv[2] || "get", {
|
|
|
13798
13847
|
function getAuthFilePath(gitDir) {
|
|
13799
13848
|
return external_path_default().join(gitDir, "anvil-auth.json");
|
|
13800
13849
|
}
|
|
13850
|
+
const AUTH_FILE_LOCK_DEFAULTS = {
|
|
13851
|
+
staleMs: 30000,
|
|
13852
|
+
timeoutMs: 60000,
|
|
13853
|
+
retryDelayMs: 100,
|
|
13854
|
+
heartbeatMs: 5000
|
|
13855
|
+
};
|
|
13856
|
+
function git_auth_delay(ms) {
|
|
13857
|
+
return new Promise((resolve)=>setTimeout(resolve, ms));
|
|
13858
|
+
}
|
|
13859
|
+
async function acquireAuthFileLock(lockPath, options) {
|
|
13860
|
+
const deadline = Date.now() + options.timeoutMs;
|
|
13861
|
+
while(true){
|
|
13862
|
+
try {
|
|
13863
|
+
const handle = await external_fs_.promises.open(lockPath, "wx", 384);
|
|
13864
|
+
try {
|
|
13865
|
+
await handle.writeFile(JSON.stringify({
|
|
13866
|
+
pid: process.pid,
|
|
13867
|
+
acquiredAt: new Date().toISOString()
|
|
13868
|
+
}));
|
|
13869
|
+
} finally{
|
|
13870
|
+
await handle.close();
|
|
13871
|
+
}
|
|
13872
|
+
return;
|
|
13873
|
+
} catch (e) {
|
|
13874
|
+
if ("EEXIST" !== e.code) throw e;
|
|
13875
|
+
}
|
|
13876
|
+
try {
|
|
13877
|
+
const lockStat = await external_fs_.promises.stat(lockPath);
|
|
13878
|
+
if (Date.now() - lockStat.mtimeMs > options.staleMs) {
|
|
13879
|
+
await external_fs_.promises.rm(lockPath, {
|
|
13880
|
+
force: true
|
|
13881
|
+
});
|
|
13882
|
+
continue;
|
|
13883
|
+
}
|
|
13884
|
+
} catch {
|
|
13885
|
+
continue;
|
|
13886
|
+
}
|
|
13887
|
+
if (Date.now() >= deadline) throw new Error(`Timed out waiting for the Anvil auth file lock at ${lockPath}`);
|
|
13888
|
+
await git_auth_delay(options.retryDelayMs);
|
|
13889
|
+
}
|
|
13890
|
+
}
|
|
13891
|
+
async function withRepoAuthFileLock(repoPath, fn, options) {
|
|
13892
|
+
const gitDir = await getRepositoryGitDir(repoPath);
|
|
13893
|
+
const lockPath = `${getAuthFilePath(gitDir)}.lock`;
|
|
13894
|
+
const lockOptions = {
|
|
13895
|
+
...AUTH_FILE_LOCK_DEFAULTS,
|
|
13896
|
+
...options
|
|
13897
|
+
};
|
|
13898
|
+
await external_fs_.promises.mkdir(external_path_default().dirname(lockPath), {
|
|
13899
|
+
recursive: true
|
|
13900
|
+
});
|
|
13901
|
+
await acquireAuthFileLock(lockPath, lockOptions);
|
|
13902
|
+
const heartbeat = startAuthFileLockHeartbeat(lockPath, lockOptions.heartbeatMs);
|
|
13903
|
+
try {
|
|
13904
|
+
return await fn();
|
|
13905
|
+
} finally{
|
|
13906
|
+
clearInterval(heartbeat);
|
|
13907
|
+
await external_fs_.promises.rm(lockPath, {
|
|
13908
|
+
force: true
|
|
13909
|
+
});
|
|
13910
|
+
}
|
|
13911
|
+
}
|
|
13912
|
+
function startAuthFileLockHeartbeat(lockPath, heartbeatMs) {
|
|
13913
|
+
const heartbeat = setInterval(()=>{
|
|
13914
|
+
const now = new Date();
|
|
13915
|
+
external_fs_.promises.utimes(lockPath, now, now).catch(()=>{});
|
|
13916
|
+
}, heartbeatMs);
|
|
13917
|
+
heartbeat.unref();
|
|
13918
|
+
return heartbeat;
|
|
13919
|
+
}
|
|
13801
13920
|
function decodeKey(b64key) {
|
|
13802
13921
|
const key = Buffer.from(b64key, "base64");
|
|
13803
13922
|
if (32 !== key.length) throw new Error("Encryption key must be a base64-encoded 32-byte key");
|
|
@@ -13842,10 +13961,19 @@ Promise.resolve(executeGitCredentialOperation(process.argv[2] || "get", {
|
|
|
13842
13961
|
recursive: true
|
|
13843
13962
|
});
|
|
13844
13963
|
const authJson = JSON.stringify(auth, null, 2);
|
|
13845
|
-
|
|
13846
|
-
|
|
13847
|
-
|
|
13848
|
-
|
|
13964
|
+
const tempFilePath = `${authFilePath}.tmp-${external_node_crypto_default().randomBytes(6).toString("hex")}`;
|
|
13965
|
+
try {
|
|
13966
|
+
await external_fs_.promises.writeFile(tempFilePath, encrypt(authJson, encryptionKey), {
|
|
13967
|
+
mode: 384
|
|
13968
|
+
});
|
|
13969
|
+
await external_fs_.promises.chmod(tempFilePath, 448);
|
|
13970
|
+
await external_fs_.promises.rename(tempFilePath, authFilePath);
|
|
13971
|
+
} catch (e) {
|
|
13972
|
+
await external_fs_.promises.rm(tempFilePath, {
|
|
13973
|
+
force: true
|
|
13974
|
+
});
|
|
13975
|
+
throw e;
|
|
13976
|
+
}
|
|
13849
13977
|
await git.raw([
|
|
13850
13978
|
"config",
|
|
13851
13979
|
"--local",
|
|
@@ -13856,6 +13984,7 @@ Promise.resolve(executeGitCredentialOperation(process.argv[2] || "get", {
|
|
|
13856
13984
|
}
|
|
13857
13985
|
const readAuthFromRepo = async (repoPath)=>{
|
|
13858
13986
|
const git = esm_default(repoPath);
|
|
13987
|
+
if (!await git.checkIsRepo()) return;
|
|
13859
13988
|
const gitDir = await getRepositoryGitDir(repoPath, git);
|
|
13860
13989
|
const authFilePath = getAuthFilePath(gitDir);
|
|
13861
13990
|
if (!external_fs_default().existsSync(authFilePath)) return;
|
|
@@ -13867,6 +13996,7 @@ Promise.resolve(executeGitCredentialOperation(process.argv[2] || "get", {
|
|
|
13867
13996
|
return resolveAnvilUrl();
|
|
13868
13997
|
}
|
|
13869
13998
|
const ANVIL_SYNC_CLIENT_ID = "anvil-sync";
|
|
13999
|
+
const REFRESH_REQUEST_TIMEOUT_MS = 12000;
|
|
13870
14000
|
let inMemoryAuth = null;
|
|
13871
14001
|
function setInMemoryTokens(anvilUrl, username, tokens) {
|
|
13872
14002
|
inMemoryAuth = {
|
|
@@ -13885,6 +14015,11 @@ Promise.resolve(executeGitCredentialOperation(process.argv[2] || "get", {
|
|
|
13885
14015
|
function auth_setRepoContext(repoPath) {
|
|
13886
14016
|
repoContext = repoPath;
|
|
13887
14017
|
}
|
|
14018
|
+
async function auth_resolveAuthAnvilUrl() {
|
|
14019
|
+
if (inMemoryAuth) return inMemoryAuth.anvilUrl;
|
|
14020
|
+
const repoAuth = repoContext ? await readAuthFromRepo(repoContext) : void 0;
|
|
14021
|
+
return repoAuth?.anvilUrl ?? resolveAnvilUrl();
|
|
14022
|
+
}
|
|
13888
14023
|
const inFlightRefreshes = new Map();
|
|
13889
14024
|
async function auth_verifyAuth(authToken, anvilUrl = getDefaultAnvilUrl()) {
|
|
13890
14025
|
try {
|
|
@@ -13905,7 +14040,7 @@ Promise.resolve(executeGitCredentialOperation(process.argv[2] || "get", {
|
|
|
13905
14040
|
throw createAuthError.invalid(`Network error: ${e.message}`);
|
|
13906
14041
|
}
|
|
13907
14042
|
}
|
|
13908
|
-
async function refreshAccessToken(refreshToken, anvilUrl = getDefaultAnvilUrl(), clientId = ANVIL_SYNC_CLIENT_ID) {
|
|
14043
|
+
async function refreshAccessToken(refreshToken, anvilUrl = getDefaultAnvilUrl(), clientId = ANVIL_SYNC_CLIENT_ID, options) {
|
|
13909
14044
|
try {
|
|
13910
14045
|
const tokenResponse = await fetch(`${anvilUrl}/oauth/token`, {
|
|
13911
14046
|
method: "POST",
|
|
@@ -13916,11 +14051,12 @@ Promise.resolve(executeGitCredentialOperation(process.argv[2] || "get", {
|
|
|
13916
14051
|
grant_type: "refresh_token",
|
|
13917
14052
|
refresh_token: refreshToken,
|
|
13918
14053
|
client_id: clientId
|
|
13919
|
-
})
|
|
14054
|
+
}),
|
|
14055
|
+
signal: AbortSignal.timeout(options?.timeoutMs ?? REFRESH_REQUEST_TIMEOUT_MS)
|
|
13920
14056
|
});
|
|
13921
14057
|
if (!tokenResponse.ok) {
|
|
13922
14058
|
const errorText = await tokenResponse.text();
|
|
13923
|
-
throw createAuthError.refreshFailed(`Failed to refresh token: ${tokenResponse.status} ${errorText}
|
|
14059
|
+
throw createAuthError.refreshFailed(`Failed to refresh token: ${tokenResponse.status} ${errorText}`, tokenResponse.status);
|
|
13924
14060
|
}
|
|
13925
14061
|
const tokenData = await tokenResponse.json();
|
|
13926
14062
|
return tokenData;
|
|
@@ -13937,17 +14073,16 @@ Promise.resolve(executeGitCredentialOperation(process.argv[2] || "get", {
|
|
|
13937
14073
|
return hasTokens(anvilUrl, username);
|
|
13938
14074
|
}
|
|
13939
14075
|
}
|
|
13940
|
-
async function auth_getValidAuthToken(anvilUrl = getDefaultAnvilUrl(), username) {
|
|
14076
|
+
async function auth_getValidAuthToken(anvilUrl = getDefaultAnvilUrl(), username, options) {
|
|
13941
14077
|
const normalized = config_normalizeAnvilUrl(anvilUrl);
|
|
13942
14078
|
const source = await resolveTokenSource(normalized, username);
|
|
13943
14079
|
const tokens = getSourceTokens(source);
|
|
13944
14080
|
if (!tokens.authToken && !tokens.refreshToken) throw createAuthError.required("Not logged in. Please log in first.");
|
|
13945
|
-
|
|
13946
|
-
if (tokens.refreshToken && isExpired) {
|
|
14081
|
+
if (tokens.refreshToken && isAccessTokenExpired(tokens)) {
|
|
13947
14082
|
const refreshKey = getRefreshKey(normalized, username, source, tokens.refreshToken);
|
|
13948
14083
|
const existingRefresh = inFlightRefreshes.get(refreshKey);
|
|
13949
14084
|
if (existingRefresh) return existingRefresh;
|
|
13950
|
-
const refreshPromise = refreshAndStoreAccessToken(normalized, username, source, tokens.refreshToken).finally(()=>{
|
|
14085
|
+
const refreshPromise = refreshAndStoreAccessToken(normalized, username, source, tokens.refreshToken, options).finally(()=>{
|
|
13951
14086
|
inFlightRefreshes.delete(refreshKey);
|
|
13952
14087
|
});
|
|
13953
14088
|
inFlightRefreshes.set(refreshKey, refreshPromise);
|
|
@@ -13977,6 +14112,9 @@ Promise.resolve(executeGitCredentialOperation(process.argv[2] || "get", {
|
|
|
13977
14112
|
function getSourceTokens(source) {
|
|
13978
14113
|
return "store" === source.type ? source.tokens : source.auth.tokens;
|
|
13979
14114
|
}
|
|
14115
|
+
function isAccessTokenExpired(tokens) {
|
|
14116
|
+
return null !== tokens.authTokenExpiresAt && tokens.authTokenExpiresAt <= Math.floor(Date.now() / 1000) + 60;
|
|
14117
|
+
}
|
|
13980
14118
|
function getStoreAccountUsername(normalizedUrl, username, refreshToken) {
|
|
13981
14119
|
if (username) return username;
|
|
13982
14120
|
const urlTokens = getUrlTokens(normalizedUrl);
|
|
@@ -13995,11 +14133,43 @@ Promise.resolve(executeGitCredentialOperation(process.argv[2] || "get", {
|
|
|
13995
14133
|
return `store:${normalizedUrl}:${source.accountUsername ?? username ?? refreshToken}`;
|
|
13996
14134
|
}
|
|
13997
14135
|
}
|
|
13998
|
-
async function refreshAndStoreAccessToken(normalizedUrl, username, source, refreshToken) {
|
|
14136
|
+
async function refreshAndStoreAccessToken(normalizedUrl, username, source, refreshToken, options) {
|
|
14137
|
+
if ("repo" === source.type) return refreshRepoAuthTokens(source.repoPath, {
|
|
14138
|
+
anvilUrl: normalizedUrl,
|
|
14139
|
+
username,
|
|
14140
|
+
auth: source.auth,
|
|
14141
|
+
refreshToken,
|
|
14142
|
+
lock: options?.lock,
|
|
14143
|
+
requestTimeoutMs: options?.requestTimeoutMs
|
|
14144
|
+
});
|
|
14145
|
+
return performTokenRefresh(normalizedUrl, username, source, refreshToken, options?.requestTimeoutMs);
|
|
14146
|
+
}
|
|
14147
|
+
async function refreshRepoAuthTokens(repoPath, options) {
|
|
14148
|
+
const normalizedUrl = config_normalizeAnvilUrl(options.anvilUrl);
|
|
14149
|
+
return withRepoAuthFileLock(repoPath, async ()=>{
|
|
14150
|
+
let auth = options.auth;
|
|
14151
|
+
let refreshToken = options.refreshToken;
|
|
14152
|
+
const current = await readAuthFromRepo(repoPath);
|
|
14153
|
+
if (current && authMatches(current, normalizedUrl, options.username)) {
|
|
14154
|
+
if (current.tokens.authToken && !isAccessTokenExpired(current.tokens)) return current.tokens.authToken;
|
|
14155
|
+
auth = current;
|
|
14156
|
+
refreshToken = current.tokens.refreshToken ?? refreshToken;
|
|
14157
|
+
}
|
|
14158
|
+
if (!auth || !refreshToken) throw createAuthError.required("No repo auth tokens available to refresh.");
|
|
14159
|
+
return performTokenRefresh(normalizedUrl, options.username, {
|
|
14160
|
+
type: "repo",
|
|
14161
|
+
auth,
|
|
14162
|
+
repoPath
|
|
14163
|
+
}, refreshToken, options.requestTimeoutMs);
|
|
14164
|
+
}, options.lock);
|
|
14165
|
+
}
|
|
14166
|
+
async function performTokenRefresh(normalizedUrl, username, source, refreshToken, requestTimeoutMs) {
|
|
13999
14167
|
try {
|
|
14000
14168
|
const tokens = getSourceTokens(source);
|
|
14001
14169
|
const clientId = tokens.clientId ?? ANVIL_SYNC_CLIENT_ID;
|
|
14002
|
-
const tokenData = await refreshAccessToken(refreshToken, normalizedUrl, clientId
|
|
14170
|
+
const tokenData = await refreshAccessToken(refreshToken, normalizedUrl, clientId, {
|
|
14171
|
+
timeoutMs: requestTimeoutMs
|
|
14172
|
+
});
|
|
14003
14173
|
const newTokens = {
|
|
14004
14174
|
authToken: tokenData.access_token,
|
|
14005
14175
|
refreshToken: tokenData.refresh_token,
|
|
@@ -14021,10 +14191,26 @@ Promise.resolve(executeGitCredentialOperation(process.argv[2] || "get", {
|
|
|
14021
14191
|
}
|
|
14022
14192
|
return tokenData.access_token;
|
|
14023
14193
|
} catch (e) {
|
|
14194
|
+
if ("repo" === source.type && isRefreshTokenRejection(e)) await clearRepoAuthTokens(source.repoPath, source.auth).catch(()=>{});
|
|
14024
14195
|
await clearTokensForAccount(normalizedUrl, username, refreshToken);
|
|
14025
14196
|
throw e;
|
|
14026
14197
|
}
|
|
14027
14198
|
}
|
|
14199
|
+
function isRefreshTokenRejection(e) {
|
|
14200
|
+
const error = e;
|
|
14201
|
+
return error?.type === "token_refresh_failed" && (400 === error.status || 401 === error.status);
|
|
14202
|
+
}
|
|
14203
|
+
async function clearRepoAuthTokens(repoPath, auth) {
|
|
14204
|
+
const encryptionKey = process.env["ANVIL_AUTH_FILE_ENCRYPTION_KEY"];
|
|
14205
|
+
if (!encryptionKey) return;
|
|
14206
|
+
auth.tokens = {
|
|
14207
|
+
authToken: null,
|
|
14208
|
+
refreshToken: null,
|
|
14209
|
+
authTokenExpiresAt: null,
|
|
14210
|
+
clientId: auth.tokens.clientId
|
|
14211
|
+
};
|
|
14212
|
+
await writeAuthToFile(repoPath, auth, encryptionKey);
|
|
14213
|
+
}
|
|
14028
14214
|
async function clearTokensForAccount(url, username, refreshToken) {
|
|
14029
14215
|
if (inMemoryAuth || repoContext && await readAuthFromRepo(repoContext)) return;
|
|
14030
14216
|
{
|
|
@@ -14106,630 +14292,65 @@ Promise.resolve(executeGitCredentialOperation(process.argv[2] || "get", {
|
|
|
14106
14292
|
return getAvailableUrls();
|
|
14107
14293
|
}
|
|
14108
14294
|
}
|
|
14109
|
-
|
|
14110
|
-
|
|
14295
|
+
const promises_namespaceObject = require("timers/promises");
|
|
14296
|
+
const WANT_NOT_VALID_RE = /\bwant\s+[0-9a-f]{7,40}\s+not valid\b/i;
|
|
14297
|
+
const FETCH_RETRY_DELAY_MS = 200;
|
|
14298
|
+
function isWantNotValidError(error) {
|
|
14299
|
+
return WANT_NOT_VALID_RE.test(error.message || "");
|
|
14111
14300
|
}
|
|
14112
|
-
|
|
14113
|
-
|
|
14114
|
-
|
|
14115
|
-
|
|
14116
|
-
|
|
14117
|
-
|
|
14118
|
-
|
|
14119
|
-
|
|
14120
|
-
|
|
14121
|
-
|
|
14122
|
-
|
|
14123
|
-
|
|
14124
|
-
|
|
14125
|
-
const data = await resp.json();
|
|
14126
|
-
if (resp.ok) return {
|
|
14127
|
-
valid: data.valid ?? false,
|
|
14128
|
-
app_name: data.app_name
|
|
14129
|
-
};
|
|
14130
|
-
if (401 === resp.status) throw createAuthError.invalid("Authentication failed");
|
|
14131
|
-
if (403 === resp.status) throw createAppError.accessDenied(appId);
|
|
14132
|
-
if (404 === resp.status) throw createAppError.notFound(appId);
|
|
14133
|
-
logger_logger.debug(`validate-app-id failed: ${resp.status} ${resp.statusText}`);
|
|
14134
|
-
return {
|
|
14135
|
-
valid: false,
|
|
14136
|
-
error: data.error || `Server error: ${resp.status}`
|
|
14137
|
-
};
|
|
14138
|
-
} catch (error) {
|
|
14139
|
-
if (error.type) throw error;
|
|
14140
|
-
throw createNetworkError.network(error.message);
|
|
14301
|
+
class git_GitService extends Emitter {
|
|
14302
|
+
git;
|
|
14303
|
+
repoPath;
|
|
14304
|
+
static INDEX_LOCK_RETRY_DELAYS_MS = [
|
|
14305
|
+
25,
|
|
14306
|
+
50,
|
|
14307
|
+
100,
|
|
14308
|
+
200
|
|
14309
|
+
];
|
|
14310
|
+
constructor(repoPath){
|
|
14311
|
+
super();
|
|
14312
|
+
this.repoPath = external_path_default().resolve(repoPath);
|
|
14313
|
+
this.git = esm_default(this.repoPath);
|
|
14141
14314
|
}
|
|
14142
|
-
|
|
14143
|
-
|
|
14144
|
-
|
|
14145
|
-
|
|
14146
|
-
|
|
14147
|
-
|
|
14148
|
-
|
|
14149
|
-
|
|
14150
|
-
|
|
14151
|
-
|
|
14152
|
-
|
|
14153
|
-
|
|
14154
|
-
|
|
14155
|
-
|
|
14156
|
-
|
|
14157
|
-
if (!resp.ok) {
|
|
14158
|
-
if (401 === resp.status) throw createAuthError.invalid("Authentication failed");
|
|
14159
|
-
throw createNetworkError.server(resp.status, resp.statusText);
|
|
14315
|
+
getGit() {
|
|
14316
|
+
return this.git;
|
|
14317
|
+
}
|
|
14318
|
+
getRepoPath() {
|
|
14319
|
+
return this.repoPath;
|
|
14320
|
+
}
|
|
14321
|
+
async getGitDir() {
|
|
14322
|
+
try {
|
|
14323
|
+
const gitDir = (await this.git.revparse([
|
|
14324
|
+
"--git-dir"
|
|
14325
|
+
])).trim();
|
|
14326
|
+
if (!external_path_default().isAbsolute(gitDir)) return external_path_default().resolve(this.repoPath, gitDir);
|
|
14327
|
+
return gitDir;
|
|
14328
|
+
} catch (e) {
|
|
14329
|
+
throw errors_createGitError.commandFailed("rev-parse --git-dir", e.message);
|
|
14160
14330
|
}
|
|
14161
|
-
const data = await resp.json();
|
|
14162
|
-
return data;
|
|
14163
|
-
} catch (error) {
|
|
14164
|
-
if (error.type) throw error;
|
|
14165
|
-
throw createNetworkError.network(error.message);
|
|
14166
14331
|
}
|
|
14167
|
-
|
|
14168
|
-
|
|
14169
|
-
|
|
14170
|
-
|
|
14171
|
-
|
|
14172
|
-
|
|
14173
|
-
|
|
14174
|
-
|
|
14175
|
-
|
|
14176
|
-
|
|
14177
|
-
|
|
14178
|
-
const resp = await fetch(url, {
|
|
14179
|
-
method: "GET",
|
|
14180
|
-
headers: {
|
|
14181
|
-
Authorization: `Bearer ${authToken}`
|
|
14182
|
-
},
|
|
14183
|
-
signal: options.signal
|
|
14184
|
-
});
|
|
14185
|
-
if (!resp.ok) {
|
|
14186
|
-
if (401 === resp.status) throw createAuthError.invalid("Authentication failed");
|
|
14187
|
-
throw createNetworkError.server(resp.status, resp.statusText);
|
|
14332
|
+
async getCurrentBranch() {
|
|
14333
|
+
try {
|
|
14334
|
+
const branchRef = await this.git.revparse([
|
|
14335
|
+
"--abbrev-ref",
|
|
14336
|
+
"HEAD"
|
|
14337
|
+
]);
|
|
14338
|
+
if ("HEAD" === branchRef) throw createSyncError.detachedHead();
|
|
14339
|
+
return branchRef;
|
|
14340
|
+
} catch (e) {
|
|
14341
|
+
if ("detached_head" === e.type) throw e;
|
|
14342
|
+
throw createSyncError.detachedHead();
|
|
14188
14343
|
}
|
|
14189
|
-
const data = await resp.json();
|
|
14190
|
-
return {
|
|
14191
|
-
apps: Array.isArray(data.apps) ? data.apps : [],
|
|
14192
|
-
next_cursor: "string" == typeof data.next_cursor ? data.next_cursor : null
|
|
14193
|
-
};
|
|
14194
|
-
} catch (error) {
|
|
14195
|
-
if (error.type) throw error;
|
|
14196
|
-
throw createNetworkError.network(error.message);
|
|
14197
14344
|
}
|
|
14198
|
-
|
|
14199
|
-
|
|
14200
|
-
|
|
14201
|
-
|
|
14202
|
-
|
|
14203
|
-
|
|
14204
|
-
|
|
14205
|
-
|
|
14206
|
-
|
|
14207
|
-
body: JSON.stringify(payload)
|
|
14208
|
-
});
|
|
14209
|
-
const data = await resp.json();
|
|
14210
|
-
if (401 === resp.status) throw createAuthError.invalid(`Authentication failed (401): ${data.error || "unauthorized"}`);
|
|
14211
|
-
if (403 === resp.status) throw createAuthError.invalid(`Authentication failed (403): ${data.error || "unauthorized"}\n This may indicate:\n - The OAuth token doesn't have permission to write to app ${appId}\n - The app belongs to a different user or organization\n - The OAuth token is missing required scopes (apps:write)`);
|
|
14212
|
-
return {
|
|
14213
|
-
version: data.version,
|
|
14214
|
-
overtaken: data.overtaken,
|
|
14215
|
-
latest_version: data.latest_version,
|
|
14216
|
-
error: 200 !== resp.status ? data.error || `Status ${resp.status}` : void 0
|
|
14217
|
-
};
|
|
14218
|
-
} catch (error) {
|
|
14219
|
-
if (error.type) throw error;
|
|
14220
|
-
throw createNetworkError.network(error.message);
|
|
14221
|
-
}
|
|
14222
|
-
}
|
|
14223
|
-
function anvil_api_getGitFetchUrl(appId, authToken, gitUrl = anvil_api_getDefaultAnvilUrl()) {
|
|
14224
|
-
const url = new URL(gitUrl);
|
|
14225
|
-
const encodedToken = encodeURIComponent(authToken);
|
|
14226
|
-
return `${url.protocol}//git:${encodedToken}@${url.hostname}${url.port ? ":" + url.port : ""}/git/${appId}.git?no_freeze=true&q=`;
|
|
14227
|
-
}
|
|
14228
|
-
function anvil_api_getGitPushUrl(appId, authToken, anvilUrl = anvil_api_getDefaultAnvilUrl()) {
|
|
14229
|
-
const url = new URL(anvilUrl);
|
|
14230
|
-
const encodedToken = encodeURIComponent(authToken);
|
|
14231
|
-
return `${url.protocol}//git:${encodedToken}@${url.hostname}${url.port ? ":" + url.port : ""}/git/${appId}.git`;
|
|
14232
|
-
}
|
|
14233
|
-
function getWebSocketUrl(appId, authToken, anvilUrl = anvil_api_getDefaultAnvilUrl()) {
|
|
14234
|
-
return anvilUrl.replace(/^http/, "ws") + `/ide/api/_/apps/${appId}/ws?access_token=${authToken}`;
|
|
14235
|
-
}
|
|
14236
|
-
async function getLatestVersion() {
|
|
14237
|
-
try {
|
|
14238
|
-
const response = await fetch("https://registry.npmjs.org/@anvil-works/anvil-cli/latest");
|
|
14239
|
-
if (!response.ok) return null;
|
|
14240
|
-
const data = await response.json();
|
|
14241
|
-
return data.version;
|
|
14242
|
-
} catch (e) {
|
|
14243
|
-
return null;
|
|
14244
|
-
}
|
|
14245
|
-
}
|
|
14246
|
-
function anvil_api_filterCandidates(candidates, explicitUrl, explicitUsername) {
|
|
14247
|
-
let filtered = candidates;
|
|
14248
|
-
if (explicitUrl) {
|
|
14249
|
-
const normalizedExplicit = config_normalizeAnvilUrl(explicitUrl);
|
|
14250
|
-
filtered = filtered.filter((c)=>c.detectedUrl && config_normalizeAnvilUrl(c.detectedUrl) === normalizedExplicit);
|
|
14251
|
-
}
|
|
14252
|
-
if (explicitUsername) filtered = filtered.filter((c)=>!c.detectedUsername || c.detectedUsername === explicitUsername);
|
|
14253
|
-
return filtered;
|
|
14254
|
-
}
|
|
14255
|
-
function anvil_api_formatCandidateLabel(candidate) {
|
|
14256
|
-
const parts = [
|
|
14257
|
-
candidate.appId
|
|
14258
|
-
];
|
|
14259
|
-
if (candidate.detectedUrl) if (candidate.detectedUsername) parts.push(`(${candidate.detectedUsername} on ${candidate.detectedUrl})`);
|
|
14260
|
-
else parts.push(`(${candidate.detectedUrl})`);
|
|
14261
|
-
parts.push(`- ${candidate.description}`);
|
|
14262
|
-
return parts.join(" ");
|
|
14263
|
-
}
|
|
14264
|
-
function anvil_api_lookupRemoteInfoForAppId(appId, detectedRemotes) {
|
|
14265
|
-
const matches = detectedRemotes.filter((c)=>c.appId === appId);
|
|
14266
|
-
if (0 === matches.length) return {};
|
|
14267
|
-
const withUsername = matches.find((c)=>c.detectedUsername);
|
|
14268
|
-
if (withUsername) return {
|
|
14269
|
-
detectedUrl: withUsername.detectedUrl,
|
|
14270
|
-
detectedUsername: withUsername.detectedUsername
|
|
14271
|
-
};
|
|
14272
|
-
return {
|
|
14273
|
-
detectedUrl: matches[0].detectedUrl,
|
|
14274
|
-
detectedUsername: matches[0].detectedUsername
|
|
14275
|
-
};
|
|
14276
|
-
}
|
|
14277
|
-
async function anvil_api_detectAppIdsFromAllRemotes(repoPath) {
|
|
14278
|
-
const git = esm_default(repoPath);
|
|
14279
|
-
const out = [];
|
|
14280
|
-
try {
|
|
14281
|
-
const remotes = await git.getRemotes(true);
|
|
14282
|
-
for (const remote of remotes){
|
|
14283
|
-
const httpMatch = remote.refs.fetch?.match(/(?:http|https):\/\/(?:[^@]+@)?([^:\/]+)(?::\d+)?\/git\/([A-Z0-9]+)\.git/);
|
|
14284
|
-
if (httpMatch) {
|
|
14285
|
-
const [, host, detectedAppId] = httpMatch;
|
|
14286
|
-
out.push({
|
|
14287
|
-
appId: detectedAppId,
|
|
14288
|
-
source: "remote",
|
|
14289
|
-
description: `Git remote '${remote.name}'`,
|
|
14290
|
-
detectedUrl: config_normalizeAnvilUrl(host)
|
|
14291
|
-
});
|
|
14292
|
-
continue;
|
|
14293
|
-
}
|
|
14294
|
-
const sshMatch = remote.refs.fetch?.match(/ssh:\/\/([^@]+)@([^:]+):(\d+)\/(?:git\/)?([A-Z0-9]+)\.git/);
|
|
14295
|
-
if (sshMatch) {
|
|
14296
|
-
const [, usernamePart, host, , detectedAppId] = sshMatch;
|
|
14297
|
-
const detectedUsername = usernamePart.includes("%") ? decodeURIComponent(usernamePart) : usernamePart;
|
|
14298
|
-
const finalUsername = "git" !== detectedUsername ? detectedUsername : void 0;
|
|
14299
|
-
out.push({
|
|
14300
|
-
appId: detectedAppId,
|
|
14301
|
-
source: "remote",
|
|
14302
|
-
description: `Git remote '${remote.name}' (SSH)`,
|
|
14303
|
-
detectedUrl: config_normalizeAnvilUrl(host),
|
|
14304
|
-
detectedUsername: finalUsername
|
|
14305
|
-
});
|
|
14306
|
-
}
|
|
14307
|
-
}
|
|
14308
|
-
} catch (_e) {}
|
|
14309
|
-
return out;
|
|
14310
|
-
}
|
|
14311
|
-
async function anvil_api_detectAppIdsByCommitLookup(repoPath, options) {
|
|
14312
|
-
const anvilUrl = options.anvilUrl || resolveAnvilUrl();
|
|
14313
|
-
const username = options.username;
|
|
14314
|
-
const git = esm_default(repoPath);
|
|
14315
|
-
const out = [];
|
|
14316
|
-
try {
|
|
14317
|
-
const authToken = await auth_getValidAuthToken(anvilUrl, username);
|
|
14318
|
-
const branchRef = await git.revparse([
|
|
14319
|
-
"--abbrev-ref",
|
|
14320
|
-
"HEAD"
|
|
14321
|
-
]);
|
|
14322
|
-
const currentBranch = "HEAD" === branchRef ? "master" : branchRef;
|
|
14323
|
-
const commitId = (await git.revparse([
|
|
14324
|
-
"HEAD"
|
|
14325
|
-
]))?.trim();
|
|
14326
|
-
if (!commitId) return out;
|
|
14327
|
-
const resp = await fetch(`${anvilUrl}/ide/api/_/apps/lookup-by-commit`, {
|
|
14328
|
-
method: "POST",
|
|
14329
|
-
headers: {
|
|
14330
|
-
Authorization: `Bearer ${authToken}`,
|
|
14331
|
-
"Content-Type": "application/json"
|
|
14332
|
-
},
|
|
14333
|
-
body: JSON.stringify({
|
|
14334
|
-
commit_id: commitId,
|
|
14335
|
-
branch_name: currentBranch
|
|
14336
|
-
})
|
|
14337
|
-
});
|
|
14338
|
-
logger_logger.debug(`Reverse lookup by commit ${commitId} on branch ${currentBranch} returned ${resp.status}`);
|
|
14339
|
-
if (!resp.ok) {
|
|
14340
|
-
const errorBody = await resp.text();
|
|
14341
|
-
logger_logger.debug(`lookup-by-commit error: ${errorBody}`);
|
|
14342
|
-
}
|
|
14343
|
-
if (resp.ok) {
|
|
14344
|
-
const data = await resp.json();
|
|
14345
|
-
logger_logger.debug(`data ${JSON.stringify(data)}`);
|
|
14346
|
-
const apps = data?.apps ?? [];
|
|
14347
|
-
for (const app of apps)if (app?.app_id) out.push({
|
|
14348
|
-
appId: app.app_id,
|
|
14349
|
-
source: "remote",
|
|
14350
|
-
description: `Reverse lookup by commit${app.branch ? ` on branch '${app.branch}'` : ""}`
|
|
14351
|
-
});
|
|
14352
|
-
}
|
|
14353
|
-
} catch (_e) {}
|
|
14354
|
-
return out;
|
|
14355
|
-
}
|
|
14356
|
-
const promises_namespaceObject = require("fs/promises");
|
|
14357
|
-
var promises_default = /*#__PURE__*/ __webpack_require__.n(promises_namespaceObject);
|
|
14358
|
-
function normalizeLineEndings(content) {
|
|
14359
|
-
return content.replace(/\r\n/g, "\n");
|
|
14360
|
-
}
|
|
14361
|
-
function pythonifyName(name) {
|
|
14362
|
-
return name.replace(/[^A-z0-9]/g, "_").replace(/^[0-9]/, "_$&");
|
|
14363
|
-
}
|
|
14364
|
-
function extractPythonName(relativePath, skipParts = 1) {
|
|
14365
|
-
const parts = relativePath.slice(0, -3).split("/");
|
|
14366
|
-
const isPackage = relativePath.endsWith("__init__.py");
|
|
14367
|
-
const relevantParts = isPackage ? parts.slice(skipParts, -1) : parts.slice(skipParts);
|
|
14368
|
-
return relevantParts.map(pythonifyName).join(".");
|
|
14369
|
-
}
|
|
14370
|
-
function deepEqual(a, b) {
|
|
14371
|
-
if (a === b) return true;
|
|
14372
|
-
if (null == a || null == b) return false;
|
|
14373
|
-
if (typeof a !== typeof b) return false;
|
|
14374
|
-
if ("object" != typeof a) return a === b;
|
|
14375
|
-
if (Array.isArray(a) !== Array.isArray(b)) return false;
|
|
14376
|
-
if (Array.isArray(a)) {
|
|
14377
|
-
if (a.length !== b.length) return false;
|
|
14378
|
-
return a.every((item, index)=>deepEqual(item, b[index]));
|
|
14379
|
-
}
|
|
14380
|
-
const keysA = Object.keys(a);
|
|
14381
|
-
const keysB = Object.keys(b);
|
|
14382
|
-
if (keysA.length !== keysB.length) return false;
|
|
14383
|
-
return keysA.every((key)=>deepEqual(a[key], b[key]));
|
|
14384
|
-
}
|
|
14385
|
-
function parseHtmlWithFrontmatter(htmlContent) {
|
|
14386
|
-
const frontmatterPattern = /^---\s*\n([\s\S]*?)\n---\s*\n([\s\S]*)$/;
|
|
14387
|
-
const match = htmlContent.match(frontmatterPattern);
|
|
14388
|
-
if (match) {
|
|
14389
|
-
const yamlStr = match[1];
|
|
14390
|
-
const html = match[2];
|
|
14391
|
-
const frontmatter = yamlStr ? external_js_yaml_default().load(yamlStr) ?? {} : {};
|
|
14392
|
-
return {
|
|
14393
|
-
frontmatter,
|
|
14394
|
-
html
|
|
14395
|
-
};
|
|
14396
|
-
}
|
|
14397
|
-
return {
|
|
14398
|
-
frontmatter: {},
|
|
14399
|
-
html: htmlContent
|
|
14400
|
-
};
|
|
14401
|
-
}
|
|
14402
|
-
const ANVIL_GITIGNORE_ENTRY = "/.anvil/";
|
|
14403
|
-
async function ensureProjectGitignoreIgnoresAnvilDir(projectRoot) {
|
|
14404
|
-
const rootGitignorePath = external_path_default().join(projectRoot, ".gitignore");
|
|
14405
|
-
try {
|
|
14406
|
-
const existing = await external_fs_.promises.readFile(rootGitignorePath, "utf8");
|
|
14407
|
-
if (existing.includes(ANVIL_GITIGNORE_ENTRY)) return false;
|
|
14408
|
-
const prefix = existing.endsWith("\n") || 0 === existing.length ? "" : "\n";
|
|
14409
|
-
await external_fs_.promises.writeFile(rootGitignorePath, `${existing}${prefix}${ANVIL_GITIGNORE_ENTRY}\n`, "utf8");
|
|
14410
|
-
return true;
|
|
14411
|
-
} catch (error) {
|
|
14412
|
-
if ("ENOENT" !== error.code) throw error;
|
|
14413
|
-
await external_fs_.promises.writeFile(rootGitignorePath, `${ANVIL_GITIGNORE_ENTRY}\n`, "utf8");
|
|
14414
|
-
return true;
|
|
14415
|
-
}
|
|
14416
|
-
}
|
|
14417
|
-
const ANVIL_DEPS_DIR = ".anvil/deps";
|
|
14418
|
-
const ANVIL_DEPS_MANIFEST = "manifest.json";
|
|
14419
|
-
const DEPS_MANIFEST_VERSION = 1;
|
|
14420
|
-
function formatFetchError(prefix, status, errorBody) {
|
|
14421
|
-
if (!errorBody) return `${prefix}: ${status}`;
|
|
14422
|
-
try {
|
|
14423
|
-
const parsed = JSON.parse(errorBody);
|
|
14424
|
-
if ("string" == typeof parsed.error && parsed.error.trim()) return `${prefix}: ${parsed.error} (${status})`;
|
|
14425
|
-
} catch {}
|
|
14426
|
-
return `${prefix}: ${status} ${errorBody}`;
|
|
14427
|
-
}
|
|
14428
|
-
async function fetchAllDependencies(appId, anvilUrl = resolveAnvilUrl(), options = {}) {
|
|
14429
|
-
const { commit, includeDocs = true, knownDependencies = [] } = options;
|
|
14430
|
-
const token = await auth_getValidAuthToken(anvilUrl);
|
|
14431
|
-
const body = {
|
|
14432
|
-
include_docs: includeDocs,
|
|
14433
|
-
known_dependencies: knownDependencies
|
|
14434
|
-
};
|
|
14435
|
-
if (commit) body.commit = commit;
|
|
14436
|
-
const resp = await fetch(`${anvilUrl}/ide/api/_/apps/${appId}/resolved-dependencies`, {
|
|
14437
|
-
method: "POST",
|
|
14438
|
-
headers: {
|
|
14439
|
-
Authorization: `Bearer ${token}`,
|
|
14440
|
-
"Content-Type": "application/json"
|
|
14441
|
-
},
|
|
14442
|
-
body: JSON.stringify(body)
|
|
14443
|
-
});
|
|
14444
|
-
if (!resp.ok) {
|
|
14445
|
-
const errorBody = await resp.text();
|
|
14446
|
-
throw new Error(formatFetchError("Failed to fetch dependencies", resp.status, errorBody));
|
|
14447
|
-
}
|
|
14448
|
-
return await resp.json();
|
|
14449
|
-
}
|
|
14450
|
-
async function refreshDependencyCache(projectRoot, appId, anvilUrl = resolveAnvilUrl(), options = {}) {
|
|
14451
|
-
const includeDocs = options.includeDocs ?? true;
|
|
14452
|
-
const knownDependencies = await getKnownDependenciesForCache(projectRoot, includeDocs);
|
|
14453
|
-
const response = await fetchAllDependencies(appId, anvilUrl, {
|
|
14454
|
-
commit: options.commit,
|
|
14455
|
-
includeDocs,
|
|
14456
|
-
knownDependencies
|
|
14457
|
-
});
|
|
14458
|
-
if (!response.resolved_dependencies.length) {
|
|
14459
|
-
await clearDepsCache(projectRoot);
|
|
14460
|
-
return {
|
|
14461
|
-
response,
|
|
14462
|
-
addedGitignoreEntry: false
|
|
14463
|
-
};
|
|
14464
|
-
}
|
|
14465
|
-
const addedGitignoreEntry = await ensureProjectGitignoreIgnoresAnvilDir(projectRoot);
|
|
14466
|
-
await writeAllDepsToCache(projectRoot, response, {
|
|
14467
|
-
includeDocs
|
|
14468
|
-
});
|
|
14469
|
-
return {
|
|
14470
|
-
response,
|
|
14471
|
-
addedGitignoreEntry
|
|
14472
|
-
};
|
|
14473
|
-
}
|
|
14474
|
-
function depsDir(projectRoot) {
|
|
14475
|
-
return external_path_default().join(projectRoot, ANVIL_DEPS_DIR);
|
|
14476
|
-
}
|
|
14477
|
-
function depsManifestPath(projectRoot) {
|
|
14478
|
-
return external_path_default().join(depsDir(projectRoot), ANVIL_DEPS_MANIFEST);
|
|
14479
|
-
}
|
|
14480
|
-
function isResolvedDependencyManifestEntry(value) {
|
|
14481
|
-
if (!value || "object" != typeof value) return false;
|
|
14482
|
-
const entry = value;
|
|
14483
|
-
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);
|
|
14484
|
-
}
|
|
14485
|
-
function isCachedDependencyManifestEntry(value) {
|
|
14486
|
-
if (!isResolvedDependencyManifestEntry(value)) return false;
|
|
14487
|
-
const entry = value;
|
|
14488
|
-
return "string" == typeof entry.package_name && entry.package_name.length > 0;
|
|
14489
|
-
}
|
|
14490
|
-
function isDepsCacheManifest(value) {
|
|
14491
|
-
if (!value || "object" != typeof value) return false;
|
|
14492
|
-
const manifest = value;
|
|
14493
|
-
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);
|
|
14494
|
-
}
|
|
14495
|
-
async function readDepsCacheManifest(projectRoot) {
|
|
14496
|
-
try {
|
|
14497
|
-
const raw = await promises_default().readFile(depsManifestPath(projectRoot), "utf-8");
|
|
14498
|
-
const parsed = JSON.parse(raw);
|
|
14499
|
-
return isDepsCacheManifest(parsed) ? parsed : void 0;
|
|
14500
|
-
} catch {
|
|
14501
|
-
return;
|
|
14502
|
-
}
|
|
14503
|
-
}
|
|
14504
|
-
async function getKnownDependenciesForCache(projectRoot, includeDocs) {
|
|
14505
|
-
const manifest = await readDepsCacheManifest(projectRoot);
|
|
14506
|
-
if (!manifest || manifest.includeDocs !== includeDocs) return [];
|
|
14507
|
-
return manifest.resolved_dependencies.map(({ app_id, commit_id })=>({
|
|
14508
|
-
app_id,
|
|
14509
|
-
commit_id
|
|
14510
|
-
}));
|
|
14511
|
-
}
|
|
14512
|
-
function getDependencyWatchSubscriptionsFromManifest(manifest) {
|
|
14513
|
-
if (!manifest) return [];
|
|
14514
|
-
const cachedByAppId = new Map(manifest.cached_dependencies.map((dep)=>[
|
|
14515
|
-
dep.app_id,
|
|
14516
|
-
dep
|
|
14517
|
-
]));
|
|
14518
|
-
return manifest.resolved_dependencies.flatMap((dep)=>{
|
|
14519
|
-
if (!dep.branch) return [];
|
|
14520
|
-
return [
|
|
14521
|
-
{
|
|
14522
|
-
appId: dep.app_id,
|
|
14523
|
-
branch: dep.branch,
|
|
14524
|
-
packageName: cachedByAppId.get(dep.app_id)?.package_name
|
|
14525
|
-
}
|
|
14526
|
-
];
|
|
14527
|
-
});
|
|
14528
|
-
}
|
|
14529
|
-
function normalizeAnvilYamlDependenciesFromContent(content) {
|
|
14530
|
-
const parsed = external_js_yaml_default().load(content);
|
|
14531
|
-
if (!parsed || "object" != typeof parsed || !Array.isArray(parsed.dependencies)) return [];
|
|
14532
|
-
return normalizeAnvilYamlDependenciesValue(parsed.dependencies);
|
|
14533
|
-
}
|
|
14534
|
-
function normalizeAnvilYamlDependenciesValue(value) {
|
|
14535
|
-
if (!Array.isArray(value)) return [];
|
|
14536
|
-
return normalizeForComparison(value);
|
|
14537
|
-
}
|
|
14538
|
-
async function readAnvilYamlDependencies(projectRoot) {
|
|
14539
|
-
try {
|
|
14540
|
-
const content = await promises_default().readFile(external_path_default().join(projectRoot, "anvil.yaml"), "utf-8");
|
|
14541
|
-
return normalizeAnvilYamlDependenciesFromContent(content);
|
|
14542
|
-
} catch {
|
|
14543
|
-
return [];
|
|
14544
|
-
}
|
|
14545
|
-
}
|
|
14546
|
-
function anvilYamlDependenciesEqual(left, right) {
|
|
14547
|
-
return deepEqual(left, right);
|
|
14548
|
-
}
|
|
14549
|
-
function normalizeForComparison(value) {
|
|
14550
|
-
if (Array.isArray(value)) return value.map(normalizeForComparison);
|
|
14551
|
-
if (value && "object" == typeof value) return Object.fromEntries(Object.entries(value).sort(([left], [right])=>left.localeCompare(right)).map(([key, entryValue])=>[
|
|
14552
|
-
key,
|
|
14553
|
-
normalizeForComparison(entryValue)
|
|
14554
|
-
]));
|
|
14555
|
-
return value;
|
|
14556
|
-
}
|
|
14557
|
-
function cachedManifestEntry(dep, packageName) {
|
|
14558
|
-
return {
|
|
14559
|
-
app_id: dep.app_id,
|
|
14560
|
-
commit_id: dep.commit_id,
|
|
14561
|
-
...dep.branch ? {
|
|
14562
|
-
branch: dep.branch
|
|
14563
|
-
} : {},
|
|
14564
|
-
package_name: packageName
|
|
14565
|
-
};
|
|
14566
|
-
}
|
|
14567
|
-
function safeDependencyPath(filePath, kind) {
|
|
14568
|
-
const normalizedPath = external_path_default().normalize(filePath);
|
|
14569
|
-
if (external_path_default().isAbsolute(normalizedPath) || normalizedPath.startsWith("..")) throw new Error(`Invalid dependency ${kind} path: ${filePath}`);
|
|
14570
|
-
return normalizedPath;
|
|
14571
|
-
}
|
|
14572
|
-
async function writeDepToCache(projectRoot, dep) {
|
|
14573
|
-
const depDir = external_path_default().join(projectRoot, ANVIL_DEPS_DIR, dep.package_name);
|
|
14574
|
-
await promises_default().rm(depDir, {
|
|
14575
|
-
recursive: true,
|
|
14576
|
-
force: true
|
|
14577
|
-
});
|
|
14578
|
-
await promises_default().mkdir(external_path_default().join(depDir, "client_code"), {
|
|
14579
|
-
recursive: true
|
|
14580
|
-
});
|
|
14581
|
-
await promises_default().mkdir(external_path_default().join(depDir, "server_code"), {
|
|
14582
|
-
recursive: true
|
|
14583
|
-
});
|
|
14584
|
-
await promises_default().writeFile(external_path_default().join(depDir, "anvil.yaml"), external_js_yaml_default().dump({
|
|
14585
|
-
package_name: dep.package_name
|
|
14586
|
-
}));
|
|
14587
|
-
for (const mod of dep.modules ?? [])if (mod.is_package) {
|
|
14588
|
-
const modDir = external_path_default().join(depDir, "client_code", mod.name);
|
|
14589
|
-
await promises_default().mkdir(modDir, {
|
|
14590
|
-
recursive: true
|
|
14591
|
-
});
|
|
14592
|
-
await promises_default().writeFile(external_path_default().join(modDir, "__init__.py"), mod.code);
|
|
14593
|
-
} else await promises_default().writeFile(external_path_default().join(depDir, "client_code", `${mod.name}.py`), mod.code);
|
|
14594
|
-
for (const form of dep.forms ?? []){
|
|
14595
|
-
const { code, class_name, is_package, ...formTemplate } = form;
|
|
14596
|
-
if (is_package) {
|
|
14597
|
-
const formDir = external_path_default().join(depDir, "client_code", class_name);
|
|
14598
|
-
await promises_default().mkdir(formDir, {
|
|
14599
|
-
recursive: true
|
|
14600
|
-
});
|
|
14601
|
-
await promises_default().writeFile(external_path_default().join(formDir, "__init__.py"), code);
|
|
14602
|
-
await promises_default().writeFile(external_path_default().join(formDir, "form_template.yaml"), external_js_yaml_default().dump(formTemplate));
|
|
14603
|
-
} else {
|
|
14604
|
-
await promises_default().writeFile(external_path_default().join(depDir, "client_code", `${class_name}.py`), code);
|
|
14605
|
-
await promises_default().writeFile(external_path_default().join(depDir, "client_code", `${class_name}.yaml`), external_js_yaml_default().dump(formTemplate));
|
|
14606
|
-
}
|
|
14607
|
-
}
|
|
14608
|
-
for (const mod of dep.server_modules ?? [])if (mod.is_package) {
|
|
14609
|
-
const modDir = external_path_default().join(depDir, "server_code", mod.name);
|
|
14610
|
-
await promises_default().mkdir(modDir, {
|
|
14611
|
-
recursive: true
|
|
14612
|
-
});
|
|
14613
|
-
await promises_default().writeFile(external_path_default().join(modDir, "__init__.py"), mod.code);
|
|
14614
|
-
} else await promises_default().writeFile(external_path_default().join(depDir, "server_code", `${mod.name}.py`), mod.code);
|
|
14615
|
-
for (const asset of dep.assets ?? []){
|
|
14616
|
-
const targetPath = external_path_default().join(depDir, "theme", "assets", safeDependencyPath(asset.name, "asset"));
|
|
14617
|
-
await promises_default().mkdir(external_path_default().dirname(targetPath), {
|
|
14618
|
-
recursive: true
|
|
14619
|
-
});
|
|
14620
|
-
await promises_default().writeFile(targetPath, Buffer.from(asset.content, "base64"));
|
|
14621
|
-
}
|
|
14622
|
-
for (const doc of dep.docs ?? []){
|
|
14623
|
-
const targetPath = external_path_default().join(depDir, safeDependencyPath(doc.path, "doc"));
|
|
14624
|
-
await promises_default().mkdir(external_path_default().dirname(targetPath), {
|
|
14625
|
-
recursive: true
|
|
14626
|
-
});
|
|
14627
|
-
await promises_default().writeFile(targetPath, doc.content);
|
|
14628
|
-
}
|
|
14629
|
-
}
|
|
14630
|
-
async function writeAllDepsToCache(projectRoot, response, options = {}) {
|
|
14631
|
-
const includeDocs = options.includeDocs ?? true;
|
|
14632
|
-
const manifest = await readDepsCacheManifest(projectRoot);
|
|
14633
|
-
const canReuseExistingCache = manifest?.includeDocs === includeDocs;
|
|
14634
|
-
if (!canReuseExistingCache) await clearDepsCache(projectRoot);
|
|
14635
|
-
const previousCached = canReuseExistingCache ? manifest.cached_dependencies : [];
|
|
14636
|
-
const resolvedAppIds = new Set(response.resolved_dependencies.map((dep)=>dep.app_id));
|
|
14637
|
-
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), {
|
|
14638
|
-
recursive: true,
|
|
14639
|
-
force: true
|
|
14640
|
-
});
|
|
14641
|
-
for (const dep of response.dependencies)await writeDepToCache(projectRoot, dep);
|
|
14642
|
-
const returnedByAppId = new Map(response.dependencies.map((dep)=>[
|
|
14643
|
-
dep.app_id,
|
|
14644
|
-
dep
|
|
14645
|
-
]));
|
|
14646
|
-
const previousByAppId = new Map(previousCached.map((dep)=>[
|
|
14647
|
-
dep.app_id,
|
|
14648
|
-
dep
|
|
14649
|
-
]));
|
|
14650
|
-
const cachedDependencies = response.resolved_dependencies.map((dep)=>{
|
|
14651
|
-
const returned = returnedByAppId.get(dep.app_id);
|
|
14652
|
-
if (returned) return cachedManifestEntry(returned, returned.package_name);
|
|
14653
|
-
const previous = previousByAppId.get(dep.app_id);
|
|
14654
|
-
if (!previous) return;
|
|
14655
|
-
return cachedManifestEntry(dep, previous.package_name);
|
|
14656
|
-
}).filter((dep)=>void 0 !== dep);
|
|
14657
|
-
const newManifest = {
|
|
14658
|
-
version: DEPS_MANIFEST_VERSION,
|
|
14659
|
-
includeDocs,
|
|
14660
|
-
resolved_dependencies: response.resolved_dependencies,
|
|
14661
|
-
cached_dependencies: cachedDependencies
|
|
14662
|
-
};
|
|
14663
|
-
await promises_default().mkdir(depsDir(projectRoot), {
|
|
14664
|
-
recursive: true
|
|
14665
|
-
});
|
|
14666
|
-
await promises_default().writeFile(depsManifestPath(projectRoot), `${JSON.stringify(newManifest, null, 2)}\n`);
|
|
14667
|
-
}
|
|
14668
|
-
async function clearDepsCache(projectRoot) {
|
|
14669
|
-
await promises_default().rm(depsDir(projectRoot), {
|
|
14670
|
-
recursive: true,
|
|
14671
|
-
force: true
|
|
14672
|
-
});
|
|
14673
|
-
}
|
|
14674
|
-
const external_timers_promises_namespaceObject = require("timers/promises");
|
|
14675
|
-
const WANT_NOT_VALID_RE = /\bwant\s+[0-9a-f]{7,40}\s+not valid\b/i;
|
|
14676
|
-
const FETCH_RETRY_DELAY_MS = 200;
|
|
14677
|
-
function isWantNotValidError(error) {
|
|
14678
|
-
return WANT_NOT_VALID_RE.test(error.message || "");
|
|
14679
|
-
}
|
|
14680
|
-
class git_GitService extends Emitter {
|
|
14681
|
-
git;
|
|
14682
|
-
repoPath;
|
|
14683
|
-
static INDEX_LOCK_RETRY_DELAYS_MS = [
|
|
14684
|
-
25,
|
|
14685
|
-
50,
|
|
14686
|
-
100,
|
|
14687
|
-
200
|
|
14688
|
-
];
|
|
14689
|
-
constructor(repoPath){
|
|
14690
|
-
super();
|
|
14691
|
-
this.repoPath = external_path_default().resolve(repoPath);
|
|
14692
|
-
this.git = esm_default(this.repoPath);
|
|
14693
|
-
}
|
|
14694
|
-
getGit() {
|
|
14695
|
-
return this.git;
|
|
14696
|
-
}
|
|
14697
|
-
getRepoPath() {
|
|
14698
|
-
return this.repoPath;
|
|
14699
|
-
}
|
|
14700
|
-
async getGitDir() {
|
|
14701
|
-
try {
|
|
14702
|
-
const gitDir = (await this.git.revparse([
|
|
14703
|
-
"--git-dir"
|
|
14704
|
-
])).trim();
|
|
14705
|
-
if (!external_path_default().isAbsolute(gitDir)) return external_path_default().resolve(this.repoPath, gitDir);
|
|
14706
|
-
return gitDir;
|
|
14707
|
-
} catch (e) {
|
|
14708
|
-
throw errors_createGitError.commandFailed("rev-parse --git-dir", e.message);
|
|
14709
|
-
}
|
|
14710
|
-
}
|
|
14711
|
-
async getCurrentBranch() {
|
|
14712
|
-
try {
|
|
14713
|
-
const branchRef = await this.git.revparse([
|
|
14714
|
-
"--abbrev-ref",
|
|
14715
|
-
"HEAD"
|
|
14716
|
-
]);
|
|
14717
|
-
if ("HEAD" === branchRef) throw createSyncError.detachedHead();
|
|
14718
|
-
return branchRef;
|
|
14719
|
-
} catch (e) {
|
|
14720
|
-
if ("detached_head" === e.type) throw e;
|
|
14721
|
-
throw createSyncError.detachedHead();
|
|
14722
|
-
}
|
|
14723
|
-
}
|
|
14724
|
-
async getCommitId() {
|
|
14725
|
-
try {
|
|
14726
|
-
const commitId = (await this.git.revparse([
|
|
14727
|
-
"HEAD"
|
|
14728
|
-
])).trim();
|
|
14729
|
-
return commitId;
|
|
14730
|
-
} catch (e) {
|
|
14731
|
-
throw errors_createGitError.commandFailed("revparse", e.message);
|
|
14732
|
-
}
|
|
14345
|
+
async getCommitId() {
|
|
14346
|
+
try {
|
|
14347
|
+
const commitId = (await this.git.revparse([
|
|
14348
|
+
"HEAD"
|
|
14349
|
+
])).trim();
|
|
14350
|
+
return commitId;
|
|
14351
|
+
} catch (e) {
|
|
14352
|
+
throw errors_createGitError.commandFailed("revparse", e.message);
|
|
14353
|
+
}
|
|
14733
14354
|
}
|
|
14734
14355
|
async getCommitInfo() {
|
|
14735
14356
|
try {
|
|
@@ -14767,359 +14388,987 @@ Promise.resolve(executeGitCredentialOperation(process.argv[2] || "get", {
|
|
|
14767
14388
|
renamed: status.renamed || []
|
|
14768
14389
|
};
|
|
14769
14390
|
} catch (e) {
|
|
14770
|
-
throw errors_createGitError.commandFailed("status", e.message);
|
|
14391
|
+
throw errors_createGitError.commandFailed("status", e.message);
|
|
14392
|
+
}
|
|
14393
|
+
}
|
|
14394
|
+
async discardModeOnlyChanges(paths) {
|
|
14395
|
+
const discarded = [];
|
|
14396
|
+
for (const relativePath of paths)try {
|
|
14397
|
+
const headMode = await this.getHeadFileMode(relativePath);
|
|
14398
|
+
if ("100644" !== headMode && "100755" !== headMode) continue;
|
|
14399
|
+
const contentUnchanged = await this.hasSameContentAsHead(relativePath);
|
|
14400
|
+
if (!contentUnchanged) continue;
|
|
14401
|
+
await this.restoreExecutableBit(relativePath, "100755" === headMode);
|
|
14402
|
+
discarded.push(relativePath);
|
|
14403
|
+
} catch (e) {
|
|
14404
|
+
throw errors_createGitError.commandFailed("discard mode-only changes", e.message);
|
|
14405
|
+
}
|
|
14406
|
+
return discarded;
|
|
14407
|
+
}
|
|
14408
|
+
async discardStagedModeOnlyChanges(paths) {
|
|
14409
|
+
const discarded = [];
|
|
14410
|
+
for (const relativePath of paths)try {
|
|
14411
|
+
const headMode = await this.getHeadFileMode(relativePath);
|
|
14412
|
+
if ("100644" !== headMode && "100755" !== headMode) continue;
|
|
14413
|
+
const indexContentUnchanged = await this.hasSameIndexContentAsHead(relativePath);
|
|
14414
|
+
if (!indexContentUnchanged) continue;
|
|
14415
|
+
await this.git.raw([
|
|
14416
|
+
"reset",
|
|
14417
|
+
"-q",
|
|
14418
|
+
"HEAD",
|
|
14419
|
+
"--",
|
|
14420
|
+
relativePath
|
|
14421
|
+
]);
|
|
14422
|
+
await this.restoreExecutableBit(relativePath, "100755" === headMode);
|
|
14423
|
+
discarded.push(relativePath);
|
|
14424
|
+
} catch (e) {
|
|
14425
|
+
throw errors_createGitError.commandFailed("discard staged mode-only changes", e.message);
|
|
14426
|
+
}
|
|
14427
|
+
return discarded;
|
|
14428
|
+
}
|
|
14429
|
+
async getHeadFileMode(relativePath) {
|
|
14430
|
+
const output = await this.git.raw([
|
|
14431
|
+
"ls-tree",
|
|
14432
|
+
"HEAD",
|
|
14433
|
+
"--",
|
|
14434
|
+
relativePath
|
|
14435
|
+
]);
|
|
14436
|
+
const match = output.match(/^(\d{6})\s/);
|
|
14437
|
+
return match?.[1] ?? null;
|
|
14438
|
+
}
|
|
14439
|
+
async hasSameContentAsHead(relativePath) {
|
|
14440
|
+
const headHash = (await this.git.revparse([
|
|
14441
|
+
`HEAD:${relativePath}`
|
|
14442
|
+
])).trim();
|
|
14443
|
+
const worktreeHash = (await this.git.raw([
|
|
14444
|
+
"hash-object",
|
|
14445
|
+
"--",
|
|
14446
|
+
relativePath
|
|
14447
|
+
])).trim();
|
|
14448
|
+
return headHash === worktreeHash;
|
|
14449
|
+
}
|
|
14450
|
+
async hasSameIndexContentAsHead(relativePath) {
|
|
14451
|
+
const headHash = (await this.git.revparse([
|
|
14452
|
+
`HEAD:${relativePath}`
|
|
14453
|
+
])).trim();
|
|
14454
|
+
const indexHash = (await this.git.revparse([
|
|
14455
|
+
`:${relativePath}`
|
|
14456
|
+
])).trim();
|
|
14457
|
+
return headHash === indexHash;
|
|
14458
|
+
}
|
|
14459
|
+
async restoreExecutableBit(relativePath, executable) {
|
|
14460
|
+
const filePath = external_path_default().join(this.repoPath, relativePath);
|
|
14461
|
+
const stat = await external_fs_.promises.stat(filePath);
|
|
14462
|
+
const mode = executable ? 73 | stat.mode : -74 & stat.mode;
|
|
14463
|
+
if ((511 & stat.mode) !== (511 & mode)) await external_fs_.promises.chmod(filePath, mode);
|
|
14464
|
+
}
|
|
14465
|
+
async hasUncommittedChanges() {
|
|
14466
|
+
const status = await this.getStatus();
|
|
14467
|
+
return !status.isClean;
|
|
14468
|
+
}
|
|
14469
|
+
async fetch(url, refSpec) {
|
|
14470
|
+
let attempt = 0;
|
|
14471
|
+
while(true)try {
|
|
14472
|
+
await this.git.fetch([
|
|
14473
|
+
url,
|
|
14474
|
+
refSpec
|
|
14475
|
+
]);
|
|
14476
|
+
if (attempt > 0) this.emit("fetch-retry-resolved", {
|
|
14477
|
+
attempts: attempt,
|
|
14478
|
+
outcome: "succeeded"
|
|
14479
|
+
});
|
|
14480
|
+
return;
|
|
14481
|
+
} catch (e) {
|
|
14482
|
+
const err = e;
|
|
14483
|
+
if (!isWantNotValidError(err)) {
|
|
14484
|
+
if (attempt > 0) this.emit("fetch-retry-resolved", {
|
|
14485
|
+
attempts: attempt,
|
|
14486
|
+
outcome: "failed"
|
|
14487
|
+
});
|
|
14488
|
+
throw errors_createGitError.fetchFailed(err.message);
|
|
14489
|
+
}
|
|
14490
|
+
attempt++;
|
|
14491
|
+
this.emit("fetch-retry", {
|
|
14492
|
+
attempt,
|
|
14493
|
+
delayMs: FETCH_RETRY_DELAY_MS,
|
|
14494
|
+
error: err.message
|
|
14495
|
+
});
|
|
14496
|
+
logger_logger.verbose(`git fetch race (attempt ${attempt}, sleeping ${FETCH_RETRY_DELAY_MS}ms): ${err.message}`);
|
|
14497
|
+
await (0, promises_namespaceObject.setTimeout)(FETCH_RETRY_DELAY_MS);
|
|
14498
|
+
}
|
|
14499
|
+
}
|
|
14500
|
+
async reset(ref, mode = "mixed") {
|
|
14501
|
+
await this.runGitCommandWithIndexLockRetry("reset", ()=>this.git.reset([
|
|
14502
|
+
`--${mode}`,
|
|
14503
|
+
ref
|
|
14504
|
+
]));
|
|
14505
|
+
}
|
|
14506
|
+
async checkout(paths) {
|
|
14507
|
+
await this.runGitCommandWithIndexLockRetry("checkout", ()=>this.git.checkout(paths));
|
|
14508
|
+
}
|
|
14509
|
+
async runGitCommandWithIndexLockRetry(command, operation) {
|
|
14510
|
+
let lastError;
|
|
14511
|
+
for(let attempt = 0; attempt <= git_GitService.INDEX_LOCK_RETRY_DELAYS_MS.length; attempt += 1)try {
|
|
14512
|
+
await operation();
|
|
14513
|
+
return;
|
|
14514
|
+
} catch (e) {
|
|
14515
|
+
lastError = e;
|
|
14516
|
+
if (!this.isIndexLockError(e) || attempt === git_GitService.INDEX_LOCK_RETRY_DELAYS_MS.length) throw errors_createGitError.commandFailed(command, e.message);
|
|
14517
|
+
await (0, promises_namespaceObject.setTimeout)(git_GitService.INDEX_LOCK_RETRY_DELAYS_MS[attempt]);
|
|
14518
|
+
}
|
|
14519
|
+
throw errors_createGitError.commandFailed(command, lastError.message);
|
|
14520
|
+
}
|
|
14521
|
+
isIndexLockError(error) {
|
|
14522
|
+
const message = error.message ?? "";
|
|
14523
|
+
return message.includes("Unable to create") && message.includes("index.lock") && message.includes("File exists");
|
|
14524
|
+
}
|
|
14525
|
+
async stash(message) {
|
|
14526
|
+
try {
|
|
14527
|
+
const args = [
|
|
14528
|
+
"stash",
|
|
14529
|
+
"push",
|
|
14530
|
+
"--include-untracked"
|
|
14531
|
+
];
|
|
14532
|
+
if (message) args.push("-m", message);
|
|
14533
|
+
const result = await this.git.raw(args);
|
|
14534
|
+
return !result.includes("No local changes to save");
|
|
14535
|
+
} catch (e) {
|
|
14536
|
+
throw errors_createGitError.commandFailed("stash", e.message);
|
|
14537
|
+
}
|
|
14538
|
+
}
|
|
14539
|
+
async stashPop() {
|
|
14540
|
+
try {
|
|
14541
|
+
await this.git.raw([
|
|
14542
|
+
"stash",
|
|
14543
|
+
"pop"
|
|
14544
|
+
]);
|
|
14545
|
+
} catch (e) {
|
|
14546
|
+
throw errors_createGitError.commandFailed("stash pop", e.message);
|
|
14547
|
+
}
|
|
14548
|
+
}
|
|
14549
|
+
async clean(files) {
|
|
14550
|
+
try {
|
|
14551
|
+
await this.git.clean(CleanOptions.FORCE, files);
|
|
14552
|
+
} catch (e) {
|
|
14553
|
+
throw errors_createGitError.commandFailed("clean", e.message);
|
|
14554
|
+
}
|
|
14555
|
+
}
|
|
14556
|
+
async diffNames(fromCommit, toCommit) {
|
|
14557
|
+
try {
|
|
14558
|
+
const diffResult = await this.git.diff([
|
|
14559
|
+
fromCommit,
|
|
14560
|
+
toCommit,
|
|
14561
|
+
"--name-only"
|
|
14562
|
+
]);
|
|
14563
|
+
const files = diffResult.split("\n").filter((f)=>f.trim());
|
|
14564
|
+
return files;
|
|
14565
|
+
} catch (e) {
|
|
14566
|
+
throw errors_createGitError.commandFailed("diff", e.message);
|
|
14567
|
+
}
|
|
14568
|
+
}
|
|
14569
|
+
async getAheadBehind(localRef, remoteRef) {
|
|
14570
|
+
try {
|
|
14571
|
+
const result = await this.git.raw([
|
|
14572
|
+
"rev-list",
|
|
14573
|
+
"--left-right",
|
|
14574
|
+
"--count",
|
|
14575
|
+
`${remoteRef}...${localRef}`
|
|
14576
|
+
]);
|
|
14577
|
+
const [behind, ahead] = result.trim().split("\t").map(Number);
|
|
14578
|
+
return {
|
|
14579
|
+
ahead,
|
|
14580
|
+
behind
|
|
14581
|
+
};
|
|
14582
|
+
} catch (e) {
|
|
14583
|
+
throw errors_createGitError.commandFailed("rev-list", e.message);
|
|
14584
|
+
}
|
|
14585
|
+
}
|
|
14586
|
+
async show(refPath) {
|
|
14587
|
+
try {
|
|
14588
|
+
const content = await this.git.show([
|
|
14589
|
+
refPath
|
|
14590
|
+
]);
|
|
14591
|
+
return content;
|
|
14592
|
+
} catch (e) {
|
|
14593
|
+
throw errors_createGitError.commandFailed("show", e.message);
|
|
14594
|
+
}
|
|
14595
|
+
}
|
|
14596
|
+
async push(url, refSpec, force = false) {
|
|
14597
|
+
try {
|
|
14598
|
+
const args = force ? [
|
|
14599
|
+
"push",
|
|
14600
|
+
"--force",
|
|
14601
|
+
url,
|
|
14602
|
+
refSpec
|
|
14603
|
+
] : [
|
|
14604
|
+
"push",
|
|
14605
|
+
url,
|
|
14606
|
+
refSpec
|
|
14607
|
+
];
|
|
14608
|
+
await this.git.raw(args);
|
|
14609
|
+
} catch (e) {
|
|
14610
|
+
throw errors_createGitError.commandFailed("push", e.message);
|
|
14771
14611
|
}
|
|
14772
14612
|
}
|
|
14773
|
-
async
|
|
14774
|
-
|
|
14775
|
-
|
|
14776
|
-
|
|
14777
|
-
|
|
14778
|
-
|
|
14779
|
-
if (!contentUnchanged) continue;
|
|
14780
|
-
await this.restoreExecutableBit(relativePath, "100755" === headMode);
|
|
14781
|
-
discarded.push(relativePath);
|
|
14613
|
+
async rebase(onto) {
|
|
14614
|
+
try {
|
|
14615
|
+
await this.git.raw([
|
|
14616
|
+
"rebase",
|
|
14617
|
+
onto
|
|
14618
|
+
]);
|
|
14782
14619
|
} catch (e) {
|
|
14783
|
-
throw errors_createGitError.commandFailed("
|
|
14620
|
+
throw errors_createGitError.commandFailed("rebase", e.message);
|
|
14784
14621
|
}
|
|
14785
|
-
return discarded;
|
|
14786
14622
|
}
|
|
14787
|
-
async
|
|
14788
|
-
|
|
14789
|
-
for (const relativePath of paths)try {
|
|
14790
|
-
const headMode = await this.getHeadFileMode(relativePath);
|
|
14791
|
-
if ("100644" !== headMode && "100755" !== headMode) continue;
|
|
14792
|
-
const indexContentUnchanged = await this.hasSameIndexContentAsHead(relativePath);
|
|
14793
|
-
if (!indexContentUnchanged) continue;
|
|
14623
|
+
async rebaseAbort() {
|
|
14624
|
+
try {
|
|
14794
14625
|
await this.git.raw([
|
|
14795
|
-
"
|
|
14796
|
-
"
|
|
14797
|
-
"HEAD",
|
|
14798
|
-
"--",
|
|
14799
|
-
relativePath
|
|
14626
|
+
"rebase",
|
|
14627
|
+
"--abort"
|
|
14800
14628
|
]);
|
|
14801
|
-
await this.restoreExecutableBit(relativePath, "100755" === headMode);
|
|
14802
|
-
discarded.push(relativePath);
|
|
14803
14629
|
} catch (e) {
|
|
14804
|
-
throw errors_createGitError.commandFailed("
|
|
14630
|
+
throw errors_createGitError.commandFailed("rebase --abort", e.message);
|
|
14805
14631
|
}
|
|
14806
|
-
return discarded;
|
|
14807
14632
|
}
|
|
14808
|
-
async
|
|
14809
|
-
|
|
14810
|
-
|
|
14811
|
-
|
|
14812
|
-
|
|
14813
|
-
|
|
14814
|
-
|
|
14815
|
-
|
|
14816
|
-
|
|
14633
|
+
async isAncestor(ancestorRef, descendantRef) {
|
|
14634
|
+
try {
|
|
14635
|
+
const mergeBase = (await this.git.raw([
|
|
14636
|
+
"merge-base",
|
|
14637
|
+
ancestorRef,
|
|
14638
|
+
descendantRef
|
|
14639
|
+
])).trim();
|
|
14640
|
+
const ancestorCommit = (await this.git.revparse([
|
|
14641
|
+
ancestorRef
|
|
14642
|
+
])).trim();
|
|
14643
|
+
return mergeBase === ancestorCommit;
|
|
14644
|
+
} catch {
|
|
14645
|
+
return false;
|
|
14646
|
+
}
|
|
14817
14647
|
}
|
|
14818
|
-
async
|
|
14819
|
-
|
|
14820
|
-
|
|
14821
|
-
|
|
14822
|
-
|
|
14823
|
-
|
|
14824
|
-
"--",
|
|
14825
|
-
relativePath
|
|
14826
|
-
])).trim();
|
|
14827
|
-
return headHash === worktreeHash;
|
|
14648
|
+
async mergeFastForward(ref) {
|
|
14649
|
+
await this.runGitCommandWithIndexLockRetry("merge --ff-only", ()=>this.git.raw([
|
|
14650
|
+
"merge",
|
|
14651
|
+
"--ff-only",
|
|
14652
|
+
ref
|
|
14653
|
+
]));
|
|
14828
14654
|
}
|
|
14829
|
-
async
|
|
14830
|
-
|
|
14831
|
-
|
|
14832
|
-
|
|
14833
|
-
|
|
14834
|
-
|
|
14835
|
-
|
|
14836
|
-
|
|
14655
|
+
async deleteRef(ref) {
|
|
14656
|
+
try {
|
|
14657
|
+
await this.git.raw([
|
|
14658
|
+
"update-ref",
|
|
14659
|
+
"-d",
|
|
14660
|
+
ref
|
|
14661
|
+
]);
|
|
14662
|
+
} catch (e) {}
|
|
14837
14663
|
}
|
|
14838
|
-
async
|
|
14839
|
-
|
|
14840
|
-
|
|
14841
|
-
|
|
14842
|
-
|
|
14664
|
+
async getRemotes() {
|
|
14665
|
+
try {
|
|
14666
|
+
const remotes = await this.git.getRemotes(true);
|
|
14667
|
+
return remotes.map((r)=>({
|
|
14668
|
+
name: r.name,
|
|
14669
|
+
fetchUrl: r.refs.fetch
|
|
14670
|
+
}));
|
|
14671
|
+
} catch (e) {
|
|
14672
|
+
throw errors_createGitError.commandFailed("remote", e.message);
|
|
14673
|
+
}
|
|
14843
14674
|
}
|
|
14844
|
-
async
|
|
14845
|
-
const
|
|
14846
|
-
return
|
|
14675
|
+
async isGitInitialized() {
|
|
14676
|
+
const gitPath = external_path_default().join(this.repoPath, ".git");
|
|
14677
|
+
return external_fs_.existsSync(gitPath);
|
|
14847
14678
|
}
|
|
14848
|
-
async
|
|
14849
|
-
|
|
14850
|
-
|
|
14851
|
-
|
|
14852
|
-
|
|
14853
|
-
|
|
14854
|
-
|
|
14855
|
-
|
|
14856
|
-
|
|
14857
|
-
outcome: "succeeded"
|
|
14858
|
-
});
|
|
14859
|
-
return;
|
|
14860
|
-
} catch (e) {
|
|
14861
|
-
const err = e;
|
|
14862
|
-
if (!isWantNotValidError(err)) {
|
|
14863
|
-
if (attempt > 0) this.emit("fetch-retry-resolved", {
|
|
14864
|
-
attempts: attempt,
|
|
14865
|
-
outcome: "failed"
|
|
14866
|
-
});
|
|
14867
|
-
throw errors_createGitError.fetchFailed(err.message);
|
|
14679
|
+
async removeEmptyDirectories(removedFiles) {
|
|
14680
|
+
const directoriesToCheck = new Set();
|
|
14681
|
+
for (const file of removedFiles){
|
|
14682
|
+
let dir = external_path_default().dirname(file);
|
|
14683
|
+
while(dir && "." !== dir){
|
|
14684
|
+
directoriesToCheck.add(dir);
|
|
14685
|
+
const parentDir = external_path_default().dirname(dir);
|
|
14686
|
+
if (parentDir === dir) break;
|
|
14687
|
+
dir = parentDir;
|
|
14868
14688
|
}
|
|
14869
|
-
|
|
14870
|
-
|
|
14871
|
-
|
|
14872
|
-
|
|
14873
|
-
|
|
14689
|
+
}
|
|
14690
|
+
const sortedDirs = Array.from(directoriesToCheck).sort((a, b)=>b.split("/").length - a.split("/").length);
|
|
14691
|
+
for (const dir of sortedDirs)try {
|
|
14692
|
+
const dirPath = external_path_default().join(this.repoPath, dir);
|
|
14693
|
+
if (!external_fs_.existsSync(dirPath)) continue;
|
|
14694
|
+
const entries = await external_fs_.promises.readdir(dirPath);
|
|
14695
|
+
if (0 === entries.length) await external_fs_.promises.rmdir(dirPath);
|
|
14696
|
+
} catch (error) {}
|
|
14697
|
+
}
|
|
14698
|
+
}
|
|
14699
|
+
async function getStagedFileChanges(git, gitService) {
|
|
14700
|
+
try {
|
|
14701
|
+
const status = await git.status();
|
|
14702
|
+
const renames = status.renamed || [];
|
|
14703
|
+
const stagedFiles = status.files.filter((f)=>" " !== f.index && "?" !== f.index);
|
|
14704
|
+
const stagedModeOnlyPaths = new Set(gitService ? await gitService.discardStagedModeOnlyChanges(stagedFiles.filter((f)=>"R" !== f.index && "D" !== f.index).map((f)=>f.path)) : []);
|
|
14705
|
+
const stagedRenames = [];
|
|
14706
|
+
for (const f of stagedFiles)if ("R" === f.index) {
|
|
14707
|
+
const rename = renames.find((r)=>r.to === f.path);
|
|
14708
|
+
if (rename) stagedRenames.push({
|
|
14709
|
+
path: rename.to,
|
|
14710
|
+
type: "rename",
|
|
14711
|
+
from: rename.from
|
|
14874
14712
|
});
|
|
14875
|
-
logger_logger.verbose(`git fetch race (attempt ${attempt}, sleeping ${FETCH_RETRY_DELAY_MS}ms): ${err.message}`);
|
|
14876
|
-
await (0, external_timers_promises_namespaceObject.setTimeout)(FETCH_RETRY_DELAY_MS);
|
|
14877
14713
|
}
|
|
14714
|
+
const stagedRenamedFromPaths = new Set(stagedRenames.map((r)=>r.from));
|
|
14715
|
+
const stagedRenamedToPaths = new Set(stagedRenames.map((r)=>r.path));
|
|
14716
|
+
const changes = [];
|
|
14717
|
+
for (const f of stagedFiles){
|
|
14718
|
+
if ("R" !== f.index) {
|
|
14719
|
+
if (!stagedModeOnlyPaths.has(f.path)) {
|
|
14720
|
+
if (!(stagedRenamedFromPaths.has(f.path) || stagedRenamedToPaths.has(f.path))) if ("D" === f.index) changes.push({
|
|
14721
|
+
path: f.path,
|
|
14722
|
+
type: "unlink"
|
|
14723
|
+
});
|
|
14724
|
+
else if ("A" === f.index || "?" === f.index) changes.push({
|
|
14725
|
+
path: f.path,
|
|
14726
|
+
type: "add"
|
|
14727
|
+
});
|
|
14728
|
+
else changes.push({
|
|
14729
|
+
path: f.path,
|
|
14730
|
+
type: "change"
|
|
14731
|
+
});
|
|
14732
|
+
}
|
|
14733
|
+
}
|
|
14734
|
+
}
|
|
14735
|
+
changes.push(...stagedRenames);
|
|
14736
|
+
return changes;
|
|
14737
|
+
} catch (e) {
|
|
14738
|
+
throw errors_createGitError.commandFailed("status", e.message);
|
|
14878
14739
|
}
|
|
14879
|
-
|
|
14880
|
-
|
|
14881
|
-
|
|
14882
|
-
|
|
14883
|
-
|
|
14740
|
+
}
|
|
14741
|
+
async function git_getCurrentOrFallbackBranchName(git) {
|
|
14742
|
+
const branchRef = (await git.revparse([
|
|
14743
|
+
"--abbrev-ref",
|
|
14744
|
+
"HEAD"
|
|
14745
|
+
])).trim();
|
|
14746
|
+
if ("HEAD" !== branchRef) return branchRef;
|
|
14747
|
+
const [localBranches, remoteBranches] = await Promise.all([
|
|
14748
|
+
git.raw([
|
|
14749
|
+
"branch",
|
|
14750
|
+
"--format=%(refname:short)",
|
|
14751
|
+
"--contains",
|
|
14752
|
+
"HEAD"
|
|
14753
|
+
]),
|
|
14754
|
+
git.raw([
|
|
14755
|
+
"branch",
|
|
14756
|
+
"-r",
|
|
14757
|
+
"--format=%(refname:short)",
|
|
14758
|
+
"--contains",
|
|
14759
|
+
"HEAD"
|
|
14760
|
+
])
|
|
14761
|
+
]);
|
|
14762
|
+
const branchNames = [
|
|
14763
|
+
...parseBranchList(localBranches),
|
|
14764
|
+
...parseBranchList(remoteBranches).filter((branchName)=>!branchName.endsWith("/HEAD")).map((branchName)=>branchName.replace(/^[^/]+\//, ""))
|
|
14765
|
+
];
|
|
14766
|
+
const branchSet = new Set(branchNames);
|
|
14767
|
+
if (branchSet.has("master")) return "master";
|
|
14768
|
+
if (branchSet.has("main")) return "main";
|
|
14769
|
+
return "master";
|
|
14770
|
+
}
|
|
14771
|
+
function parseBranchList(output) {
|
|
14772
|
+
return output.split(/\r?\n/).map((line)=>line.trim()).filter(Boolean);
|
|
14773
|
+
}
|
|
14774
|
+
function anvil_api_getDefaultAnvilUrl() {
|
|
14775
|
+
return resolveAnvilUrl();
|
|
14776
|
+
}
|
|
14777
|
+
async function anvil_api_validateAppId(appId, anvilUrl = anvil_api_getDefaultAnvilUrl(), username) {
|
|
14778
|
+
const authToken = await auth_getValidAuthToken(anvilUrl, username);
|
|
14779
|
+
try {
|
|
14780
|
+
const resp = await fetch(`${anvilUrl}/ide/api/_/apps/validate-app-id`, {
|
|
14781
|
+
method: "POST",
|
|
14782
|
+
headers: {
|
|
14783
|
+
Authorization: `Bearer ${authToken}`,
|
|
14784
|
+
"Content-Type": "application/json"
|
|
14785
|
+
},
|
|
14786
|
+
body: JSON.stringify({
|
|
14787
|
+
app_id: appId
|
|
14788
|
+
})
|
|
14789
|
+
});
|
|
14790
|
+
const data = await resp.json();
|
|
14791
|
+
if (resp.ok) return {
|
|
14792
|
+
valid: data.valid ?? false,
|
|
14793
|
+
app_name: data.app_name
|
|
14794
|
+
};
|
|
14795
|
+
if (401 === resp.status) throw createAuthError.invalid("Authentication failed");
|
|
14796
|
+
if (403 === resp.status) throw createAppError.accessDenied(appId);
|
|
14797
|
+
if (404 === resp.status) throw createAppError.notFound(appId);
|
|
14798
|
+
logger_logger.debug(`validate-app-id failed: ${resp.status} ${resp.statusText}`);
|
|
14799
|
+
return {
|
|
14800
|
+
valid: false,
|
|
14801
|
+
error: data.error || `Server error: ${resp.status}`
|
|
14802
|
+
};
|
|
14803
|
+
} catch (error) {
|
|
14804
|
+
if (error.type) throw error;
|
|
14805
|
+
throw createNetworkError.network(error.message);
|
|
14884
14806
|
}
|
|
14885
|
-
|
|
14886
|
-
|
|
14807
|
+
}
|
|
14808
|
+
async function lookupByCommit(commitId, branchName, anvilUrl = anvil_api_getDefaultAnvilUrl(), username) {
|
|
14809
|
+
const authToken = await auth_getValidAuthToken(anvilUrl, username);
|
|
14810
|
+
try {
|
|
14811
|
+
const resp = await fetch(`${anvilUrl}/ide/api/_/apps/lookup-by-commit`, {
|
|
14812
|
+
method: "POST",
|
|
14813
|
+
headers: {
|
|
14814
|
+
Authorization: `Bearer ${authToken}`,
|
|
14815
|
+
"Content-Type": "application/json"
|
|
14816
|
+
},
|
|
14817
|
+
body: JSON.stringify({
|
|
14818
|
+
commit_id: commitId,
|
|
14819
|
+
branch_name: branchName
|
|
14820
|
+
})
|
|
14821
|
+
});
|
|
14822
|
+
if (!resp.ok) {
|
|
14823
|
+
if (401 === resp.status) throw createAuthError.invalid("Authentication failed");
|
|
14824
|
+
throw createNetworkError.server(resp.status, resp.statusText);
|
|
14825
|
+
}
|
|
14826
|
+
const data = await resp.json();
|
|
14827
|
+
return data;
|
|
14828
|
+
} catch (error) {
|
|
14829
|
+
if (error.type) throw error;
|
|
14830
|
+
throw createNetworkError.network(error.message);
|
|
14887
14831
|
}
|
|
14888
|
-
|
|
14889
|
-
|
|
14890
|
-
|
|
14891
|
-
|
|
14892
|
-
|
|
14893
|
-
|
|
14894
|
-
|
|
14895
|
-
|
|
14896
|
-
|
|
14832
|
+
}
|
|
14833
|
+
async function listAppsForCheckout(options = {}) {
|
|
14834
|
+
const anvilUrl = options.anvilUrl ?? anvil_api_getDefaultAnvilUrl();
|
|
14835
|
+
const authToken = await auth_getValidAuthToken(anvilUrl, options.username);
|
|
14836
|
+
const params = new URLSearchParams();
|
|
14837
|
+
if ("number" == typeof options.limit) params.set("limit", String(options.limit));
|
|
14838
|
+
if (options.cursor) params.set("cursor", options.cursor);
|
|
14839
|
+
if (options.q && options.q.trim()) params.set("q", options.q.trim());
|
|
14840
|
+
const query = params.toString();
|
|
14841
|
+
const url = `${anvilUrl}/ide/api/_/apps${query ? `?${query}` : ""}`;
|
|
14842
|
+
try {
|
|
14843
|
+
const resp = await fetch(url, {
|
|
14844
|
+
method: "GET",
|
|
14845
|
+
headers: {
|
|
14846
|
+
Authorization: `Bearer ${authToken}`
|
|
14847
|
+
},
|
|
14848
|
+
signal: options.signal
|
|
14849
|
+
});
|
|
14850
|
+
if (!resp.ok) {
|
|
14851
|
+
if (401 === resp.status) throw createAuthError.invalid("Authentication failed");
|
|
14852
|
+
throw createNetworkError.server(resp.status, resp.statusText);
|
|
14897
14853
|
}
|
|
14898
|
-
|
|
14854
|
+
const data = await resp.json();
|
|
14855
|
+
return {
|
|
14856
|
+
apps: Array.isArray(data.apps) ? data.apps : [],
|
|
14857
|
+
next_cursor: "string" == typeof data.next_cursor ? data.next_cursor : null
|
|
14858
|
+
};
|
|
14859
|
+
} catch (error) {
|
|
14860
|
+
if (error.type) throw error;
|
|
14861
|
+
throw createNetworkError.network(error.message);
|
|
14899
14862
|
}
|
|
14900
|
-
|
|
14901
|
-
|
|
14902
|
-
|
|
14863
|
+
}
|
|
14864
|
+
async function saveUpdates(appId, branchName, payload, anvilUrl = anvil_api_getDefaultAnvilUrl(), authToken) {
|
|
14865
|
+
try {
|
|
14866
|
+
const resp = await fetch(`${anvilUrl}/ide/api/_/apps/${appId}/update?make_fresh_version=false&branch=${branchName}`, {
|
|
14867
|
+
method: "PUT",
|
|
14868
|
+
headers: {
|
|
14869
|
+
Authorization: `Bearer ${authToken}`,
|
|
14870
|
+
"Content-Type": "application/json"
|
|
14871
|
+
},
|
|
14872
|
+
body: JSON.stringify(payload)
|
|
14873
|
+
});
|
|
14874
|
+
const data = await resp.json();
|
|
14875
|
+
if (401 === resp.status) throw createAuthError.invalid(`Authentication failed (401): ${data.error || "unauthorized"}`);
|
|
14876
|
+
if (403 === resp.status) throw createAuthError.invalid(`Authentication failed (403): ${data.error || "unauthorized"}\n This may indicate:\n - The OAuth token doesn't have permission to write to app ${appId}\n - The app belongs to a different user or organization\n - The OAuth token is missing required scopes (apps:write)`);
|
|
14877
|
+
return {
|
|
14878
|
+
version: data.version,
|
|
14879
|
+
overtaken: data.overtaken,
|
|
14880
|
+
latest_version: data.latest_version,
|
|
14881
|
+
error: 200 !== resp.status ? data.error || `Status ${resp.status}` : void 0
|
|
14882
|
+
};
|
|
14883
|
+
} catch (error) {
|
|
14884
|
+
if (error.type) throw error;
|
|
14885
|
+
throw createNetworkError.network(error.message);
|
|
14903
14886
|
}
|
|
14904
|
-
|
|
14905
|
-
|
|
14906
|
-
|
|
14907
|
-
|
|
14908
|
-
|
|
14909
|
-
|
|
14910
|
-
|
|
14911
|
-
|
|
14912
|
-
|
|
14913
|
-
|
|
14914
|
-
|
|
14915
|
-
|
|
14916
|
-
|
|
14887
|
+
}
|
|
14888
|
+
function anvil_api_getGitFetchUrl(appId, authToken, gitUrl = anvil_api_getDefaultAnvilUrl()) {
|
|
14889
|
+
const url = new URL(gitUrl);
|
|
14890
|
+
const encodedToken = encodeURIComponent(authToken);
|
|
14891
|
+
return `${url.protocol}//git:${encodedToken}@${url.hostname}${url.port ? ":" + url.port : ""}/git/${appId}.git?no_freeze=true&q=`;
|
|
14892
|
+
}
|
|
14893
|
+
function anvil_api_getGitPushUrl(appId, authToken, anvilUrl = anvil_api_getDefaultAnvilUrl()) {
|
|
14894
|
+
const url = new URL(anvilUrl);
|
|
14895
|
+
const encodedToken = encodeURIComponent(authToken);
|
|
14896
|
+
return `${url.protocol}//git:${encodedToken}@${url.hostname}${url.port ? ":" + url.port : ""}/git/${appId}.git`;
|
|
14897
|
+
}
|
|
14898
|
+
function getWebSocketUrl(appId, authToken, anvilUrl = anvil_api_getDefaultAnvilUrl()) {
|
|
14899
|
+
return anvilUrl.replace(/^http/, "ws") + `/ide/api/_/apps/${appId}/ws?access_token=${authToken}`;
|
|
14900
|
+
}
|
|
14901
|
+
async function getLatestVersion() {
|
|
14902
|
+
try {
|
|
14903
|
+
const response = await fetch("https://registry.npmjs.org/@anvil-works/anvil-cli/latest");
|
|
14904
|
+
if (!response.ok) return null;
|
|
14905
|
+
const data = await response.json();
|
|
14906
|
+
return data.version;
|
|
14907
|
+
} catch (e) {
|
|
14908
|
+
return null;
|
|
14917
14909
|
}
|
|
14918
|
-
|
|
14919
|
-
|
|
14920
|
-
|
|
14921
|
-
|
|
14922
|
-
|
|
14923
|
-
|
|
14924
|
-
} catch (e) {
|
|
14925
|
-
throw errors_createGitError.commandFailed("stash pop", e.message);
|
|
14926
|
-
}
|
|
14910
|
+
}
|
|
14911
|
+
function anvil_api_filterCandidates(candidates, explicitUrl, explicitUsername) {
|
|
14912
|
+
let filtered = candidates;
|
|
14913
|
+
if (explicitUrl) {
|
|
14914
|
+
const normalizedExplicit = config_normalizeAnvilUrl(explicitUrl);
|
|
14915
|
+
filtered = filtered.filter((c)=>c.detectedUrl && config_normalizeAnvilUrl(c.detectedUrl) === normalizedExplicit);
|
|
14927
14916
|
}
|
|
14928
|
-
|
|
14929
|
-
|
|
14930
|
-
|
|
14931
|
-
|
|
14932
|
-
|
|
14917
|
+
if (explicitUsername) filtered = filtered.filter((c)=>!c.detectedUsername || c.detectedUsername === explicitUsername);
|
|
14918
|
+
return filtered;
|
|
14919
|
+
}
|
|
14920
|
+
function anvil_api_formatCandidateLabel(candidate) {
|
|
14921
|
+
const parts = [
|
|
14922
|
+
candidate.appId
|
|
14923
|
+
];
|
|
14924
|
+
if (candidate.detectedUrl) if (candidate.detectedUsername) parts.push(`(${candidate.detectedUsername} on ${candidate.detectedUrl})`);
|
|
14925
|
+
else parts.push(`(${candidate.detectedUrl})`);
|
|
14926
|
+
parts.push(`- ${candidate.description}`);
|
|
14927
|
+
return parts.join(" ");
|
|
14928
|
+
}
|
|
14929
|
+
function anvil_api_lookupRemoteInfoForAppId(appId, detectedRemotes) {
|
|
14930
|
+
const matches = detectedRemotes.filter((c)=>c.appId === appId);
|
|
14931
|
+
if (0 === matches.length) return {};
|
|
14932
|
+
const withUsername = matches.find((c)=>c.detectedUsername);
|
|
14933
|
+
if (withUsername) return {
|
|
14934
|
+
detectedUrl: withUsername.detectedUrl,
|
|
14935
|
+
detectedUsername: withUsername.detectedUsername
|
|
14936
|
+
};
|
|
14937
|
+
return {
|
|
14938
|
+
detectedUrl: matches[0].detectedUrl,
|
|
14939
|
+
detectedUsername: matches[0].detectedUsername
|
|
14940
|
+
};
|
|
14941
|
+
}
|
|
14942
|
+
async function anvil_api_detectAppIdsFromAllRemotes(repoPath) {
|
|
14943
|
+
const git = esm_default(repoPath);
|
|
14944
|
+
const out = [];
|
|
14945
|
+
try {
|
|
14946
|
+
const remotes = await git.getRemotes(true);
|
|
14947
|
+
for (const remote of remotes){
|
|
14948
|
+
const httpMatch = remote.refs.fetch?.match(/(?:http|https):\/\/(?:[^@]+@)?([^:\/]+)(?::\d+)?\/git\/([A-Z0-9]+)\.git/);
|
|
14949
|
+
if (httpMatch) {
|
|
14950
|
+
const [, host, detectedAppId] = httpMatch;
|
|
14951
|
+
out.push({
|
|
14952
|
+
appId: detectedAppId,
|
|
14953
|
+
source: "remote",
|
|
14954
|
+
description: `Git remote '${remote.name}'`,
|
|
14955
|
+
detectedUrl: config_normalizeAnvilUrl(host)
|
|
14956
|
+
});
|
|
14957
|
+
continue;
|
|
14958
|
+
}
|
|
14959
|
+
const sshMatch = remote.refs.fetch?.match(/ssh:\/\/([^@]+)@([^:]+):(\d+)\/(?:git\/)?([A-Z0-9]+)\.git/);
|
|
14960
|
+
if (sshMatch) {
|
|
14961
|
+
const [, usernamePart, host, , detectedAppId] = sshMatch;
|
|
14962
|
+
const detectedUsername = usernamePart.includes("%") ? decodeURIComponent(usernamePart) : usernamePart;
|
|
14963
|
+
const finalUsername = "git" !== detectedUsername ? detectedUsername : void 0;
|
|
14964
|
+
out.push({
|
|
14965
|
+
appId: detectedAppId,
|
|
14966
|
+
source: "remote",
|
|
14967
|
+
description: `Git remote '${remote.name}' (SSH)`,
|
|
14968
|
+
detectedUrl: config_normalizeAnvilUrl(host),
|
|
14969
|
+
detectedUsername: finalUsername
|
|
14970
|
+
});
|
|
14971
|
+
}
|
|
14933
14972
|
}
|
|
14934
|
-
}
|
|
14935
|
-
|
|
14936
|
-
|
|
14937
|
-
|
|
14938
|
-
|
|
14939
|
-
|
|
14940
|
-
|
|
14941
|
-
|
|
14942
|
-
|
|
14943
|
-
|
|
14944
|
-
|
|
14945
|
-
|
|
14973
|
+
} catch (_e) {}
|
|
14974
|
+
return out;
|
|
14975
|
+
}
|
|
14976
|
+
async function anvil_api_detectAppIdsByCommitLookup(repoPath, options) {
|
|
14977
|
+
const anvilUrl = options.anvilUrl || resolveAnvilUrl();
|
|
14978
|
+
const username = options.username;
|
|
14979
|
+
const git = esm_default(repoPath);
|
|
14980
|
+
const out = [];
|
|
14981
|
+
try {
|
|
14982
|
+
const authToken = await auth_getValidAuthToken(anvilUrl, username);
|
|
14983
|
+
const commitId = (await git.revparse([
|
|
14984
|
+
"HEAD"
|
|
14985
|
+
]))?.trim();
|
|
14986
|
+
if (!commitId) return out;
|
|
14987
|
+
const branchName = await git_getCurrentOrFallbackBranchName(git);
|
|
14988
|
+
const resp = await fetch(`${anvilUrl}/ide/api/_/apps/lookup-by-commit`, {
|
|
14989
|
+
method: "POST",
|
|
14990
|
+
headers: {
|
|
14991
|
+
Authorization: `Bearer ${authToken}`,
|
|
14992
|
+
"Content-Type": "application/json"
|
|
14993
|
+
},
|
|
14994
|
+
body: JSON.stringify({
|
|
14995
|
+
commit_id: commitId,
|
|
14996
|
+
branch_name: branchName
|
|
14997
|
+
})
|
|
14998
|
+
});
|
|
14999
|
+
logger_logger.debug(`Reverse lookup by commit ${commitId} on branch ${branchName} returned ${resp.status}`);
|
|
15000
|
+
if (!resp.ok) {
|
|
15001
|
+
const errorBody = await resp.text();
|
|
15002
|
+
logger_logger.debug(`lookup-by-commit error: ${errorBody}`);
|
|
15003
|
+
}
|
|
15004
|
+
if (resp.ok) {
|
|
15005
|
+
const data = await resp.json();
|
|
15006
|
+
logger_logger.debug(`data ${JSON.stringify(data)}`);
|
|
15007
|
+
const apps = data?.apps ?? [];
|
|
15008
|
+
for (const app of apps)if (app?.app_id) out.push({
|
|
15009
|
+
appId: app.app_id,
|
|
15010
|
+
source: "remote",
|
|
15011
|
+
description: `Reverse lookup by commit${app.branch ? ` on branch '${app.branch}'` : ""}`
|
|
15012
|
+
});
|
|
14946
15013
|
}
|
|
15014
|
+
} catch (_e) {}
|
|
15015
|
+
return out;
|
|
15016
|
+
}
|
|
15017
|
+
const external_fs_promises_namespaceObject = require("fs/promises");
|
|
15018
|
+
var external_fs_promises_default = /*#__PURE__*/ __webpack_require__.n(external_fs_promises_namespaceObject);
|
|
15019
|
+
function normalizeLineEndings(content) {
|
|
15020
|
+
return content.replace(/\r\n/g, "\n");
|
|
15021
|
+
}
|
|
15022
|
+
function pythonifyName(name) {
|
|
15023
|
+
return name.replace(/[^A-z0-9]/g, "_").replace(/^[0-9]/, "_$&");
|
|
15024
|
+
}
|
|
15025
|
+
function extractPythonName(relativePath, skipParts = 1) {
|
|
15026
|
+
const parts = relativePath.slice(0, -3).split("/");
|
|
15027
|
+
const isPackage = relativePath.endsWith("__init__.py");
|
|
15028
|
+
const relevantParts = isPackage ? parts.slice(skipParts, -1) : parts.slice(skipParts);
|
|
15029
|
+
return relevantParts.map(pythonifyName).join(".");
|
|
15030
|
+
}
|
|
15031
|
+
function deepEqual(a, b) {
|
|
15032
|
+
if (a === b) return true;
|
|
15033
|
+
if (null == a || null == b) return false;
|
|
15034
|
+
if (typeof a !== typeof b) return false;
|
|
15035
|
+
if ("object" != typeof a) return a === b;
|
|
15036
|
+
if (Array.isArray(a) !== Array.isArray(b)) return false;
|
|
15037
|
+
if (Array.isArray(a)) {
|
|
15038
|
+
if (a.length !== b.length) return false;
|
|
15039
|
+
return a.every((item, index)=>deepEqual(item, b[index]));
|
|
14947
15040
|
}
|
|
14948
|
-
|
|
14949
|
-
|
|
14950
|
-
|
|
14951
|
-
|
|
14952
|
-
|
|
14953
|
-
|
|
14954
|
-
|
|
14955
|
-
|
|
14956
|
-
|
|
14957
|
-
|
|
14958
|
-
|
|
14959
|
-
|
|
14960
|
-
|
|
14961
|
-
|
|
14962
|
-
|
|
14963
|
-
}
|
|
15041
|
+
const keysA = Object.keys(a);
|
|
15042
|
+
const keysB = Object.keys(b);
|
|
15043
|
+
if (keysA.length !== keysB.length) return false;
|
|
15044
|
+
return keysA.every((key)=>deepEqual(a[key], b[key]));
|
|
15045
|
+
}
|
|
15046
|
+
function parseHtmlWithFrontmatter(htmlContent) {
|
|
15047
|
+
const frontmatterPattern = /^---\s*\n([\s\S]*?)\n---\s*\n([\s\S]*)$/;
|
|
15048
|
+
const match = htmlContent.match(frontmatterPattern);
|
|
15049
|
+
if (match) {
|
|
15050
|
+
const yamlStr = match[1];
|
|
15051
|
+
const html = match[2];
|
|
15052
|
+
const frontmatter = yamlStr ? external_js_yaml_default().load(yamlStr) ?? {} : {};
|
|
15053
|
+
return {
|
|
15054
|
+
frontmatter,
|
|
15055
|
+
html
|
|
15056
|
+
};
|
|
14964
15057
|
}
|
|
14965
|
-
|
|
14966
|
-
|
|
14967
|
-
|
|
14968
|
-
|
|
14969
|
-
|
|
14970
|
-
|
|
14971
|
-
|
|
14972
|
-
|
|
14973
|
-
|
|
15058
|
+
return {
|
|
15059
|
+
frontmatter: {},
|
|
15060
|
+
html: htmlContent
|
|
15061
|
+
};
|
|
15062
|
+
}
|
|
15063
|
+
function formatHttpError(prefix, status, body) {
|
|
15064
|
+
if (!body) return `${prefix}: ${status}`;
|
|
15065
|
+
try {
|
|
15066
|
+
const parsed = JSON.parse(body);
|
|
15067
|
+
if ("string" == typeof parsed.error && parsed.error.trim()) return `${prefix}: ${parsed.error} (${status})`;
|
|
15068
|
+
} catch {}
|
|
15069
|
+
return `${prefix}: ${status} ${body}`;
|
|
15070
|
+
}
|
|
15071
|
+
const ANVIL_GITIGNORE_ENTRY = "/.anvil/";
|
|
15072
|
+
async function ensureProjectGitignoreIgnoresAnvilDir(projectRoot) {
|
|
15073
|
+
const rootGitignorePath = external_path_default().join(projectRoot, ".gitignore");
|
|
15074
|
+
try {
|
|
15075
|
+
const existing = await external_fs_.promises.readFile(rootGitignorePath, "utf8");
|
|
15076
|
+
if (existing.includes(ANVIL_GITIGNORE_ENTRY)) return false;
|
|
15077
|
+
const prefix = existing.endsWith("\n") || 0 === existing.length ? "" : "\n";
|
|
15078
|
+
await external_fs_.promises.writeFile(rootGitignorePath, `${existing}${prefix}${ANVIL_GITIGNORE_ENTRY}\n`, "utf8");
|
|
15079
|
+
return true;
|
|
15080
|
+
} catch (error) {
|
|
15081
|
+
if ("ENOENT" !== error.code) throw error;
|
|
15082
|
+
await external_fs_.promises.writeFile(rootGitignorePath, `${ANVIL_GITIGNORE_ENTRY}\n`, "utf8");
|
|
15083
|
+
return true;
|
|
14974
15084
|
}
|
|
14975
|
-
|
|
14976
|
-
|
|
14977
|
-
|
|
14978
|
-
|
|
14979
|
-
|
|
14980
|
-
|
|
14981
|
-
|
|
14982
|
-
|
|
14983
|
-
|
|
14984
|
-
|
|
14985
|
-
|
|
14986
|
-
|
|
14987
|
-
|
|
14988
|
-
|
|
14989
|
-
|
|
14990
|
-
|
|
15085
|
+
}
|
|
15086
|
+
const ANVIL_DEPS_DIR = ".anvil/deps";
|
|
15087
|
+
const ANVIL_DEPS_MANIFEST = "manifest.json";
|
|
15088
|
+
const DEPS_MANIFEST_VERSION = 3;
|
|
15089
|
+
async function fetchAllDependencies(appId, anvilUrl, options = {}) {
|
|
15090
|
+
const { commit, includeDocs = true, knownDependencies = [] } = options;
|
|
15091
|
+
anvilUrl ??= await auth_resolveAuthAnvilUrl();
|
|
15092
|
+
const token = await auth_getValidAuthToken(anvilUrl, options.username);
|
|
15093
|
+
const body = {
|
|
15094
|
+
include_docs: includeDocs,
|
|
15095
|
+
known_dependencies: knownDependencies
|
|
15096
|
+
};
|
|
15097
|
+
if (commit) body.commit = commit;
|
|
15098
|
+
const resp = await fetch(`${anvilUrl}/ide/api/_/apps/${appId}/resolved-dependencies`, {
|
|
15099
|
+
method: "POST",
|
|
15100
|
+
headers: {
|
|
15101
|
+
Authorization: `Bearer ${token}`,
|
|
15102
|
+
"Content-Type": "application/json"
|
|
15103
|
+
},
|
|
15104
|
+
body: JSON.stringify(body)
|
|
15105
|
+
});
|
|
15106
|
+
if (!resp.ok) {
|
|
15107
|
+
const errorBody = await resp.text();
|
|
15108
|
+
throw new Error(formatHttpError("Failed to fetch dependencies", resp.status, errorBody));
|
|
14991
15109
|
}
|
|
14992
|
-
|
|
14993
|
-
|
|
14994
|
-
|
|
14995
|
-
|
|
14996
|
-
|
|
14997
|
-
|
|
14998
|
-
|
|
14999
|
-
|
|
15000
|
-
|
|
15110
|
+
return await resp.json();
|
|
15111
|
+
}
|
|
15112
|
+
async function refreshDependencyCache(projectRoot, appId, anvilUrl, options = {}) {
|
|
15113
|
+
auth_setRepoContext(projectRoot);
|
|
15114
|
+
anvilUrl ??= await auth_resolveAuthAnvilUrl();
|
|
15115
|
+
const includeDocs = options.includeDocs ?? true;
|
|
15116
|
+
const git = new git_GitService(projectRoot);
|
|
15117
|
+
const commit = options.commit ?? await git.getCommitId();
|
|
15118
|
+
const sourceYaml = await git.show(`${commit}:anvil.yaml`).catch((error)=>{
|
|
15119
|
+
if (options.commit) return null;
|
|
15120
|
+
throw error;
|
|
15121
|
+
});
|
|
15122
|
+
const sourceDependencies = null === sourceYaml ? null : normalizeAnvilYamlDependenciesFromContent(sourceYaml);
|
|
15123
|
+
const knownDependencies = await getKnownDependenciesForCache(projectRoot, includeDocs);
|
|
15124
|
+
const response = await fetchAllDependencies(appId, anvilUrl, {
|
|
15125
|
+
username: options.username,
|
|
15126
|
+
commit,
|
|
15127
|
+
includeDocs,
|
|
15128
|
+
knownDependencies
|
|
15129
|
+
});
|
|
15130
|
+
if (!response.resolved_dependencies.length) {
|
|
15131
|
+
await clearDepsCache(projectRoot);
|
|
15132
|
+
return {
|
|
15133
|
+
response,
|
|
15134
|
+
addedGitignoreEntry: false
|
|
15135
|
+
};
|
|
15001
15136
|
}
|
|
15002
|
-
|
|
15003
|
-
|
|
15004
|
-
|
|
15005
|
-
|
|
15006
|
-
|
|
15007
|
-
|
|
15008
|
-
|
|
15009
|
-
|
|
15010
|
-
|
|
15137
|
+
const addedGitignoreEntry = await ensureProjectGitignoreIgnoresAnvilDir(projectRoot);
|
|
15138
|
+
await writeAllDepsToCache(projectRoot, response, {
|
|
15139
|
+
includeDocs,
|
|
15140
|
+
sourceDependencies
|
|
15141
|
+
});
|
|
15142
|
+
return {
|
|
15143
|
+
response,
|
|
15144
|
+
addedGitignoreEntry
|
|
15145
|
+
};
|
|
15146
|
+
}
|
|
15147
|
+
function depsDir(projectRoot) {
|
|
15148
|
+
return external_path_default().join(projectRoot, ANVIL_DEPS_DIR);
|
|
15149
|
+
}
|
|
15150
|
+
function depsManifestPath(projectRoot) {
|
|
15151
|
+
return external_path_default().join(depsDir(projectRoot), ANVIL_DEPS_MANIFEST);
|
|
15152
|
+
}
|
|
15153
|
+
function isResolvedDependencyManifestEntry(value) {
|
|
15154
|
+
if (!value || "object" != typeof value) return false;
|
|
15155
|
+
const entry = value;
|
|
15156
|
+
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);
|
|
15157
|
+
}
|
|
15158
|
+
function isCachedDependencyManifestEntry(value) {
|
|
15159
|
+
if (!isResolvedDependencyManifestEntry(value)) return false;
|
|
15160
|
+
const entry = value;
|
|
15161
|
+
return "string" == typeof entry.package_name && entry.package_name.length > 0;
|
|
15162
|
+
}
|
|
15163
|
+
function isDepsCacheManifest(value) {
|
|
15164
|
+
if (!value || "object" != typeof value) return false;
|
|
15165
|
+
const manifest = value;
|
|
15166
|
+
return manifest.version === DEPS_MANIFEST_VERSION && "boolean" == typeof manifest.includeDocs && (null === manifest.source_dependencies || Array.isArray(manifest.source_dependencies)) && Array.isArray(manifest.resolved_dependencies) && manifest.resolved_dependencies.every(isResolvedDependencyManifestEntry) && Array.isArray(manifest.cached_dependencies) && manifest.cached_dependencies.every(isCachedDependencyManifestEntry);
|
|
15167
|
+
}
|
|
15168
|
+
async function readDepsCacheManifest(projectRoot) {
|
|
15169
|
+
try {
|
|
15170
|
+
const raw = await external_fs_promises_default().readFile(depsManifestPath(projectRoot), "utf-8");
|
|
15171
|
+
const parsed = JSON.parse(raw);
|
|
15172
|
+
return isDepsCacheManifest(parsed) ? parsed : void 0;
|
|
15173
|
+
} catch {
|
|
15174
|
+
return;
|
|
15011
15175
|
}
|
|
15012
|
-
|
|
15013
|
-
|
|
15014
|
-
|
|
15015
|
-
|
|
15016
|
-
|
|
15017
|
-
|
|
15018
|
-
|
|
15019
|
-
|
|
15020
|
-
|
|
15021
|
-
|
|
15022
|
-
|
|
15023
|
-
|
|
15024
|
-
|
|
15176
|
+
}
|
|
15177
|
+
async function getKnownDependenciesForCache(projectRoot, includeDocs) {
|
|
15178
|
+
const manifest = await readDepsCacheManifest(projectRoot);
|
|
15179
|
+
if (!manifest || manifest.includeDocs !== includeDocs) return [];
|
|
15180
|
+
return manifest.resolved_dependencies.map(({ app_id, commit_id })=>({
|
|
15181
|
+
app_id,
|
|
15182
|
+
commit_id
|
|
15183
|
+
}));
|
|
15184
|
+
}
|
|
15185
|
+
function getDependencyWatchSubscriptionsFromManifest(manifest) {
|
|
15186
|
+
if (!manifest) return [];
|
|
15187
|
+
const cachedByAppId = new Map(manifest.cached_dependencies.map((dep)=>[
|
|
15188
|
+
dep.app_id,
|
|
15189
|
+
dep
|
|
15190
|
+
]));
|
|
15191
|
+
return manifest.resolved_dependencies.flatMap((dep)=>{
|
|
15192
|
+
if (!dep.branch) return [];
|
|
15193
|
+
return [
|
|
15194
|
+
{
|
|
15195
|
+
appId: dep.app_id,
|
|
15196
|
+
branch: dep.branch,
|
|
15197
|
+
packageName: cachedByAppId.get(dep.app_id)?.package_name
|
|
15198
|
+
}
|
|
15199
|
+
];
|
|
15200
|
+
});
|
|
15201
|
+
}
|
|
15202
|
+
function normalizeAnvilYamlDependenciesFromContent(content) {
|
|
15203
|
+
const parsed = external_js_yaml_default().load(content);
|
|
15204
|
+
if (!parsed || "object" != typeof parsed || !Array.isArray(parsed.dependencies)) return [];
|
|
15205
|
+
return normalizeAnvilYamlDependenciesValue(parsed.dependencies);
|
|
15206
|
+
}
|
|
15207
|
+
function normalizeAnvilYamlDependenciesValue(value) {
|
|
15208
|
+
if (!Array.isArray(value)) return [];
|
|
15209
|
+
return value.map(normalizeForComparison);
|
|
15210
|
+
}
|
|
15211
|
+
async function readAnvilYamlDependencies(projectRoot) {
|
|
15212
|
+
try {
|
|
15213
|
+
const content = await external_fs_promises_default().readFile(external_path_default().join(projectRoot, "anvil.yaml"), "utf-8");
|
|
15214
|
+
return normalizeAnvilYamlDependenciesFromContent(content);
|
|
15215
|
+
} catch {
|
|
15216
|
+
return [];
|
|
15217
|
+
}
|
|
15218
|
+
}
|
|
15219
|
+
function anvilYamlDependenciesEqual(left, right) {
|
|
15220
|
+
return deepEqual(left, right);
|
|
15221
|
+
}
|
|
15222
|
+
function normalizeForComparison(value) {
|
|
15223
|
+
if (Array.isArray(value)) return value.map(normalizeForComparison);
|
|
15224
|
+
if (value && "object" == typeof value) return Object.fromEntries(Object.entries(value).sort(([left], [right])=>left.localeCompare(right)).map(([key, entryValue])=>[
|
|
15225
|
+
key,
|
|
15226
|
+
normalizeForComparison(entryValue)
|
|
15227
|
+
]));
|
|
15228
|
+
return value;
|
|
15229
|
+
}
|
|
15230
|
+
function cachedManifestEntry(dep, packageName) {
|
|
15231
|
+
return {
|
|
15232
|
+
app_id: dep.app_id,
|
|
15233
|
+
commit_id: dep.commit_id,
|
|
15234
|
+
...dep.branch ? {
|
|
15235
|
+
branch: dep.branch
|
|
15236
|
+
} : {},
|
|
15237
|
+
package_name: packageName
|
|
15238
|
+
};
|
|
15239
|
+
}
|
|
15240
|
+
function safeDependencyPath(filePath, kind) {
|
|
15241
|
+
const normalizedPath = external_path_default().normalize(filePath);
|
|
15242
|
+
if (external_path_default().isAbsolute(normalizedPath) || normalizedPath.startsWith("..")) throw new Error(`Invalid dependency ${kind} path: ${filePath}`);
|
|
15243
|
+
return normalizedPath;
|
|
15244
|
+
}
|
|
15245
|
+
function dependencyPythonPath(name, kind) {
|
|
15246
|
+
const parts = name.split(".");
|
|
15247
|
+
if (0 === parts.length || parts.some((part)=>!part || "." === part || ".." === part || part.includes("/") || part.includes("\\"))) throw new Error(`Invalid dependency ${kind} name: ${name}`);
|
|
15248
|
+
return external_path_default().join(...parts);
|
|
15249
|
+
}
|
|
15250
|
+
async function writeDepToCache(projectRoot, dep) {
|
|
15251
|
+
const depDir = external_path_default().join(projectRoot, ANVIL_DEPS_DIR, dep.package_name);
|
|
15252
|
+
await external_fs_promises_default().rm(depDir, {
|
|
15253
|
+
recursive: true,
|
|
15254
|
+
force: true
|
|
15255
|
+
});
|
|
15256
|
+
await external_fs_promises_default().mkdir(external_path_default().join(depDir, "client_code"), {
|
|
15257
|
+
recursive: true
|
|
15258
|
+
});
|
|
15259
|
+
await external_fs_promises_default().mkdir(external_path_default().join(depDir, "server_code"), {
|
|
15260
|
+
recursive: true
|
|
15261
|
+
});
|
|
15262
|
+
await external_fs_promises_default().writeFile(external_path_default().join(depDir, "anvil.yaml"), external_js_yaml_default().dump({
|
|
15263
|
+
package_name: dep.package_name
|
|
15264
|
+
}));
|
|
15265
|
+
for (const mod of dep.modules ?? []){
|
|
15266
|
+
const modulePath = external_path_default().join(depDir, "client_code", dependencyPythonPath(mod.name, "module"));
|
|
15267
|
+
if (mod.is_package) {
|
|
15268
|
+
await external_fs_promises_default().mkdir(modulePath, {
|
|
15269
|
+
recursive: true
|
|
15270
|
+
});
|
|
15271
|
+
await external_fs_promises_default().writeFile(external_path_default().join(modulePath, "__init__.py"), mod.code);
|
|
15272
|
+
} else {
|
|
15273
|
+
const moduleFile = `${modulePath}.py`;
|
|
15274
|
+
await external_fs_promises_default().mkdir(external_path_default().dirname(moduleFile), {
|
|
15275
|
+
recursive: true
|
|
15276
|
+
});
|
|
15277
|
+
await external_fs_promises_default().writeFile(moduleFile, mod.code);
|
|
15025
15278
|
}
|
|
15026
15279
|
}
|
|
15027
|
-
|
|
15028
|
-
|
|
15029
|
-
|
|
15030
|
-
|
|
15031
|
-
|
|
15032
|
-
|
|
15033
|
-
|
|
15034
|
-
|
|
15035
|
-
|
|
15280
|
+
for (const form of dep.forms ?? []){
|
|
15281
|
+
const { code, class_name, is_package, ...formTemplate } = form;
|
|
15282
|
+
const formPath = external_path_default().join(depDir, "client_code", dependencyPythonPath(class_name, "form"));
|
|
15283
|
+
if (is_package) {
|
|
15284
|
+
await external_fs_promises_default().mkdir(formPath, {
|
|
15285
|
+
recursive: true
|
|
15286
|
+
});
|
|
15287
|
+
await external_fs_promises_default().writeFile(external_path_default().join(formPath, "__init__.py"), code);
|
|
15288
|
+
await external_fs_promises_default().writeFile(external_path_default().join(formPath, "form_template.yaml"), external_js_yaml_default().dump(formTemplate));
|
|
15289
|
+
} else {
|
|
15290
|
+
const formCodeFile = `${formPath}.py`;
|
|
15291
|
+
await external_fs_promises_default().mkdir(external_path_default().dirname(formCodeFile), {
|
|
15292
|
+
recursive: true
|
|
15293
|
+
});
|
|
15294
|
+
await external_fs_promises_default().writeFile(formCodeFile, code);
|
|
15295
|
+
await external_fs_promises_default().writeFile(`${formPath}.yaml`, external_js_yaml_default().dump(formTemplate));
|
|
15036
15296
|
}
|
|
15037
15297
|
}
|
|
15038
|
-
|
|
15039
|
-
|
|
15040
|
-
|
|
15041
|
-
|
|
15042
|
-
|
|
15043
|
-
|
|
15044
|
-
|
|
15045
|
-
}
|
|
15046
|
-
|
|
15047
|
-
|
|
15048
|
-
|
|
15049
|
-
|
|
15050
|
-
|
|
15051
|
-
name: r.name,
|
|
15052
|
-
fetchUrl: r.refs.fetch
|
|
15053
|
-
}));
|
|
15054
|
-
} catch (e) {
|
|
15055
|
-
throw errors_createGitError.commandFailed("remote", e.message);
|
|
15298
|
+
for (const mod of dep.server_modules ?? []){
|
|
15299
|
+
const modulePath = external_path_default().join(depDir, "server_code", dependencyPythonPath(mod.name, "server module"));
|
|
15300
|
+
if (mod.is_package) {
|
|
15301
|
+
await external_fs_promises_default().mkdir(modulePath, {
|
|
15302
|
+
recursive: true
|
|
15303
|
+
});
|
|
15304
|
+
await external_fs_promises_default().writeFile(external_path_default().join(modulePath, "__init__.py"), mod.code);
|
|
15305
|
+
} else {
|
|
15306
|
+
const moduleFile = `${modulePath}.py`;
|
|
15307
|
+
await external_fs_promises_default().mkdir(external_path_default().dirname(moduleFile), {
|
|
15308
|
+
recursive: true
|
|
15309
|
+
});
|
|
15310
|
+
await external_fs_promises_default().writeFile(moduleFile, mod.code);
|
|
15056
15311
|
}
|
|
15057
15312
|
}
|
|
15058
|
-
|
|
15059
|
-
const
|
|
15060
|
-
|
|
15313
|
+
for (const asset of dep.assets ?? []){
|
|
15314
|
+
const targetPath = external_path_default().join(depDir, "theme", "assets", safeDependencyPath(asset.name, "asset"));
|
|
15315
|
+
await external_fs_promises_default().mkdir(external_path_default().dirname(targetPath), {
|
|
15316
|
+
recursive: true
|
|
15317
|
+
});
|
|
15318
|
+
await external_fs_promises_default().writeFile(targetPath, Buffer.from(asset.content, "base64"));
|
|
15061
15319
|
}
|
|
15062
|
-
|
|
15063
|
-
const
|
|
15064
|
-
|
|
15065
|
-
|
|
15066
|
-
|
|
15067
|
-
|
|
15068
|
-
const parentDir = external_path_default().dirname(dir);
|
|
15069
|
-
if (parentDir === dir) break;
|
|
15070
|
-
dir = parentDir;
|
|
15071
|
-
}
|
|
15072
|
-
}
|
|
15073
|
-
const sortedDirs = Array.from(directoriesToCheck).sort((a, b)=>b.split("/").length - a.split("/").length);
|
|
15074
|
-
for (const dir of sortedDirs)try {
|
|
15075
|
-
const dirPath = external_path_default().join(this.repoPath, dir);
|
|
15076
|
-
if (!external_fs_.existsSync(dirPath)) continue;
|
|
15077
|
-
const entries = await external_fs_.promises.readdir(dirPath);
|
|
15078
|
-
if (0 === entries.length) await external_fs_.promises.rmdir(dirPath);
|
|
15079
|
-
} catch (error) {}
|
|
15320
|
+
for (const doc of dep.docs ?? []){
|
|
15321
|
+
const targetPath = external_path_default().join(depDir, safeDependencyPath(doc.path, "doc"));
|
|
15322
|
+
await external_fs_promises_default().mkdir(external_path_default().dirname(targetPath), {
|
|
15323
|
+
recursive: true
|
|
15324
|
+
});
|
|
15325
|
+
await external_fs_promises_default().writeFile(targetPath, doc.content);
|
|
15080
15326
|
}
|
|
15081
15327
|
}
|
|
15082
|
-
async function
|
|
15083
|
-
|
|
15084
|
-
|
|
15085
|
-
|
|
15086
|
-
|
|
15087
|
-
|
|
15088
|
-
|
|
15089
|
-
|
|
15090
|
-
|
|
15091
|
-
|
|
15092
|
-
|
|
15093
|
-
|
|
15094
|
-
|
|
15095
|
-
|
|
15096
|
-
|
|
15097
|
-
|
|
15098
|
-
|
|
15099
|
-
|
|
15100
|
-
|
|
15101
|
-
|
|
15102
|
-
|
|
15103
|
-
|
|
15104
|
-
|
|
15105
|
-
|
|
15106
|
-
|
|
15107
|
-
|
|
15108
|
-
|
|
15109
|
-
|
|
15110
|
-
|
|
15111
|
-
|
|
15112
|
-
|
|
15113
|
-
|
|
15114
|
-
|
|
15115
|
-
|
|
15116
|
-
|
|
15117
|
-
|
|
15118
|
-
|
|
15119
|
-
|
|
15120
|
-
|
|
15121
|
-
|
|
15122
|
-
|
|
15328
|
+
async function writeAllDepsToCache(projectRoot, response, options = {}) {
|
|
15329
|
+
const includeDocs = options.includeDocs ?? true;
|
|
15330
|
+
const manifest = await readDepsCacheManifest(projectRoot);
|
|
15331
|
+
const canReuseExistingCache = manifest?.includeDocs === includeDocs;
|
|
15332
|
+
if (!canReuseExistingCache) await clearDepsCache(projectRoot);
|
|
15333
|
+
const previousCached = canReuseExistingCache ? manifest.cached_dependencies : [];
|
|
15334
|
+
const resolvedAppIds = new Set(response.resolved_dependencies.map((dep)=>dep.app_id));
|
|
15335
|
+
for (const dep of previousCached)if (!resolvedAppIds.has(dep.app_id)) await external_fs_promises_default().rm(external_path_default().join(projectRoot, ANVIL_DEPS_DIR, dep.package_name), {
|
|
15336
|
+
recursive: true,
|
|
15337
|
+
force: true
|
|
15338
|
+
});
|
|
15339
|
+
for (const dep of response.dependencies)await writeDepToCache(projectRoot, dep);
|
|
15340
|
+
const returnedByAppId = new Map(response.dependencies.map((dep)=>[
|
|
15341
|
+
dep.app_id,
|
|
15342
|
+
dep
|
|
15343
|
+
]));
|
|
15344
|
+
const previousByAppId = new Map(previousCached.map((dep)=>[
|
|
15345
|
+
dep.app_id,
|
|
15346
|
+
dep
|
|
15347
|
+
]));
|
|
15348
|
+
const cachedDependencies = response.resolved_dependencies.map((dep)=>{
|
|
15349
|
+
const returned = returnedByAppId.get(dep.app_id);
|
|
15350
|
+
if (returned) return cachedManifestEntry(returned, returned.package_name);
|
|
15351
|
+
const previous = previousByAppId.get(dep.app_id);
|
|
15352
|
+
if (!previous) return;
|
|
15353
|
+
return cachedManifestEntry(dep, previous.package_name);
|
|
15354
|
+
}).filter((dep)=>void 0 !== dep);
|
|
15355
|
+
const newManifest = {
|
|
15356
|
+
version: DEPS_MANIFEST_VERSION,
|
|
15357
|
+
includeDocs,
|
|
15358
|
+
source_dependencies: void 0 === options.sourceDependencies ? await readAnvilYamlDependencies(projectRoot) : options.sourceDependencies,
|
|
15359
|
+
resolved_dependencies: response.resolved_dependencies,
|
|
15360
|
+
cached_dependencies: cachedDependencies
|
|
15361
|
+
};
|
|
15362
|
+
await external_fs_promises_default().mkdir(depsDir(projectRoot), {
|
|
15363
|
+
recursive: true
|
|
15364
|
+
});
|
|
15365
|
+
await external_fs_promises_default().writeFile(depsManifestPath(projectRoot), `${JSON.stringify(newManifest, null, 2)}\n`);
|
|
15366
|
+
}
|
|
15367
|
+
async function clearDepsCache(projectRoot) {
|
|
15368
|
+
await external_fs_promises_default().rm(depsDir(projectRoot), {
|
|
15369
|
+
recursive: true,
|
|
15370
|
+
force: true
|
|
15371
|
+
});
|
|
15123
15372
|
}
|
|
15124
15373
|
const external_chokidar_namespaceObject = require("chokidar");
|
|
15125
15374
|
var external_chokidar_default = /*#__PURE__*/ __webpack_require__.n(external_chokidar_namespaceObject);
|
|
@@ -15892,7 +16141,8 @@ Promise.resolve(executeGitCredentialOperation(process.argv[2] || "get", {
|
|
|
15892
16141
|
if (PYTHON_KEYWORDS.has(value)) pushIssue(issues, path, "must be a valid Python identifier (not a keyword)");
|
|
15893
16142
|
}
|
|
15894
16143
|
const componentTypeSpec_IDENTIFIER_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
15895
|
-
const
|
|
16144
|
+
const LEGACY_CUSTOM_COMPONENT_SPEC_RE = /^form:(?:([^:]+):)?(.+)$/;
|
|
16145
|
+
const PACKAGE_QUALIFIED_FORM_NAME_RE = /^([^.]+)\.(.+)$/;
|
|
15896
16146
|
const componentTypeSpec_PYTHON_KEYWORDS = new Set([
|
|
15897
16147
|
"False",
|
|
15898
16148
|
"None",
|
|
@@ -15932,32 +16182,17 @@ Promise.resolve(executeGitCredentialOperation(process.argv[2] || "get", {
|
|
|
15932
16182
|
"match",
|
|
15933
16183
|
"case"
|
|
15934
16184
|
]);
|
|
15935
|
-
const LEGACY_FORM_TYPE_REFERENCE_MESSAGE = "must be a valid form: reference (form:Module.Form, form:dep_xxx:Package.Form)";
|
|
16185
|
+
const LEGACY_FORM_TYPE_REFERENCE_MESSAGE = "must be a valid form: reference (form:Module.Form, form:dep_xxx:Package.Form, form:APP_ID:Package.Form)";
|
|
15936
16186
|
const COMPONENT_TYPE_REFERENCE_MESSAGE = "must be a known Anvil component type (see componentIcons.ts), a form: reference, or a package-qualified form reference (Package.Form)";
|
|
15937
16187
|
function isValidIdentifierSegment(value) {
|
|
15938
16188
|
return value.length > 0 && componentTypeSpec_IDENTIFIER_RE.test(value) && !componentTypeSpec_PYTHON_KEYWORDS.has(value);
|
|
15939
16189
|
}
|
|
15940
16190
|
function isValidLegacyFormTypeReference(typeName) {
|
|
15941
|
-
|
|
15942
|
-
const rest = typeName.slice(5);
|
|
15943
|
-
if (0 === rest.length) return false;
|
|
15944
|
-
let classPath = rest;
|
|
15945
|
-
if (rest.startsWith("dep_")) {
|
|
15946
|
-
const splitIndex = rest.indexOf(":");
|
|
15947
|
-
if (splitIndex <= 0) return false;
|
|
15948
|
-
const depId = rest.slice(0, splitIndex);
|
|
15949
|
-
if (!DEP_ID_RE.test(depId)) return false;
|
|
15950
|
-
classPath = rest.slice(splitIndex + 1);
|
|
15951
|
-
}
|
|
15952
|
-
if (0 === classPath.length) return false;
|
|
15953
|
-
return classPath.split(".").every((part)=>isValidIdentifierSegment(part));
|
|
16191
|
+
return LEGACY_CUSTOM_COMPONENT_SPEC_RE.test(typeName);
|
|
15954
16192
|
}
|
|
15955
16193
|
function isValidPackageQualifiedComponentType(typeName) {
|
|
15956
16194
|
if (typeName.startsWith("anvil.")) return false;
|
|
15957
|
-
|
|
15958
|
-
const parts = typeName.split(".");
|
|
15959
|
-
if (parts.length < 2) return false;
|
|
15960
|
-
return parts.every((part)=>isValidIdentifierSegment(part));
|
|
16195
|
+
return PACKAGE_QUALIFIED_FORM_NAME_RE.test(typeName);
|
|
15961
16196
|
}
|
|
15962
16197
|
function componentTypeSpec_pushIssue(issues, path, message) {
|
|
15963
16198
|
issues.push({
|
|
@@ -16040,8 +16275,15 @@ Promise.resolve(executeGitCredentialOperation(process.argv[2] || "get", {
|
|
|
16040
16275
|
message
|
|
16041
16276
|
});
|
|
16042
16277
|
}
|
|
16278
|
+
function validateNonEmptyString(value, path, issues) {
|
|
16279
|
+
if ("string" != typeof value || 0 === value.length) {
|
|
16280
|
+
formTemplateValidation_pushIssue(issues, path, "must be a non-empty string");
|
|
16281
|
+
return false;
|
|
16282
|
+
}
|
|
16283
|
+
return true;
|
|
16284
|
+
}
|
|
16043
16285
|
function validateIdentifier(value, path, issues) {
|
|
16044
|
-
if (
|
|
16286
|
+
if (!validateNonEmptyString(value, path, issues)) return;
|
|
16045
16287
|
if (!formTemplateValidation_IDENTIFIER_RE.test(value)) return void formTemplateValidation_pushIssue(issues, path, "must be a valid identifier");
|
|
16046
16288
|
if (formTemplateValidation_PYTHON_KEYWORDS.has(value)) formTemplateValidation_pushIssue(issues, path, "must be a valid Python identifier (not a keyword)");
|
|
16047
16289
|
}
|
|
@@ -16066,7 +16308,7 @@ Promise.resolve(executeGitCredentialOperation(process.argv[2] || "get", {
|
|
|
16066
16308
|
const bindingPath = `${path}[${index}]`;
|
|
16067
16309
|
if (!isPlainObject(binding)) return void formTemplateValidation_pushIssue(issues, bindingPath, "must be an object");
|
|
16068
16310
|
if ("string" != typeof binding.property || 0 === binding.property.length) formTemplateValidation_pushIssue(issues, `${bindingPath}.property`, "must be a non-empty string");
|
|
16069
|
-
if ("string" != typeof binding.code
|
|
16311
|
+
if ("string" != typeof binding.code) formTemplateValidation_pushIssue(issues, `${bindingPath}.code`, "must be a string");
|
|
16070
16312
|
if (void 0 !== binding.writeback && "boolean" != typeof binding.writeback) formTemplateValidation_pushIssue(issues, `${bindingPath}.writeback`, "must be a boolean");
|
|
16071
16313
|
});
|
|
16072
16314
|
}
|
|
@@ -16124,7 +16366,8 @@ Promise.resolve(executeGitCredentialOperation(process.argv[2] || "get", {
|
|
|
16124
16366
|
validatePropertiesObject(value.set_layout_properties, `${path}.set_layout_properties`, issues);
|
|
16125
16367
|
if (isPlainObject(value.target)) {
|
|
16126
16368
|
if ("container" !== value.target.type && "slot" !== value.target.type) formTemplateValidation_pushIssue(issues, `${path}.target.type`, "must be 'container' or 'slot'");
|
|
16127
|
-
if ("
|
|
16369
|
+
if ("slot" === value.target.type) validateNonEmptyString(value.target.name, `${path}.target.name`, issues);
|
|
16370
|
+
else if ("container" !== value.target.type || "" !== value.target.name) validateIdentifier(value.target.name, `${path}.target.name`, issues);
|
|
16128
16371
|
} else formTemplateValidation_pushIssue(issues, `${path}.target`, "must be an object");
|
|
16129
16372
|
if (void 0 !== value.template) validateComponentNode(value.template, `${path}.template`, issues, seenNames);
|
|
16130
16373
|
}
|
|
@@ -16181,7 +16424,7 @@ Promise.resolve(executeGitCredentialOperation(process.argv[2] || "get", {
|
|
|
16181
16424
|
} else {
|
|
16182
16425
|
const componentNames = options.componentNames ?? new Set();
|
|
16183
16426
|
for (const [slotName, slotDef] of Object.entries(value.slots)){
|
|
16184
|
-
|
|
16427
|
+
validateNonEmptyString(slotName, path(`slots.${slotName}.name`), issues);
|
|
16185
16428
|
validateSlotDefinition(slotDef, path(`slots.${slotName}`), issues, componentNames);
|
|
16186
16429
|
}
|
|
16187
16430
|
}
|
|
@@ -16189,6 +16432,7 @@ Promise.resolve(executeGitCredentialOperation(process.argv[2] || "get", {
|
|
|
16189
16432
|
}
|
|
16190
16433
|
if (void 0 !== value.toolbox_item && !isPlainObject(value.toolbox_item)) formTemplateValidation_pushIssue(issues, path("toolbox_item"), "must be an object");
|
|
16191
16434
|
if (void 0 !== value.layout_metadata && !isPlainObject(value.layout_metadata)) formTemplateValidation_pushIssue(issues, path("layout_metadata"), "must be an object");
|
|
16435
|
+
if (void 0 !== value.help_text && "string" != typeof value.help_text) formTemplateValidation_pushIssue(issues, path("help_text"), "must be a string");
|
|
16192
16436
|
if (void 0 !== value.item_type) if (isPlainObject(value.item_type)) {
|
|
16193
16437
|
if ("number" != typeof value.item_type.table_id) formTemplateValidation_pushIssue(issues, path("item_type.table_id"), "must be a number");
|
|
16194
16438
|
} else formTemplateValidation_pushIssue(issues, path("item_type"), "must be an object");
|
|
@@ -16263,7 +16507,8 @@ Promise.resolve(executeGitCredentialOperation(process.argv[2] || "get", {
|
|
|
16263
16507
|
"toolbox_item",
|
|
16264
16508
|
"layout_metadata",
|
|
16265
16509
|
"item_type",
|
|
16266
|
-
"slots"
|
|
16510
|
+
"slots",
|
|
16511
|
+
"help_text"
|
|
16267
16512
|
]);
|
|
16268
16513
|
const FRONTMATTER_DISALLOWED_KEYS = new Set([
|
|
16269
16514
|
"container",
|
|
@@ -16382,7 +16627,7 @@ Promise.resolve(executeGitCredentialOperation(process.argv[2] || "get", {
|
|
|
16382
16627
|
function validateParsedHtmlTemplate(parsed) {
|
|
16383
16628
|
return validateFormTemplateData(parsed);
|
|
16384
16629
|
}
|
|
16385
|
-
function validateFormTemplateHtml(htmlContent, parseHtml = form_template_parser_namespaceObject.parseSerializedHtml) {
|
|
16630
|
+
function validateFormTemplateHtml(htmlContent, parseHtml = form_template_parser_namespaceObject.parseSerializedHtml, options = {}) {
|
|
16386
16631
|
const { frontmatter, body, bodyStartOffset } = splitHtmlFormTemplateFrontmatter(htmlContent);
|
|
16387
16632
|
if (null !== frontmatter) {
|
|
16388
16633
|
const frontmatterIssues = validateHtmlFrontmatter(frontmatter);
|
|
@@ -16395,7 +16640,10 @@ Promise.resolve(executeGitCredentialOperation(process.argv[2] || "get", {
|
|
|
16395
16640
|
let parsed;
|
|
16396
16641
|
try {
|
|
16397
16642
|
parsed = parseHtml(body, {
|
|
16398
|
-
warnings: true
|
|
16643
|
+
warnings: true,
|
|
16644
|
+
...options.formSpecPackageContext ? {
|
|
16645
|
+
formSpecPackageContext: options.formSpecPackageContext
|
|
16646
|
+
} : {}
|
|
16399
16647
|
});
|
|
16400
16648
|
} catch (error) {
|
|
16401
16649
|
return {
|
|
@@ -16426,6 +16674,7 @@ Promise.resolve(executeGitCredentialOperation(process.argv[2] || "get", {
|
|
|
16426
16674
|
ok: true
|
|
16427
16675
|
};
|
|
16428
16676
|
}
|
|
16677
|
+
(0, external_module_namespaceObject.createRequire)(__filename);
|
|
16429
16678
|
const PYTHON_VALIDATOR_SCRIPT = String.raw`
|
|
16430
16679
|
import ast
|
|
16431
16680
|
import json
|
|
@@ -16889,7 +17138,7 @@ print(json.dumps({"issues": issues}))
|
|
|
16889
17138
|
}
|
|
16890
17139
|
function validateScheduledTasks(value, path) {
|
|
16891
17140
|
const issues = [];
|
|
16892
|
-
if (
|
|
17141
|
+
if (null == value) return issues;
|
|
16893
17142
|
if (!Array.isArray(value)) {
|
|
16894
17143
|
validators_pushIssue(issues, path, "must be an array");
|
|
16895
17144
|
return issues;
|
|
@@ -17037,15 +17286,25 @@ print(json.dumps({"issues": issues}))
|
|
|
17037
17286
|
const existingPath = resolveExistingPath(filePath);
|
|
17038
17287
|
if (!existingPath) return null;
|
|
17039
17288
|
let currentDir = external_fs_.statSync(existingPath).isDirectory() ? existingPath : external_path_default().dirname(existingPath);
|
|
17040
|
-
|
|
17289
|
+
const candidates = [];
|
|
17041
17290
|
while(true){
|
|
17042
|
-
if (external_fs_.existsSync(external_path_default().join(currentDir, "anvil.yaml")))
|
|
17291
|
+
if (external_fs_.existsSync(external_path_default().join(currentDir, "anvil.yaml"))) candidates.push(currentDir);
|
|
17043
17292
|
const parentDir = external_path_default().dirname(currentDir);
|
|
17044
|
-
if (parentDir === currentDir)
|
|
17293
|
+
if (parentDir === currentDir) break;
|
|
17045
17294
|
currentDir = parentDir;
|
|
17046
17295
|
}
|
|
17296
|
+
return candidates.find((candidate)=>!candidates.some((outerCandidate)=>{
|
|
17297
|
+
if (candidate === outerCandidate) return false;
|
|
17298
|
+
return [
|
|
17299
|
+
"client_code",
|
|
17300
|
+
"server_code"
|
|
17301
|
+
].some((codeDir)=>{
|
|
17302
|
+
const relativePath = external_path_default().relative(external_path_default().join(outerCandidate, codeDir), candidate);
|
|
17303
|
+
return !external_path_default().isAbsolute(relativePath) && ".." !== relativePath && !relativePath.startsWith(`..${external_path_default().sep}`);
|
|
17304
|
+
});
|
|
17305
|
+
})) ?? null;
|
|
17047
17306
|
}
|
|
17048
|
-
function validatePath(filePath, fileContent) {
|
|
17307
|
+
function validatePath(filePath, fileContent, options = {}) {
|
|
17049
17308
|
const target = inferValidationTarget(filePath);
|
|
17050
17309
|
const existingPath = resolveExistingPath(filePath);
|
|
17051
17310
|
const appRoot = findAnvilAppRoot(filePath);
|
|
@@ -17118,7 +17377,9 @@ print(json.dumps({"issues": issues}))
|
|
|
17118
17377
|
]
|
|
17119
17378
|
};
|
|
17120
17379
|
}
|
|
17121
|
-
const result = validateFormTemplateHtml(fileContent
|
|
17380
|
+
const result = validateFormTemplateHtml(fileContent, void 0, {
|
|
17381
|
+
formSpecPackageContext: options.formSpecPackageContext
|
|
17382
|
+
});
|
|
17122
17383
|
return result.ok ? validPathResult(target, result.warnings) : {
|
|
17123
17384
|
...result,
|
|
17124
17385
|
target
|
|
@@ -17935,7 +18196,7 @@ print(json.dumps({"issues": issues}))
|
|
|
17935
18196
|
]
|
|
17936
18197
|
});
|
|
17937
18198
|
}
|
|
17938
|
-
async function buildFormTemplateData(relativePath, templateInfo, context) {
|
|
18199
|
+
async function buildFormTemplateData(relativePath, templateInfo, context, missingCodeBehavior = "error") {
|
|
17939
18200
|
const { repoPath, stagedOnly = false } = context;
|
|
17940
18201
|
const { kind } = templateInfo;
|
|
17941
18202
|
const loadResult = await loadFormTemplateData(relativePath, templateInfo, context);
|
|
@@ -17949,15 +18210,28 @@ print(json.dumps({"issues": issues}))
|
|
|
17949
18210
|
};
|
|
17950
18211
|
const codeRelativePath = "package" === kind ? relativePath.replace(/form_template\.(yaml|html)$/i, "__init__.py") : relativePath.replace(/\.(yaml|html)$/i, ".py");
|
|
17951
18212
|
const className = extractPythonName(codeRelativePath);
|
|
17952
|
-
|
|
17953
|
-
|
|
17954
|
-
|
|
17955
|
-
|
|
17956
|
-
|
|
17957
|
-
|
|
17958
|
-
|
|
18213
|
+
let codeContent;
|
|
18214
|
+
try {
|
|
18215
|
+
codeContent = await readFileContent(repoPath, codeRelativePath, stagedOnly);
|
|
18216
|
+
} catch (error) {
|
|
18217
|
+
if (stagedOnly || !isFileNotFoundError(error) || "error" === missingCodeBehavior) throw error;
|
|
18218
|
+
const missingCodeReason = `Form code file ${codeRelativePath} is not present yet`;
|
|
18219
|
+
const knownForm = context.editorYaml.hasId("forms", className);
|
|
18220
|
+
if ("defer" === missingCodeBehavior || knownForm) {
|
|
18221
|
+
const action = knownForm ? `not saving existing form ${className}` : `deferring form ${className}`;
|
|
18222
|
+
logger_logger.warn(external_chalk_default().yellow(`Form template ${relativePath} has no code file ${codeRelativePath} yet; ${action}.`));
|
|
18223
|
+
return {
|
|
18224
|
+
formData: {},
|
|
18225
|
+
className: "",
|
|
18226
|
+
ignoreResult: {
|
|
18227
|
+
type: "ignore",
|
|
18228
|
+
reason: missingCodeReason
|
|
18229
|
+
}
|
|
18230
|
+
};
|
|
17959
18231
|
}
|
|
17960
|
-
|
|
18232
|
+
codeContent = "";
|
|
18233
|
+
logger_logger.warn(external_chalk_default().yellow(`Form template ${relativePath} has no code file ${codeRelativePath} yet; creating ${className} with empty code.`));
|
|
18234
|
+
}
|
|
17961
18235
|
const formData = (0, form_template_parser_namespaceObject.buildFormTemplateSavePayload)(loadResult.source, {
|
|
17962
18236
|
className,
|
|
17963
18237
|
code: codeContent,
|
|
@@ -17969,6 +18243,9 @@ print(json.dumps({"issues": issues}))
|
|
|
17969
18243
|
ignoreResult: null
|
|
17970
18244
|
};
|
|
17971
18245
|
}
|
|
18246
|
+
function isFileNotFoundError(error) {
|
|
18247
|
+
return "object" == typeof error && null !== error && "ENOENT" === error.code;
|
|
18248
|
+
}
|
|
17972
18249
|
async function handleFormTemplateChange(change, context) {
|
|
17973
18250
|
const { editorYaml } = context;
|
|
17974
18251
|
const { relativePath, changeType } = change;
|
|
@@ -17979,10 +18256,11 @@ print(json.dumps({"issues": issues}))
|
|
|
17979
18256
|
};
|
|
17980
18257
|
const kind = templateInfo.kind;
|
|
17981
18258
|
const format = templateInfo.format;
|
|
18259
|
+
const missingCodeBehavior = "add" === changeType ? "create-new-form-if-unknown" : "defer";
|
|
17982
18260
|
const result = await buildFormTemplateData(relativePath, {
|
|
17983
18261
|
kind,
|
|
17984
18262
|
format
|
|
17985
|
-
}, context);
|
|
18263
|
+
}, context, missingCodeBehavior);
|
|
17986
18264
|
if (result.ignoreResult) return result.ignoreResult;
|
|
17987
18265
|
const { formData, className } = result;
|
|
17988
18266
|
return handleIdBasedOperation({
|
|
@@ -18268,10 +18546,13 @@ print(json.dumps({"issues": issues}))
|
|
|
18268
18546
|
code: error.type,
|
|
18269
18547
|
recoverability: "requires_user_action",
|
|
18270
18548
|
requiresUserAction: true,
|
|
18271
|
-
suggestedAction: "Inspect the repository state and resolve the Git error before retrying.",
|
|
18549
|
+
suggestedAction: isIndexLockFailure(error) ? "Confirm no Git process is running for this repository, then remove the stale .git/index.lock file and retry." : "Inspect the repository state and resolve the Git error before retrying.",
|
|
18272
18550
|
gitCommand: "git_command_failed" === error.type ? error.command : void 0
|
|
18273
18551
|
};
|
|
18274
18552
|
}
|
|
18553
|
+
function isIndexLockFailure(error) {
|
|
18554
|
+
return "git_command_failed" === error.type && error.message.includes("Unable to create") && error.message.includes("index.lock") && error.message.includes("File exists");
|
|
18555
|
+
}
|
|
18275
18556
|
function networkDiagnostic(error) {
|
|
18276
18557
|
return {
|
|
18277
18558
|
code: error.type,
|
|
@@ -18356,6 +18637,15 @@ print(json.dumps({"issues": issues}))
|
|
|
18356
18637
|
this.hasPendingChanges = true;
|
|
18357
18638
|
await this.processSaveBatchWithEvents();
|
|
18358
18639
|
}
|
|
18640
|
+
async flush() {
|
|
18641
|
+
const requireReady = ()=>{
|
|
18642
|
+
if (this.config.isPaused() || this.config.isCleanedUp() || !this.config.isSettlePeriodOver()) throw new Error("Cannot flush saves while watching is paused, closed, or changing branch");
|
|
18643
|
+
};
|
|
18644
|
+
requireReady();
|
|
18645
|
+
await this.forceSave();
|
|
18646
|
+
requireReady();
|
|
18647
|
+
if (this.hasPendingChanges) throw new Error("Saves remain pending");
|
|
18648
|
+
}
|
|
18359
18649
|
cleanup() {
|
|
18360
18650
|
if (this.hasPendingChanges) {
|
|
18361
18651
|
logger_logger.progressEnd("sync");
|
|
@@ -18452,10 +18742,7 @@ print(json.dumps({"issues": issues}))
|
|
|
18452
18742
|
status = await this.config.gitService.getStatus();
|
|
18453
18743
|
} catch (e) {
|
|
18454
18744
|
logger_logger.error(external_chalk_default().red(`Error reading git status: ${errors_getErrorMessage(e)}`));
|
|
18455
|
-
|
|
18456
|
-
skipped: true,
|
|
18457
|
-
reason: "Git status error"
|
|
18458
|
-
};
|
|
18745
|
+
throw e;
|
|
18459
18746
|
}
|
|
18460
18747
|
let allChanges;
|
|
18461
18748
|
if (this.config.stagedOnly) {
|
|
@@ -19301,6 +19588,7 @@ print(json.dumps({"issues": issues}))
|
|
|
19301
19588
|
try {
|
|
19302
19589
|
const previousSubscriptions = this.wsClient?.getDependencySubscriptions() ?? [];
|
|
19303
19590
|
const { response, addedGitignoreEntry } = await refreshDependencyCache(this.repoPath, this.appId, this.anvilUrl, {
|
|
19591
|
+
username: this.username,
|
|
19304
19592
|
includeDocs: true
|
|
19305
19593
|
});
|
|
19306
19594
|
if (addedGitignoreEntry) logger_logger.info("Added /.anvil/ to .gitignore");
|
|
@@ -19612,6 +19900,12 @@ print(json.dumps({"issues": issues}))
|
|
|
19612
19900
|
if (!this.saveProcessor) return;
|
|
19613
19901
|
return this.saveProcessor.forceSave();
|
|
19614
19902
|
}
|
|
19903
|
+
async flushSaves() {
|
|
19904
|
+
if (!this.saveProcessor || this.isSwitchingForEnvironment) throw new Error("App synchronization is not ready");
|
|
19905
|
+
const branch = this.currentBranch;
|
|
19906
|
+
await this.saveProcessor.flush();
|
|
19907
|
+
if (this.isSwitchingForEnvironment || this.currentBranch !== branch || await this.gitService.getCurrentBranch() !== branch) throw new Error("App branch changed while flushing saves");
|
|
19908
|
+
}
|
|
19615
19909
|
async getSuccessMessage() {
|
|
19616
19910
|
try {
|
|
19617
19911
|
const info = await this.gitService.getCommitInfo();
|
|
@@ -19872,6 +20166,182 @@ print(json.dumps({"issues": issues}))
|
|
|
19872
20166
|
}
|
|
19873
20167
|
throw new Error("Device login expired before it was approved.");
|
|
19874
20168
|
}
|
|
20169
|
+
const CHECKOUT_ERROR_VALUE = "__ERROR__";
|
|
20170
|
+
function isAbortLikeError(error) {
|
|
20171
|
+
if (error instanceof Error && "AbortError" === error.name) return true;
|
|
20172
|
+
if ("object" == typeof error && null !== error) {
|
|
20173
|
+
const maybeType = error.type;
|
|
20174
|
+
const maybeMessage = error.message;
|
|
20175
|
+
if ("network_error" === maybeType && "string" == typeof maybeMessage && maybeMessage.toLowerCase().includes("abort")) return true;
|
|
20176
|
+
}
|
|
20177
|
+
const message = errors_getErrorMessage(error).toLowerCase();
|
|
20178
|
+
return "aborted" === message || message.includes("aborted");
|
|
20179
|
+
}
|
|
20180
|
+
function normalizePickerQuery(term) {
|
|
20181
|
+
return (term || "").trim();
|
|
20182
|
+
}
|
|
20183
|
+
function parseNonNegativeMs(raw) {
|
|
20184
|
+
if (null == raw) return null;
|
|
20185
|
+
const parsed = Number(raw);
|
|
20186
|
+
if (!Number.isFinite(parsed) || parsed < 0) return null;
|
|
20187
|
+
return parsed;
|
|
20188
|
+
}
|
|
20189
|
+
function getCheckoutPickerDelayMs() {
|
|
20190
|
+
const fromEnv = parseNonNegativeMs(process.env.ANVIL_CHECKOUT_PICKER_DELAY_MS);
|
|
20191
|
+
if (null != fromEnv) return fromEnv;
|
|
20192
|
+
return 0;
|
|
20193
|
+
}
|
|
20194
|
+
function getCheckoutPickerPaginationDelayMs() {
|
|
20195
|
+
return getCheckoutPickerDelayMs();
|
|
20196
|
+
}
|
|
20197
|
+
function formatLastEdited(lastEdited) {
|
|
20198
|
+
if (!lastEdited) return;
|
|
20199
|
+
return external_chalk_default().gray(`Last edited: ${lastEdited}`);
|
|
20200
|
+
}
|
|
20201
|
+
function formatAppPickerLabel(app) {
|
|
20202
|
+
const appName = app.app_name || "Unnamed App";
|
|
20203
|
+
return `${external_chalk_default().cyan(appName)} ${external_chalk_default().gray(`(${app.app_id})`)}`;
|
|
20204
|
+
}
|
|
20205
|
+
async function waitForDebounce(ms, signal) {
|
|
20206
|
+
if (ms <= 0) return;
|
|
20207
|
+
if (signal.aborted) throw new Error("aborted");
|
|
20208
|
+
await new Promise((resolve, reject)=>{
|
|
20209
|
+
const timer = setTimeout(()=>{
|
|
20210
|
+
cleanup();
|
|
20211
|
+
resolve();
|
|
20212
|
+
}, ms);
|
|
20213
|
+
const onAbort = ()=>{
|
|
20214
|
+
cleanup();
|
|
20215
|
+
reject(new Error("aborted"));
|
|
20216
|
+
};
|
|
20217
|
+
const cleanup = ()=>{
|
|
20218
|
+
clearTimeout(timer);
|
|
20219
|
+
signal.removeEventListener("abort", onAbort);
|
|
20220
|
+
};
|
|
20221
|
+
signal.addEventListener("abort", onAbort, {
|
|
20222
|
+
once: true
|
|
20223
|
+
});
|
|
20224
|
+
});
|
|
20225
|
+
}
|
|
20226
|
+
function createCheckoutPickerSource(anvilUrl, username, deps, options = {}) {
|
|
20227
|
+
const pageSize = options.pageSize ?? 20;
|
|
20228
|
+
const debounceMs = options.debounceMs ?? 200;
|
|
20229
|
+
const staleTimeMs = options.staleTimeMs ?? 30000;
|
|
20230
|
+
const maxAutoPagesPerCall = options.maxAutoPagesPerCall ?? 8;
|
|
20231
|
+
const initialQuery = normalizePickerQuery(options.initialQuery);
|
|
20232
|
+
const simulatedDelayMs = getCheckoutPickerDelayMs();
|
|
20233
|
+
const paginationDelayMs = getCheckoutPickerPaginationDelayMs();
|
|
20234
|
+
const cache = new Map();
|
|
20235
|
+
const getState = (query)=>{
|
|
20236
|
+
const existing = cache.get(query);
|
|
20237
|
+
if (existing) return existing;
|
|
20238
|
+
const created = {
|
|
20239
|
+
items: [],
|
|
20240
|
+
nextCursor: null,
|
|
20241
|
+
exhausted: false,
|
|
20242
|
+
lastError: null,
|
|
20243
|
+
updatedAt: 0,
|
|
20244
|
+
inFlight: null
|
|
20245
|
+
};
|
|
20246
|
+
cache.set(query, created);
|
|
20247
|
+
return created;
|
|
20248
|
+
};
|
|
20249
|
+
const fetchPage = async (query, signal, options = {})=>{
|
|
20250
|
+
const state = getState(query);
|
|
20251
|
+
if (state.inFlight) return void await state.inFlight;
|
|
20252
|
+
state.inFlight = (async ()=>{
|
|
20253
|
+
try {
|
|
20254
|
+
if (simulatedDelayMs > 0) await new Promise((resolve)=>setTimeout(resolve, simulatedDelayMs));
|
|
20255
|
+
if (options.cursor && paginationDelayMs > 0) await new Promise((resolve)=>setTimeout(resolve, paginationDelayMs));
|
|
20256
|
+
const response = await deps.listAppsForCheckout({
|
|
20257
|
+
anvilUrl,
|
|
20258
|
+
username,
|
|
20259
|
+
limit: pageSize,
|
|
20260
|
+
cursor: options.cursor,
|
|
20261
|
+
q: query || void 0,
|
|
20262
|
+
signal
|
|
20263
|
+
});
|
|
20264
|
+
if (options.reset) state.items = [
|
|
20265
|
+
...response.apps
|
|
20266
|
+
];
|
|
20267
|
+
else state.items.push(...response.apps);
|
|
20268
|
+
state.nextCursor = response.next_cursor;
|
|
20269
|
+
state.exhausted = !response.next_cursor;
|
|
20270
|
+
state.lastError = null;
|
|
20271
|
+
state.updatedAt = Date.now();
|
|
20272
|
+
} catch (e) {
|
|
20273
|
+
if (!signal.aborted && !isAbortLikeError(e)) {
|
|
20274
|
+
const message = errors_getErrorMessage(e);
|
|
20275
|
+
state.lastError = message;
|
|
20276
|
+
state.updatedAt = Date.now();
|
|
20277
|
+
}
|
|
20278
|
+
} finally{
|
|
20279
|
+
state.inFlight = null;
|
|
20280
|
+
}
|
|
20281
|
+
})();
|
|
20282
|
+
await state.inFlight;
|
|
20283
|
+
};
|
|
20284
|
+
const ensureLoaded = async (query, signal)=>{
|
|
20285
|
+
const state = getState(query);
|
|
20286
|
+
const isStale = Date.now() - state.updatedAt > staleTimeMs;
|
|
20287
|
+
if (0 === state.items.length) {
|
|
20288
|
+
await waitForDebounce(debounceMs, signal);
|
|
20289
|
+
await fetchPage(query, signal, {
|
|
20290
|
+
cursor: void 0,
|
|
20291
|
+
reset: false
|
|
20292
|
+
});
|
|
20293
|
+
} else if (state.lastError) {
|
|
20294
|
+
await waitForDebounce(debounceMs, signal);
|
|
20295
|
+
await fetchPage(query, signal, {
|
|
20296
|
+
cursor: void 0,
|
|
20297
|
+
reset: true
|
|
20298
|
+
});
|
|
20299
|
+
} else if (isStale) {
|
|
20300
|
+
await waitForDebounce(debounceMs, signal);
|
|
20301
|
+
await fetchPage(query, signal, {
|
|
20302
|
+
cursor: void 0,
|
|
20303
|
+
reset: true
|
|
20304
|
+
});
|
|
20305
|
+
}
|
|
20306
|
+
let pagesLoaded = 0;
|
|
20307
|
+
while(state.nextCursor && pagesLoaded < maxAutoPagesPerCall){
|
|
20308
|
+
if (signal.aborted) throw new Error("aborted");
|
|
20309
|
+
const cursor = state.nextCursor;
|
|
20310
|
+
await fetchPage(query, signal, {
|
|
20311
|
+
cursor,
|
|
20312
|
+
reset: false
|
|
20313
|
+
});
|
|
20314
|
+
pagesLoaded += 1;
|
|
20315
|
+
}
|
|
20316
|
+
};
|
|
20317
|
+
return {
|
|
20318
|
+
source: async (term, { signal })=>{
|
|
20319
|
+
const query = normalizePickerQuery(term) || initialQuery;
|
|
20320
|
+
if (signal.aborted) return [];
|
|
20321
|
+
await ensureLoaded(query, signal);
|
|
20322
|
+
const state = getState(query);
|
|
20323
|
+
if (state.lastError) return [
|
|
20324
|
+
{
|
|
20325
|
+
name: `${external_chalk_default().yellow("Fetch failed")} ${external_chalk_default().gray(`(${state.lastError})`)}`,
|
|
20326
|
+
value: CHECKOUT_ERROR_VALUE,
|
|
20327
|
+
disabled: true
|
|
20328
|
+
}
|
|
20329
|
+
];
|
|
20330
|
+
const summary = state.nextCursor ? external_chalk_default().gray(`Loaded ${state.items.length} apps • more available`) : external_chalk_default().gray(`Loaded ${state.items.length} apps`);
|
|
20331
|
+
const choices = state.items.map((app)=>({
|
|
20332
|
+
name: formatAppPickerLabel(app),
|
|
20333
|
+
value: app.app_id,
|
|
20334
|
+
description: formatLastEdited(app.last_edited)
|
|
20335
|
+
}));
|
|
20336
|
+
choices.unshift({
|
|
20337
|
+
name: summary,
|
|
20338
|
+
value: CHECKOUT_ERROR_VALUE,
|
|
20339
|
+
disabled: true
|
|
20340
|
+
});
|
|
20341
|
+
return choices;
|
|
20342
|
+
}
|
|
20343
|
+
};
|
|
20344
|
+
}
|
|
19875
20345
|
require("inquirer");
|
|
19876
20346
|
require("ora");
|
|
19877
20347
|
const defaultGitAuthDoctorDeps = {
|
|
@@ -19950,29 +20420,30 @@ print(json.dumps({"issues": issues}))
|
|
|
19950
20420
|
return;
|
|
19951
20421
|
}
|
|
19952
20422
|
}
|
|
19953
|
-
function inspectCredentialHelperCommand(raw) {
|
|
20423
|
+
function inspectCredentialHelperCommand(raw, options = {}) {
|
|
19954
20424
|
const details = {
|
|
19955
20425
|
raw
|
|
19956
20426
|
};
|
|
19957
20427
|
try {
|
|
19958
20428
|
const parsed = parseCredentialHelperCommand(raw);
|
|
19959
20429
|
details.execPath = parsed.execPath;
|
|
19960
|
-
details.helperPath = parsed.helperPath;
|
|
20430
|
+
details.helperPath = parsed.helperPath === GIT_CREDENTIAL_HELPER_LOCATOR && options.gitCommonDir ? external_path_default().join(options.gitCommonDir, GIT_CREDENTIAL_HELPER_FILENAME) : parsed.helperPath;
|
|
19961
20431
|
} catch (e) {
|
|
19962
20432
|
details.parseError = e.message;
|
|
19963
20433
|
return details;
|
|
19964
20434
|
}
|
|
19965
20435
|
if (details.execPath) details.execExists = external_fs_default().existsSync(details.execPath);
|
|
19966
|
-
|
|
19967
|
-
|
|
20436
|
+
const helperPath = details.helperPath;
|
|
20437
|
+
if (helperPath && helperPath !== GIT_CREDENTIAL_HELPER_LOCATOR) {
|
|
20438
|
+
details.helperExists = external_fs_default().existsSync(helperPath);
|
|
19968
20439
|
if (details.helperExists) {
|
|
19969
20440
|
try {
|
|
19970
|
-
const stat = external_fs_default().statSync(
|
|
20441
|
+
const stat = external_fs_default().statSync(helperPath);
|
|
19971
20442
|
details.helperExecutable = (73 & stat.mode) !== 0;
|
|
19972
20443
|
} catch {
|
|
19973
20444
|
details.helperExecutable = false;
|
|
19974
20445
|
}
|
|
19975
|
-
details.modulePath = readHelperModulePath(
|
|
20446
|
+
details.modulePath = readHelperModulePath(helperPath);
|
|
19976
20447
|
if (details.modulePath) details.moduleExists = external_fs_default().existsSync(details.modulePath);
|
|
19977
20448
|
}
|
|
19978
20449
|
}
|
|
@@ -20071,7 +20542,7 @@ print(json.dumps({"issues": issues}))
|
|
|
20071
20542
|
child.stdin.end(input);
|
|
20072
20543
|
});
|
|
20073
20544
|
}
|
|
20074
|
-
async function buildAppReport(repoPath, remote, deps) {
|
|
20545
|
+
async function buildAppReport(repoPath, gitCommonDir, remote, deps) {
|
|
20075
20546
|
const appId = remote.appId;
|
|
20076
20547
|
const anvilUrl = remote.anvilUrl;
|
|
20077
20548
|
const url = new URL(anvilUrl);
|
|
@@ -20079,7 +20550,9 @@ print(json.dumps({"issues": issues}))
|
|
|
20079
20550
|
const helperConfigKey = `credential.${credentialScope}.helper`;
|
|
20080
20551
|
const helperValues = await getLocalConfigValues(repoPath, helperConfigKey);
|
|
20081
20552
|
const nonEmptyHelperValues = helperValues.filter((value)=>"" !== value.trim());
|
|
20082
|
-
const helperDetails = nonEmptyHelperValues.map(inspectCredentialHelperCommand
|
|
20553
|
+
const helperDetails = nonEmptyHelperValues.map((value)=>inspectCredentialHelperCommand(value, {
|
|
20554
|
+
gitCommonDir
|
|
20555
|
+
}));
|
|
20083
20556
|
const findings = [];
|
|
20084
20557
|
if (0 === helperValues.length) findings.push(finding("error", "missing_helper_config", `No local ${helperConfigKey} entries found.`));
|
|
20085
20558
|
else if (0 === nonEmptyHelperValues.length) findings.push(finding("error", "missing_helper_command", `${helperConfigKey} only resets helpers; no Anvil helper command is configured.`));
|
|
@@ -20158,12 +20631,18 @@ print(json.dumps({"issues": issues}))
|
|
|
20158
20631
|
"--git-dir"
|
|
20159
20632
|
])).trim();
|
|
20160
20633
|
const gitDir = external_path_default().isAbsolute(rawGitDir) ? rawGitDir : external_path_default().resolve(inputPath, rawGitDir);
|
|
20634
|
+
const rawGitCommonDir = (await git.raw([
|
|
20635
|
+
"rev-parse",
|
|
20636
|
+
"--git-common-dir"
|
|
20637
|
+
])).trim();
|
|
20638
|
+
const resolvedGitCommonDir = external_path_default().isAbsolute(rawGitCommonDir) ? rawGitCommonDir : external_path_default().resolve(inputPath, rawGitCommonDir);
|
|
20639
|
+
const gitCommonDir = external_fs_default().realpathSync(resolvedGitCommonDir);
|
|
20161
20640
|
auth_setRepoContext(repoPath);
|
|
20162
20641
|
const remotes = (await git.getRemotes(true)).map(detectAnvilRemote);
|
|
20163
20642
|
const anvilRemotes = remotes.filter((remote)=>remote.appId && remote.anvilUrl && "https" === remote.transport);
|
|
20164
20643
|
const findings = [];
|
|
20165
20644
|
if (0 === anvilRemotes.length) findings.push(finding("error", "missing_anvil_https_remote", "No HTTPS Anvil git remotes were detected."));
|
|
20166
|
-
const apps = await Promise.all(anvilRemotes.map((remote)=>buildAppReport(repoPath, remote, deps)));
|
|
20645
|
+
const apps = await Promise.all(anvilRemotes.map((remote)=>buildAppReport(repoPath, gitCommonDir, remote, deps)));
|
|
20167
20646
|
const status = getHighestStatus([
|
|
20168
20647
|
...findings.map((item)=>item.status),
|
|
20169
20648
|
...apps.flatMap((app)=>app.findings.map((item)=>item.status))
|
|
@@ -20171,6 +20650,7 @@ print(json.dumps({"issues": issues}))
|
|
|
20171
20650
|
return {
|
|
20172
20651
|
repoPath,
|
|
20173
20652
|
gitDir,
|
|
20653
|
+
gitCommonDir,
|
|
20174
20654
|
remotes,
|
|
20175
20655
|
apps,
|
|
20176
20656
|
findings,
|
|
@@ -20187,6 +20667,7 @@ print(json.dumps({"issues": issues}))
|
|
|
20187
20667
|
lines.push(external_chalk_default().bold("Git auth doctor"));
|
|
20188
20668
|
lines.push(`Repository: ${report.repoPath}`);
|
|
20189
20669
|
lines.push(`Git dir: ${report.gitDir}`);
|
|
20670
|
+
lines.push(`Git common dir: ${report.gitCommonDir}`);
|
|
20190
20671
|
lines.push(`Overall: ${formatStatus(report.status)}`);
|
|
20191
20672
|
lines.push("");
|
|
20192
20673
|
lines.push(external_chalk_default().bold("Detected remotes:"));
|
|
@@ -20295,7 +20776,29 @@ print(json.dumps({"issues": issues}))
|
|
|
20295
20776
|
const response = await buildGitCredentialResponse(operation, request, options);
|
|
20296
20777
|
writeGitCredentialResponse(response);
|
|
20297
20778
|
}
|
|
20298
|
-
|
|
20779
|
+
const MODEL_TOKEN_LOCK_TIMEOUT_MS = 12000;
|
|
20780
|
+
const modelToken_defaultDeps = {
|
|
20781
|
+
getValidAuthToken: auth_getValidAuthToken,
|
|
20782
|
+
readAuthFromRepo: readAuthFromRepo,
|
|
20783
|
+
cwd: ()=>process.cwd()
|
|
20784
|
+
};
|
|
20785
|
+
async function getModelToken(options = {}) {
|
|
20786
|
+
const deps = options.deps ?? modelToken_defaultDeps;
|
|
20787
|
+
const repoPath = options.repoPath ?? deps.cwd();
|
|
20788
|
+
auth_setRepoContext(repoPath);
|
|
20789
|
+
if (!process.env["ANVIL_AUTH_FILE_ENCRYPTION_KEY"]) throw new Error("ANVIL_AUTH_FILE_ENCRYPTION_KEY is not set; cannot read repo auth tokens.");
|
|
20790
|
+
const auth = await deps.readAuthFromRepo(repoPath);
|
|
20791
|
+
if (!auth) throw new Error(`No Anvil auth tokens found for the repository at ${repoPath}.`);
|
|
20792
|
+
return deps.getValidAuthToken(auth.anvilUrl, auth.username, {
|
|
20793
|
+
lock: {
|
|
20794
|
+
timeoutMs: options.lockTimeoutMs ?? MODEL_TOKEN_LOCK_TIMEOUT_MS
|
|
20795
|
+
}
|
|
20796
|
+
});
|
|
20797
|
+
}
|
|
20798
|
+
async function executeModelTokenOperation(options = {}) {
|
|
20799
|
+
const token = await getModelToken(options);
|
|
20800
|
+
process.stdout.write(`${token}\n`);
|
|
20801
|
+
}
|
|
20299
20802
|
async function watch_decideFallbackUrl(explicitUrl, availableUrls) {
|
|
20300
20803
|
if (explicitUrl) return {
|
|
20301
20804
|
source: "explicit",
|
|
@@ -20506,7 +21009,6 @@ print(json.dumps({"issues": issues}))
|
|
|
20506
21009
|
remoteName
|
|
20507
21010
|
};
|
|
20508
21011
|
}
|
|
20509
|
-
const api_DEFAULT_ANVIL_URL = resolveAnvilUrl();
|
|
20510
21012
|
async function api_checkUncommittedChanges(repoPath) {
|
|
20511
21013
|
try {
|
|
20512
21014
|
const git = esm_default(repoPath);
|
|
@@ -20555,9 +21057,10 @@ print(json.dumps({"issues": issues}))
|
|
|
20555
21057
|
}
|
|
20556
21058
|
}
|
|
20557
21059
|
async function src_api_watch(repoPath, appId, options = {}) {
|
|
20558
|
-
const { anvilUrl = api_DEFAULT_ANVIL_URL, gitUrl = anvilUrl, stagedOnly = false, username } = options;
|
|
20559
21060
|
repoPath = external_path_default().resolve(repoPath);
|
|
20560
21061
|
auth_setRepoContext(repoPath);
|
|
21062
|
+
const anvilUrl = options.anvilUrl ?? await auth_resolveAuthAnvilUrl();
|
|
21063
|
+
const { gitUrl = anvilUrl, stagedOnly = false, username } = options;
|
|
20561
21064
|
const storedEnvPid = options.environment?.envPid ? void 0 : await environment_readEnvironmentPid(repoPath);
|
|
20562
21065
|
const environment = options.environment ?? (storedEnvPid ? {
|
|
20563
21066
|
envPid: storedEnvPid
|
|
@@ -20672,6 +21175,7 @@ print(json.dumps({"issues": issues}))
|
|
|
20672
21175
|
}
|
|
20673
21176
|
})();
|
|
20674
21177
|
exports.ANVIL_SYNC_CLIENT_ID = __webpack_exports__.ANVIL_SYNC_CLIENT_ID;
|
|
21178
|
+
exports.AUTH_FILE_LOCK_DEFAULTS = __webpack_exports__.AUTH_FILE_LOCK_DEFAULTS;
|
|
20675
21179
|
exports.AnvilYamlValidationResult = __webpack_exports__.AnvilYamlValidationResult;
|
|
20676
21180
|
exports.AppIdCandidate = __webpack_exports__.AppIdCandidate;
|
|
20677
21181
|
exports.AppIdWithContext = __webpack_exports__.AppIdWithContext;
|
|
@@ -20683,11 +21187,15 @@ exports.CheckoutOptions = __webpack_exports__.CheckoutOptions;
|
|
|
20683
21187
|
exports.CheckoutResult = __webpack_exports__.CheckoutResult;
|
|
20684
21188
|
exports.DeviceAuthorizationResponse = __webpack_exports__.DeviceAuthorizationResponse;
|
|
20685
21189
|
exports.FormTemplateValidationResult = __webpack_exports__.FormTemplateValidationResult;
|
|
21190
|
+
exports.GetValidAuthTokenOptions = __webpack_exports__.GetValidAuthTokenOptions;
|
|
20686
21191
|
exports.LoginResult = __webpack_exports__.LoginResult;
|
|
20687
21192
|
exports.LogoutResult = __webpack_exports__.LogoutResult;
|
|
21193
|
+
exports.MODEL_TOKEN_HELPER_FILENAME = __webpack_exports__.MODEL_TOKEN_HELPER_FILENAME;
|
|
21194
|
+
exports.MODEL_TOKEN_LOCK_TIMEOUT_MS = __webpack_exports__.MODEL_TOKEN_LOCK_TIMEOUT_MS;
|
|
20688
21195
|
exports.OAuthTokenResponse = __webpack_exports__.OAuthTokenResponse;
|
|
20689
21196
|
exports.ParsedCheckoutInput = __webpack_exports__.ParsedCheckoutInput;
|
|
20690
21197
|
exports.PreferredEditor = __webpack_exports__.PreferredEditor;
|
|
21198
|
+
exports.REFRESH_REQUEST_TIMEOUT_MS = __webpack_exports__.REFRESH_REQUEST_TIMEOUT_MS;
|
|
20691
21199
|
exports.SettableConfigKey = __webpack_exports__.SettableConfigKey;
|
|
20692
21200
|
exports.TokenData = __webpack_exports__.TokenData;
|
|
20693
21201
|
exports.ValidateAppIdResponse = __webpack_exports__.ValidateAppIdResponse;
|
|
@@ -20700,6 +21208,7 @@ exports.branchFromEnvironmentVersion = __webpack_exports__.branchFromEnvironment
|
|
|
20700
21208
|
exports.buildGitAuthDoctorReport = __webpack_exports__.buildGitAuthDoctorReport;
|
|
20701
21209
|
exports.checkUncommittedChanges = __webpack_exports__.checkUncommittedChanges;
|
|
20702
21210
|
exports.clearInMemoryTokens = __webpack_exports__.clearInMemoryTokens;
|
|
21211
|
+
exports.createCheckoutPickerSource = __webpack_exports__.createCheckoutPickerSource;
|
|
20703
21212
|
exports.createWatchDiagnostic = __webpack_exports__.createWatchDiagnostic;
|
|
20704
21213
|
exports.createWatchDiagnosticFromMessage = __webpack_exports__.createWatchDiagnosticFromMessage;
|
|
20705
21214
|
exports.deleteConfig = __webpack_exports__.deleteConfig;
|
|
@@ -20707,6 +21216,7 @@ exports.detectAppIdsByCommitLookup = __webpack_exports__.detectAppIdsByCommitLoo
|
|
|
20707
21216
|
exports.detectAppIdsFromAllRemotes = __webpack_exports__.detectAppIdsFromAllRemotes;
|
|
20708
21217
|
exports.executeCheckout = __webpack_exports__.executeCheckout;
|
|
20709
21218
|
exports.executeGitCredentialOperation = __webpack_exports__.executeGitCredentialOperation;
|
|
21219
|
+
exports.executeModelTokenOperation = __webpack_exports__.executeModelTokenOperation;
|
|
20710
21220
|
exports.filterCandidates = __webpack_exports__.filterCandidates;
|
|
20711
21221
|
exports.formatCandidateLabel = __webpack_exports__.formatCandidateLabel;
|
|
20712
21222
|
exports.formatGitAuthDoctorReport = __webpack_exports__.formatGitAuthDoctorReport;
|
|
@@ -20720,12 +21230,15 @@ exports.getGitFetchUrl = __webpack_exports__.getGitFetchUrl;
|
|
|
20720
21230
|
exports.getGitPushUrl = __webpack_exports__.getGitPushUrl;
|
|
20721
21231
|
exports.getInstalledPreferredEditors = __webpack_exports__.getInstalledPreferredEditors;
|
|
20722
21232
|
exports.getLatestVersion = __webpack_exports__.getLatestVersion;
|
|
21233
|
+
exports.getModelToken = __webpack_exports__.getModelToken;
|
|
21234
|
+
exports.getModelTokenHelperScript = __webpack_exports__.getModelTokenHelperScript;
|
|
20723
21235
|
exports.getPreferredEditorCommand = __webpack_exports__.getPreferredEditorCommand;
|
|
20724
21236
|
exports.getSettableConfigKeys = __webpack_exports__.getSettableConfigKeys;
|
|
20725
21237
|
exports.getValidAuthToken = __webpack_exports__.getValidAuthToken;
|
|
20726
21238
|
exports.getWebSocketUrl = __webpack_exports__.getWebSocketUrl;
|
|
20727
21239
|
exports.hasTokensForUrl = __webpack_exports__.hasTokensForUrl;
|
|
20728
21240
|
exports.inspectCredentialHelperCommand = __webpack_exports__.inspectCredentialHelperCommand;
|
|
21241
|
+
exports.installModelTokenHelperScript = __webpack_exports__.installModelTokenHelperScript;
|
|
20729
21242
|
exports.isCommandAvailable = __webpack_exports__.isCommandAvailable;
|
|
20730
21243
|
exports.isDirectoryNonEmpty = __webpack_exports__.isDirectoryNonEmpty;
|
|
20731
21244
|
exports.isPathInsideGitRepo = __webpack_exports__.isPathInsideGitRepo;
|
|
@@ -20761,9 +21274,11 @@ exports.validatePath = __webpack_exports__.validatePath;
|
|
|
20761
21274
|
exports.verifyAndStoreTokens = __webpack_exports__.verifyAndStoreTokens;
|
|
20762
21275
|
exports.verifyAuth = __webpack_exports__.verifyAuth;
|
|
20763
21276
|
exports.watch = __webpack_exports__.watch;
|
|
21277
|
+
exports.withRepoAuthFileLock = __webpack_exports__.withRepoAuthFileLock;
|
|
20764
21278
|
exports.writeEnvironmentPid = __webpack_exports__.writeEnvironmentPid;
|
|
20765
21279
|
for(var __rspack_i in __webpack_exports__)if (-1 === [
|
|
20766
21280
|
"ANVIL_SYNC_CLIENT_ID",
|
|
21281
|
+
"AUTH_FILE_LOCK_DEFAULTS",
|
|
20767
21282
|
"AnvilYamlValidationResult",
|
|
20768
21283
|
"AppIdCandidate",
|
|
20769
21284
|
"AppIdWithContext",
|
|
@@ -20775,11 +21290,15 @@ for(var __rspack_i in __webpack_exports__)if (-1 === [
|
|
|
20775
21290
|
"CheckoutResult",
|
|
20776
21291
|
"DeviceAuthorizationResponse",
|
|
20777
21292
|
"FormTemplateValidationResult",
|
|
21293
|
+
"GetValidAuthTokenOptions",
|
|
20778
21294
|
"LoginResult",
|
|
20779
21295
|
"LogoutResult",
|
|
21296
|
+
"MODEL_TOKEN_HELPER_FILENAME",
|
|
21297
|
+
"MODEL_TOKEN_LOCK_TIMEOUT_MS",
|
|
20780
21298
|
"OAuthTokenResponse",
|
|
20781
21299
|
"ParsedCheckoutInput",
|
|
20782
21300
|
"PreferredEditor",
|
|
21301
|
+
"REFRESH_REQUEST_TIMEOUT_MS",
|
|
20783
21302
|
"SettableConfigKey",
|
|
20784
21303
|
"TokenData",
|
|
20785
21304
|
"ValidateAppIdResponse",
|
|
@@ -20792,6 +21311,7 @@ for(var __rspack_i in __webpack_exports__)if (-1 === [
|
|
|
20792
21311
|
"buildGitAuthDoctorReport",
|
|
20793
21312
|
"checkUncommittedChanges",
|
|
20794
21313
|
"clearInMemoryTokens",
|
|
21314
|
+
"createCheckoutPickerSource",
|
|
20795
21315
|
"createWatchDiagnostic",
|
|
20796
21316
|
"createWatchDiagnosticFromMessage",
|
|
20797
21317
|
"deleteConfig",
|
|
@@ -20799,6 +21319,7 @@ for(var __rspack_i in __webpack_exports__)if (-1 === [
|
|
|
20799
21319
|
"detectAppIdsFromAllRemotes",
|
|
20800
21320
|
"executeCheckout",
|
|
20801
21321
|
"executeGitCredentialOperation",
|
|
21322
|
+
"executeModelTokenOperation",
|
|
20802
21323
|
"filterCandidates",
|
|
20803
21324
|
"formatCandidateLabel",
|
|
20804
21325
|
"formatGitAuthDoctorReport",
|
|
@@ -20812,12 +21333,15 @@ for(var __rspack_i in __webpack_exports__)if (-1 === [
|
|
|
20812
21333
|
"getGitPushUrl",
|
|
20813
21334
|
"getInstalledPreferredEditors",
|
|
20814
21335
|
"getLatestVersion",
|
|
21336
|
+
"getModelToken",
|
|
21337
|
+
"getModelTokenHelperScript",
|
|
20815
21338
|
"getPreferredEditorCommand",
|
|
20816
21339
|
"getSettableConfigKeys",
|
|
20817
21340
|
"getValidAuthToken",
|
|
20818
21341
|
"getWebSocketUrl",
|
|
20819
21342
|
"hasTokensForUrl",
|
|
20820
21343
|
"inspectCredentialHelperCommand",
|
|
21344
|
+
"installModelTokenHelperScript",
|
|
20821
21345
|
"isCommandAvailable",
|
|
20822
21346
|
"isDirectoryNonEmpty",
|
|
20823
21347
|
"isPathInsideGitRepo",
|
|
@@ -20853,6 +21377,7 @@ for(var __rspack_i in __webpack_exports__)if (-1 === [
|
|
|
20853
21377
|
"verifyAndStoreTokens",
|
|
20854
21378
|
"verifyAuth",
|
|
20855
21379
|
"watch",
|
|
21380
|
+
"withRepoAuthFileLock",
|
|
20856
21381
|
"writeEnvironmentPid"
|
|
20857
21382
|
].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
|
|
20858
21383
|
Object.defineProperty(exports, '__esModule', {
|