@stacksjs/ts-cloud 0.7.20 → 0.7.22

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.
Files changed (40) hide show
  1. package/dist/bin/cli.js +552 -551
  2. package/dist/{chunk-49nmy775.js → chunk-0tfwdnmf.js} +9 -6
  3. package/dist/{chunk-qgnyzfmx.js → chunk-5mejsd43.js} +1 -1
  4. package/dist/{chunk-296n8hvt.js → chunk-c3yy2xyq.js} +2 -2
  5. package/dist/{chunk-qdhn1s7j.js → chunk-js3vrvvn.js} +158 -5
  6. package/dist/{chunk-xgnxz5dz.js → chunk-kcxxwg3j.js} +89 -12
  7. package/dist/deploy/dashboard-site-settings.d.ts +55 -0
  8. package/dist/deploy/dashboard-site-settings.test.d.ts +1 -0
  9. package/dist/deploy/dashboard-throttle.d.ts +46 -0
  10. package/dist/deploy/dashboard-throttle.test.d.ts +1 -0
  11. package/dist/deploy/index.js +4 -4
  12. package/dist/deploy/management-dashboard.d.ts +45 -15
  13. package/dist/drivers/index.js +2 -2
  14. package/dist/drivers/shared/deploy-script.d.ts +7 -0
  15. package/dist/index.js +8 -4
  16. package/dist/ui/index.html +3 -3
  17. package/dist/ui/server/actions.html +3 -3
  18. package/dist/ui/server/backups.html +3 -3
  19. package/dist/ui/server/database.html +3 -3
  20. package/dist/ui/server/deployments.html +3 -3
  21. package/dist/ui/server/firewall.html +3 -3
  22. package/dist/ui/server/logs.html +3 -3
  23. package/dist/ui/server/services.html +3 -3
  24. package/dist/ui/server/sites.html +3 -3
  25. package/dist/ui/server/ssh-keys.html +3 -3
  26. package/dist/ui/server/team.html +3 -3
  27. package/dist/ui/server/terminal.html +3 -3
  28. package/dist/ui/server/workers.html +3 -3
  29. package/dist/ui/serverless/alarms.html +3 -3
  30. package/dist/ui/serverless/assets.html +3 -3
  31. package/dist/ui/serverless/data.html +3 -3
  32. package/dist/ui/serverless/deployments.html +3 -3
  33. package/dist/ui/serverless/functions.html +3 -3
  34. package/dist/ui/serverless/logs.html +3 -3
  35. package/dist/ui/serverless/queues.html +3 -3
  36. package/dist/ui/serverless/scheduler.html +3 -3
  37. package/dist/ui/serverless/secrets.html +3 -3
  38. package/dist/ui/serverless/traces.html +3 -3
  39. package/dist/ui/serverless.html +3 -3
  40. package/package.json +3 -3
@@ -21901,6 +21901,8 @@ function createDashboardSite(options) {
21901
21901
  }
21902
21902
  };
21903
21903
  }
21904
+ var DASHBOARD_STATE_DIR = ".ts-cloud";
21905
+ var DASHBOARD_ENTRY = "./node_modules/@stacksjs/ts-cloud/dist/bin/cli.js";
21904
21906
  function apexOf(domain) {
21905
21907
  const parts = domain.split(".").filter(Boolean);
21906
21908
  return parts.length <= 2 ? domain : parts.slice(-2).join(".");
@@ -21963,7 +21965,7 @@ function resolveManagementDashboardSites(config, environment, opts) {
21963
21965
  return [];
21964
21966
  const auth2 = opts.password ? { auth: { username: opts.username || "admin", password: opts.password, realm: opts.realm } } : {};
21965
21967
  const build = opts.build === false || opts.build === undefined ? {} : { build: opts.build };
21966
- if (opts.live) {
21968
+ if (opts.live !== false) {
21967
21969
  const domain = resolveDashboardDomain(config, environment, opts.domain);
21968
21970
  if (!domain)
21969
21971
  return [];
@@ -21972,11 +21974,12 @@ function resolveManagementDashboardSites(config, environment, opts) {
21972
21974
  root: opts.uiRoot,
21973
21975
  deploy: "server",
21974
21976
  domain,
21975
- start: `cloud dashboard:serve --box --host 127.0.0.1 --port ${port}`,
21977
+ preStart: ["bun install --production --no-save"],
21978
+ start: `bun ${DASHBOARD_ENTRY} dashboard:serve --box --host 127.0.0.1 --port ${port}`,
21976
21979
  port,
21977
- ssl: { provider: "letsencrypt" },
21978
- ...build,
21979
- ...auth2
21980
+ sharedPaths: [DASHBOARD_STATE_DIR],
21981
+ healthCheck: { path: "/login" },
21982
+ ssl: { provider: "letsencrypt" }
21980
21983
  };
21981
21984
  return [{ name: managementDashboardSiteName(domain), site }];
21982
21985
  }
@@ -46152,4 +46155,4 @@ class ResourceManagementManager {
46152
46155
  }
46153
46156
  var resourceManagementManager = new ResourceManagementManager;
46154
46157
 
46155
- export { detectServiceRegion, signRequest, signRequestAsync, createPresignedUrl, createPresignedUrlAsync, makeAWSRequest, makeAWSRequestOnce, makeAWSRequestAsync, parseXMLResponse, parseJSONResponse, clearSigningKeyCache, getSigningKeyCacheSize, isNodeCryptoAvailable, isWebCryptoAvailable, QueuePresets, RealtimePresets, detectDeploymentTargets, resolveDeploymentMode, deploymentCoexistenceError, resolveCloudProvider, TemplateBuilder, Pseudo, generateResourceName, generateLogicalId, getTimestamp, sanitizeName, resolveProjectStackName, resolveSiteStackName, resolveSiteResourceName, resolveSiteBucketName, resolveStorageBucketName, resolveDeployBucketName, resolveServerlessAppStackName, resolveServerlessArtifactBucketName, resolveServerlessAssetBucketName, DependencyGraph, analyzeStackDiff, formatDiff, Storage, Registry, CDN, DNS, Security, Compute, Network, FileSystem, Email, exports_incoming_call, Phone, Queue, JobLoader, StacksIntegration, exports_send, SMS, AI, Database, Cache, Permissions, ApiGateway, Messaging, Workflow, Monitoring, Auth, Deployment, AssetHasher, Secrets, ParameterStore, Search, Redirects, Communication, Fn2, Arn, AWS_PSEUDO_PARAMETERS, CloudFormationBuilder, buildCloudFormationTemplate, createStaticSitePreset, createNodeJsServerPreset, createNodeJsServerlessPreset, createServerlessNodePreset, createFullStackAppPreset, createApiBackendPreset, createWordPressPreset, createJamstackPreset, createMicroservicesPreset, createRealtimeAppPreset, createDataPipelinePreset, createMLApiPreset, createTraditionalWebAppPreset, createLaravelPreset, createServerlessLaravelPreset, createDashboardSite, resolveDashboardDomain, resolveDashboardDomains, hasManagementDashboardSite, MANAGEMENT_DASHBOARD_SITE_PREFIX, managementDashboardSiteName, isManagementDashboardSiteName, resolveManagementDashboardSites, resolveManagementDashboardSite, extendPreset, composePresets, createPreset, mergeInfrastructure, withMonitoring, withSecurity, withDatabase, withCache, withCDN, withQueue, fromEnvironment, fromSharedCredentials, fromEC2Metadata, fromECSMetadata, fromWebIdentity, getCredentials, createCredentialProvider, resolveCredentials, resolveRegion, getAccountId, S3Error, createS3Client, CloudError, ConfigurationError, CredentialError, DeploymentError, ValidationError, AWSAPIError, ErrorCodes, getErrorDetails, createError, DebugLogger, validateConfiguration, detectMisconfigurations, validateCredentials, checkIAMPermissions, getRequiredPermissions, suggestIAMPolicy, DEFAULT_SERVICE_LIMITS, checkServiceQuotas, getQuotaUsageSummary, suggestQuotaIncrease, FileCache, TemplateCache, templateCache, hashFile, hashString, hashBuffer, hashDirectory, hashManifest, quickHash, findChangedFiles, HashCache, parallel, batch, parallelMap, parallelWithRetry, sequence, withTimeout, RateLimiter, chunk, processInChunks, diffTemplates, getDiffSummary, requiresReplacement, categorizeChanges, getDeploymentStrategy, getDiffStats, cloud_config_schema_default, validateAgainstSchema, defaultLocalConfig, getLocalConfig, isLocalDevelopment, getLocalEndpoint, getLocalCredentials, getLocalEnvVars, MockCloudFormation, MockS3, MockDynamoDB, createMockAWS, PreviewEnvironmentManager, previewManager, generatePreviewWorkflow, generateCleanupWorkflow, generateCostReportWorkflow, PreviewNotificationService, previewNotifications, formatTable, formatTree, formatProgressBar, formatBytes, formatDuration, formatList, formatKeyValue, ProgressBar, MultiStepProgress, Spinner, TaskList, suggestCommand, formatSuggestion, isLikelyTypo, getContextualHelp, searchCommands, autocomplete, suggestFlags, formatFlagSuggestions, getCommandUsage, validateCommand, REPL, REPLContext, REPLCommandBuilder, CommandHistory, formatHistory, formatHistoryStats, MultiRegionManager, multiRegionManager, CrossRegionReferenceManager, GlobalResourceManager, RegionPairManager, StackDependencyManager, crossRegionReferenceManager, globalResourceManager, regionPairManager, stackDependencyManager, AWS_REGIONS, getRegion, getAllRegions, getRegionsByLocation, getRegionsByCompliance, getRegionsByPricingTier, isValidRegion, getClosestRegion, suggestRegions, suggestRegionPairs, formatRegion, formatRegionList, getRegionStats, MultiAccountManager, OrganizationManager, multiAccountManager, organizationManager, RECOMMENDED_ACCOUNT_STRUCTURES, RECOMMENDED_SCPS, COMMON_CROSS_ACCOUNT_ROLES, getRecommendedStructure, generateCrossAccountRoleCF, validateAccountStructure, formatAccountStructure, generateDeploymentWorkflow, generateMultiEnvWorkflow, generatePRPreviewWorkflow, generateScheduledWorkflow, generateMatrixWorkflow, generateDeploymentPipeline, generateMultiEnvPipeline, generatePreviewPipeline, generateScheduledPipeline, generateManualPipeline, generateDeploymentConfig, generateMultiEnvConfig, generateScheduledConfig, generateApprovalConfig, generateParallelConfig, BackupManager, backupManager, DisasterRecoveryManager, drManager, AWSConfigManager, awsConfigManager, CloudTrailManager, cloudTrailManager, GuardDutyManager, guardDutyManager, SecurityHubManager, securityHubManager, BlueGreenManager, blueGreenManager, CanaryManager, canaryManager, ABTestManager, abTestManager, ProgressiveDeploymentManager, progressiveDeploymentManager, XRayManager, xrayManager, MetricsManager, metricsManager, LogsManager, logsManager, SyntheticsManager, syntheticsManager, MigrationManager, migrationManager, ReplicaManager, replicaManager, PerformanceManager, performanceManager, DatabaseUserManager, databaseUserManager, SecretsRotationManager, secretsRotationManager, SecretsManager, secretsManager, CertificateManager, certificateManager, SecurityScanningManager, securityScanningManager, ImageScanningManager, imageScanningManager, BuildOptimizationManager, buildOptimizationManager, ContainerRegistryManager, containerRegistryManager, ServiceMeshManager, serviceMeshManager, LambdaLayersManager, lambdaLayersManager, LambdaVersionsManager, lambdaVersionsManager, LambdaConcurrencyManager, lambdaConcurrencyManager, LambdaDestinationsManager, lambdaDestinationsManager, LambdaVPCManager, lambdaVPCManager, LambdaDLQManager, lambdaDLQManager, Route53RoutingManager, route53RoutingManager, DNSSECManager, dnssecManager, Route53ResolverManager, route53ResolverManager, BounceComplaintHandler, bounceComplaintHandler, EmailAnalyticsManager, emailAnalyticsManager, SenderReputationManager, senderReputationManager, EmailTemplateManager, emailTemplateManager, exports_advanced, exports_inbound, exports_advanced2, exports_advanced3, FIFOQueueManager, fifoQueueManager, DLQMonitoringManager, dlqMonitoringManager, BatchProcessingManager, batchProcessingManager, QueueManagementManager, queueManagementManager, createZip, generateBootstrap, runBuildHooks, sha256, artifactKey, packageServerlessApp, MANAGED_NODE_VERSIONS, resolveServerlessRuntime, resolveQueues, resolveQueueNames, composeServerlessAppTemplate, PHP_LAYER_EXTENSIONS, phpLayerPackages, phpLayerBuildStage, generatePhpLayerDockerfile, generateAppImageDockerfile, sharedRuntimeLoop, buildNodeRuntimeLayerZip, buildBunRuntimeLayerZip, resolveApp, eventToRequest, responseToResult, createHttpHandler, createQueueHandler, createCliHandler, createHandlers, generatePhpFpmConfig, phpRuntimeLayerAssets, laravelServerlessEnvDefaults, LARAVEL_SERVERLESS_BUILD_STEPS, buildPhpRuntimeLayerZip, PHP_DEFAULT_EXCLUDES, runPhpBuildHooks, collectPhpAppEntries, packagePhpApp, StaticSiteManager, staticSiteManager, StorageAdvancedManager, storageAdvancedManager, HealthCheckManager, healthCheckManager, NetworkSecurityManager, networkSecurityManager, ResourceManagementManager, resourceManagementManager };
46158
+ export { detectServiceRegion, signRequest, signRequestAsync, createPresignedUrl, createPresignedUrlAsync, makeAWSRequest, makeAWSRequestOnce, makeAWSRequestAsync, parseXMLResponse, parseJSONResponse, clearSigningKeyCache, getSigningKeyCacheSize, isNodeCryptoAvailable, isWebCryptoAvailable, QueuePresets, RealtimePresets, detectDeploymentTargets, resolveDeploymentMode, deploymentCoexistenceError, resolveCloudProvider, TemplateBuilder, Pseudo, generateResourceName, generateLogicalId, getTimestamp, sanitizeName, resolveProjectStackName, resolveSiteStackName, resolveSiteResourceName, resolveSiteBucketName, resolveStorageBucketName, resolveDeployBucketName, resolveServerlessAppStackName, resolveServerlessArtifactBucketName, resolveServerlessAssetBucketName, DependencyGraph, analyzeStackDiff, formatDiff, Storage, Registry, CDN, DNS, Security, Compute, Network, FileSystem, Email, exports_incoming_call, Phone, Queue, JobLoader, StacksIntegration, exports_send, SMS, AI, Database, Cache, Permissions, ApiGateway, Messaging, Workflow, Monitoring, Auth, Deployment, AssetHasher, Secrets, ParameterStore, Search, Redirects, Communication, Fn2, Arn, AWS_PSEUDO_PARAMETERS, CloudFormationBuilder, buildCloudFormationTemplate, createStaticSitePreset, createNodeJsServerPreset, createNodeJsServerlessPreset, createServerlessNodePreset, createFullStackAppPreset, createApiBackendPreset, createWordPressPreset, createJamstackPreset, createMicroservicesPreset, createRealtimeAppPreset, createDataPipelinePreset, createMLApiPreset, createTraditionalWebAppPreset, createLaravelPreset, createServerlessLaravelPreset, createDashboardSite, DASHBOARD_STATE_DIR, DASHBOARD_ENTRY, resolveDashboardDomain, resolveDashboardDomains, hasManagementDashboardSite, MANAGEMENT_DASHBOARD_SITE_PREFIX, managementDashboardSiteName, isManagementDashboardSiteName, resolveManagementDashboardSites, resolveManagementDashboardSite, extendPreset, composePresets, createPreset, mergeInfrastructure, withMonitoring, withSecurity, withDatabase, withCache, withCDN, withQueue, fromEnvironment, fromSharedCredentials, fromEC2Metadata, fromECSMetadata, fromWebIdentity, getCredentials, createCredentialProvider, resolveCredentials, resolveRegion, getAccountId, S3Error, createS3Client, CloudError, ConfigurationError, CredentialError, DeploymentError, ValidationError, AWSAPIError, ErrorCodes, getErrorDetails, createError, DebugLogger, validateConfiguration, detectMisconfigurations, validateCredentials, checkIAMPermissions, getRequiredPermissions, suggestIAMPolicy, DEFAULT_SERVICE_LIMITS, checkServiceQuotas, getQuotaUsageSummary, suggestQuotaIncrease, FileCache, TemplateCache, templateCache, hashFile, hashString, hashBuffer, hashDirectory, hashManifest, quickHash, findChangedFiles, HashCache, parallel, batch, parallelMap, parallelWithRetry, sequence, withTimeout, RateLimiter, chunk, processInChunks, diffTemplates, getDiffSummary, requiresReplacement, categorizeChanges, getDeploymentStrategy, getDiffStats, cloud_config_schema_default, validateAgainstSchema, defaultLocalConfig, getLocalConfig, isLocalDevelopment, getLocalEndpoint, getLocalCredentials, getLocalEnvVars, MockCloudFormation, MockS3, MockDynamoDB, createMockAWS, PreviewEnvironmentManager, previewManager, generatePreviewWorkflow, generateCleanupWorkflow, generateCostReportWorkflow, PreviewNotificationService, previewNotifications, formatTable, formatTree, formatProgressBar, formatBytes, formatDuration, formatList, formatKeyValue, ProgressBar, MultiStepProgress, Spinner, TaskList, suggestCommand, formatSuggestion, isLikelyTypo, getContextualHelp, searchCommands, autocomplete, suggestFlags, formatFlagSuggestions, getCommandUsage, validateCommand, REPL, REPLContext, REPLCommandBuilder, CommandHistory, formatHistory, formatHistoryStats, MultiRegionManager, multiRegionManager, CrossRegionReferenceManager, GlobalResourceManager, RegionPairManager, StackDependencyManager, crossRegionReferenceManager, globalResourceManager, regionPairManager, stackDependencyManager, AWS_REGIONS, getRegion, getAllRegions, getRegionsByLocation, getRegionsByCompliance, getRegionsByPricingTier, isValidRegion, getClosestRegion, suggestRegions, suggestRegionPairs, formatRegion, formatRegionList, getRegionStats, MultiAccountManager, OrganizationManager, multiAccountManager, organizationManager, RECOMMENDED_ACCOUNT_STRUCTURES, RECOMMENDED_SCPS, COMMON_CROSS_ACCOUNT_ROLES, getRecommendedStructure, generateCrossAccountRoleCF, validateAccountStructure, formatAccountStructure, generateDeploymentWorkflow, generateMultiEnvWorkflow, generatePRPreviewWorkflow, generateScheduledWorkflow, generateMatrixWorkflow, generateDeploymentPipeline, generateMultiEnvPipeline, generatePreviewPipeline, generateScheduledPipeline, generateManualPipeline, generateDeploymentConfig, generateMultiEnvConfig, generateScheduledConfig, generateApprovalConfig, generateParallelConfig, BackupManager, backupManager, DisasterRecoveryManager, drManager, AWSConfigManager, awsConfigManager, CloudTrailManager, cloudTrailManager, GuardDutyManager, guardDutyManager, SecurityHubManager, securityHubManager, BlueGreenManager, blueGreenManager, CanaryManager, canaryManager, ABTestManager, abTestManager, ProgressiveDeploymentManager, progressiveDeploymentManager, XRayManager, xrayManager, MetricsManager, metricsManager, LogsManager, logsManager, SyntheticsManager, syntheticsManager, MigrationManager, migrationManager, ReplicaManager, replicaManager, PerformanceManager, performanceManager, DatabaseUserManager, databaseUserManager, SecretsRotationManager, secretsRotationManager, SecretsManager, secretsManager, CertificateManager, certificateManager, SecurityScanningManager, securityScanningManager, ImageScanningManager, imageScanningManager, BuildOptimizationManager, buildOptimizationManager, ContainerRegistryManager, containerRegistryManager, ServiceMeshManager, serviceMeshManager, LambdaLayersManager, lambdaLayersManager, LambdaVersionsManager, lambdaVersionsManager, LambdaConcurrencyManager, lambdaConcurrencyManager, LambdaDestinationsManager, lambdaDestinationsManager, LambdaVPCManager, lambdaVPCManager, LambdaDLQManager, lambdaDLQManager, Route53RoutingManager, route53RoutingManager, DNSSECManager, dnssecManager, Route53ResolverManager, route53ResolverManager, BounceComplaintHandler, bounceComplaintHandler, EmailAnalyticsManager, emailAnalyticsManager, SenderReputationManager, senderReputationManager, EmailTemplateManager, emailTemplateManager, exports_advanced, exports_inbound, exports_advanced2, exports_advanced3, FIFOQueueManager, fifoQueueManager, DLQMonitoringManager, dlqMonitoringManager, BatchProcessingManager, batchProcessingManager, QueueManagementManager, queueManagementManager, createZip, generateBootstrap, runBuildHooks, sha256, artifactKey, packageServerlessApp, MANAGED_NODE_VERSIONS, resolveServerlessRuntime, resolveQueues, resolveQueueNames, composeServerlessAppTemplate, PHP_LAYER_EXTENSIONS, phpLayerPackages, phpLayerBuildStage, generatePhpLayerDockerfile, generateAppImageDockerfile, sharedRuntimeLoop, buildNodeRuntimeLayerZip, buildBunRuntimeLayerZip, resolveApp, eventToRequest, responseToResult, createHttpHandler, createQueueHandler, createCliHandler, createHandlers, generatePhpFpmConfig, phpRuntimeLayerAssets, laravelServerlessEnvDefaults, LARAVEL_SERVERLESS_BUILD_STEPS, buildPhpRuntimeLayerZip, PHP_DEFAULT_EXCLUDES, runPhpBuildHooks, collectPhpAppEntries, packagePhpApp, StaticSiteManager, staticSiteManager, StorageAdvancedManager, storageAdvancedManager, HealthCheckManager, healthCheckManager, NetworkSecurityManager, networkSecurityManager, ResourceManagementManager, resourceManagementManager };
@@ -8,7 +8,7 @@ import {
8
8
  phpRuntimeLayerAssets,
9
9
  runPhpBuildHooks,
10
10
  sha256
11
- } from "./chunk-49nmy775.js";
11
+ } from "./chunk-0tfwdnmf.js";
12
12
 
13
13
  // src/deploy/serverless-image.ts
14
14
  import { execFileSync } from "node:child_process";
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  buildAndPushServerlessImage
3
- } from "./chunk-qgnyzfmx.js";
3
+ } from "./chunk-5mejsd43.js";
4
4
  import"./chunk-hsk6fe6x.js";
5
- import"./chunk-49nmy775.js";
5
+ import"./chunk-0tfwdnmf.js";
6
6
  import"./chunk-arsh1g5h.js";
7
7
  import"./chunk-v0bahtg2.js";
8
8
  export {
@@ -6,7 +6,7 @@ import {
6
6
  step,
7
7
  success,
8
8
  warn
9
- } from "./chunk-qgnyzfmx.js";
9
+ } from "./chunk-5mejsd43.js";
10
10
  import {
11
11
  CloudWatchLogsClient,
12
12
  CostExplorerClient,
@@ -28,7 +28,7 @@ import {
28
28
  resolveSiteKind,
29
29
  resolveUiSource,
30
30
  siteInstallBase
31
- } from "./chunk-xgnxz5dz.js";
31
+ } from "./chunk-kcxxwg3j.js";
32
32
  import {
33
33
  artifactKey,
34
34
  composeServerlessAppTemplate,
@@ -42,7 +42,7 @@ import {
42
42
  resolveServerlessArtifactBucketName,
43
43
  resolveServerlessAssetBucketName,
44
44
  resolveServerlessRuntime
45
- } from "./chunk-49nmy775.js";
45
+ } from "./chunk-0tfwdnmf.js";
46
46
  import {
47
47
  deployStaticSiteWithExternalDnsFull
48
48
  } from "./chunk-d7p84vz5.js";
@@ -347,7 +347,7 @@ async function deployServerlessApp(config, environment, opts = {}) {
347
347
  }
348
348
  if (imageMode) {
349
349
  step("Building + pushing container image");
350
- const { buildAndPushServerlessImage } = await import("./chunk-296n8hvt.js");
350
+ const { buildAndPushServerlessImage } = await import("./chunk-c3yy2xyq.js");
351
351
  const built = await buildAndPushServerlessImage({
352
352
  app,
353
353
  projectRoot,
@@ -11834,6 +11834,135 @@ function scopeCloudConfig(config6, user) {
11834
11834
  };
11835
11835
  }
11836
11836
 
11837
+ // src/deploy/dashboard-site-settings.ts
11838
+ var MEMBER_EDITABLE_SITE_FIELDS = new Set([
11839
+ "name",
11840
+ "ssl",
11841
+ "env",
11842
+ "redirects",
11843
+ "aliases",
11844
+ "domain",
11845
+ "path"
11846
+ ]);
11847
+ var ADMIN_ONLY_SITE_FIELDS = {
11848
+ build: "build runs as a shell command on the server",
11849
+ start: "start runs as a shell command on the server",
11850
+ root: "root is a filesystem path on the shared server",
11851
+ port: "ports are allocated by the server owner",
11852
+ type: "the deploy type is set by the server owner",
11853
+ php: "the PHP runtime is set by the server owner"
11854
+ };
11855
+ function checkMemberSiteFields(body) {
11856
+ const touched = Object.keys(body).filter((key) => body[key] !== undefined);
11857
+ const forbidden = touched.filter((key) => (key in ADMIN_ONLY_SITE_FIELDS));
11858
+ if (forbidden.length) {
11859
+ const reasons = forbidden.map((key) => `${key} (${ADMIN_ONLY_SITE_FIELDS[key]})`).join(", ");
11860
+ return { ok: false, error: `Only the server owner can change ${reasons}.` };
11861
+ }
11862
+ const unknown = touched.filter((key) => !MEMBER_EDITABLE_SITE_FIELDS.has(key));
11863
+ if (unknown.length)
11864
+ return { ok: false, error: `Only the server owner can change ${unknown.join(", ")}.` };
11865
+ return { ok: true };
11866
+ }
11867
+ function normalizeHost(value) {
11868
+ return String(value ?? "").trim().toLowerCase().replace(/\.$/, "");
11869
+ }
11870
+ function normalizePath(value) {
11871
+ const path = String(value ?? "/").trim();
11872
+ if (!path || path === "/")
11873
+ return "/";
11874
+ return (path.startsWith("/") ? path : `/${path}`).replace(/\/+$/, "") || "/";
11875
+ }
11876
+ function siteHosts(site) {
11877
+ const hosts = [normalizeHost(site?.domain)];
11878
+ if (Array.isArray(site?.aliases))
11879
+ hosts.push(...site.aliases.map(normalizeHost));
11880
+ return hosts.filter(Boolean);
11881
+ }
11882
+ function checkRouteConflict({ siteName, body, sites, ownSites }) {
11883
+ const current = sites[siteName] ?? {};
11884
+ const owned = new Set(ownSites);
11885
+ const wantedHosts = new Set;
11886
+ if (body.domain !== undefined)
11887
+ wantedHosts.add(normalizeHost(body.domain));
11888
+ if (Array.isArray(body.aliases))
11889
+ for (const alias of body.aliases)
11890
+ wantedHosts.add(normalizeHost(alias));
11891
+ wantedHosts.delete("");
11892
+ if (wantedHosts.size === 0)
11893
+ return { ok: true };
11894
+ const wantedPath = normalizePath(body.path !== undefined ? body.path : current.path);
11895
+ for (const [otherName, other] of Object.entries(sites)) {
11896
+ if (otherName === siteName || owned.has(otherName))
11897
+ continue;
11898
+ const otherPath = normalizePath(other?.path);
11899
+ for (const host of siteHosts(other)) {
11900
+ if (!wantedHosts.has(host))
11901
+ continue;
11902
+ if (otherPath === wantedPath) {
11903
+ return {
11904
+ ok: false,
11905
+ error: `${host}${wantedPath === "/" ? "" : wantedPath} is already served by another site on this server.`
11906
+ };
11907
+ }
11908
+ }
11909
+ }
11910
+ return { ok: true };
11911
+ }
11912
+
11913
+ // src/deploy/dashboard-throttle.ts
11914
+ var MAX_ATTEMPTS = 8;
11915
+ var LOCKOUT_MS = 15 * 60 * 1000;
11916
+ var WINDOW_MS = 15 * 60 * 1000;
11917
+
11918
+ class LoginThrottle {
11919
+ maxAttempts;
11920
+ lockoutMs;
11921
+ windowMs;
11922
+ attempts = new Map;
11923
+ constructor(maxAttempts = MAX_ATTEMPTS, lockoutMs = LOCKOUT_MS, windowMs = WINDOW_MS) {
11924
+ this.maxAttempts = maxAttempts;
11925
+ this.lockoutMs = lockoutMs;
11926
+ this.windowMs = windowMs;
11927
+ }
11928
+ key(username, address) {
11929
+ return `${username.trim().toLowerCase()}\x00${address}`;
11930
+ }
11931
+ check(username, address, now = Date.now()) {
11932
+ const entry = this.attempts.get(this.key(username, address));
11933
+ if (!entry?.until)
11934
+ return { allowed: true };
11935
+ if (now >= entry.until) {
11936
+ this.attempts.delete(this.key(username, address));
11937
+ return { allowed: true };
11938
+ }
11939
+ return { allowed: false, retryAfterSeconds: Math.ceil((entry.until - now) / 1000) };
11940
+ }
11941
+ recordFailure(username, address, now = Date.now()) {
11942
+ const key = this.key(username, address);
11943
+ const entry = this.attempts.get(key);
11944
+ const stale = !entry || now - entry.last > this.windowMs;
11945
+ const count = stale ? 1 : entry.count + 1;
11946
+ const next = { count, last: now };
11947
+ if (count >= this.maxAttempts)
11948
+ next.until = now + this.lockoutMs;
11949
+ this.attempts.set(key, next);
11950
+ }
11951
+ recordSuccess(username, address) {
11952
+ this.attempts.delete(this.key(username, address));
11953
+ }
11954
+ prune(now = Date.now()) {
11955
+ for (const [key, entry] of this.attempts) {
11956
+ const expired = entry.until ? now >= entry.until : now - entry.last > this.windowMs;
11957
+ if (expired)
11958
+ this.attempts.delete(key);
11959
+ }
11960
+ }
11961
+ get size() {
11962
+ return this.attempts.size;
11963
+ }
11964
+ }
11965
+
11837
11966
  // src/deploy/firewall-config-editor.ts
11838
11967
  var ALWAYS_OPEN = new Set([22, 80, 443]);
11839
11968
  function isValidPort(port) {
@@ -12988,6 +13117,9 @@ async function startLocalDashboardServer(options = {}) {
12988
13117
  const authEnabled = !authDisabled;
12989
13118
  const secret = resolveSessionSecret(cwd);
12990
13119
  const guard = createDashboardGuard({ cwd, enabled: authEnabled, secret });
13120
+ const throttle = new LoginThrottle;
13121
+ const throttleSweep = setInterval(() => throttle.prune(), 5 * 60 * 1000);
13122
+ throttleSweep.unref?.();
12991
13123
  if (authEnabled) {
12992
13124
  const bootstrap = ensureAdminUser(cwd, process.env.TS_CLOUD_UI_USERNAME?.trim() || "admin");
12993
13125
  if (bootstrap.generated) {
@@ -13040,11 +13172,19 @@ async function startLocalDashboardServer(options = {}) {
13040
13172
  const body = await readJsonBody(req);
13041
13173
  const username = String(body.username ?? "").trim();
13042
13174
  const password = String(body.password ?? "");
13175
+ const address = server2.requestIP(req)?.address ?? "unknown";
13176
+ const gate = throttle.check(username, address);
13177
+ if (!gate.allowed) {
13178
+ return json({ ok: false, error: `Too many failed attempts. Try again in ${Math.ceil((gate.retryAfterSeconds ?? 0) / 60)} minute(s).` }, 429, { "retry-after": String(gate.retryAfterSeconds ?? 60) });
13179
+ }
13043
13180
  const user2 = findUser(loadUsers(cwd), username);
13044
13181
  const hash = user2?.passwordHash || DUMMY_HASH;
13045
13182
  const ok = verifyPassword(password, hash) && !!user2;
13046
- if (!ok)
13183
+ if (!ok) {
13184
+ throttle.recordFailure(username, address);
13047
13185
  return json({ ok: false, error: "Incorrect username or password." }, 401);
13186
+ }
13187
+ throttle.recordSuccess(username, address);
13048
13188
  const token = createSessionToken(user2.username, secret);
13049
13189
  return json({ ok: true, user: describeUser(user2) }, 200, {
13050
13190
  "set-cookie": serializeSessionCookie(token, { secure: cookieSecure })
@@ -13248,6 +13388,19 @@ async function startLocalDashboardServer(options = {}) {
13248
13388
  const existing = config6.sites?.[name];
13249
13389
  if (!name || !existing)
13250
13390
  return json({ ok: false, error: `Site '${name}' was not found.` }, 404);
13391
+ if (user.role === "member") {
13392
+ const fields = checkMemberSiteFields(body);
13393
+ if (!fields.ok)
13394
+ return json({ ok: false, error: fields.error }, 403);
13395
+ const conflict = checkRouteConflict({
13396
+ siteName: name,
13397
+ body,
13398
+ sites: config6.sites ?? {},
13399
+ ownSites: Object.keys(user.sites)
13400
+ });
13401
+ if (!conflict.ok)
13402
+ return json({ ok: false, error: conflict.error }, 409);
13403
+ }
13251
13404
  if (body.port !== undefined && body.port !== null && body.port !== "" && (!Number.isInteger(Number(body.port)) || Number(body.port) < 1 || Number(body.port) > 65535))
13252
13405
  return json({ ok: false, error: "Port must be a number between 1 and 65535." }, 422);
13253
13406
  let text2 = await readFile(configPath, "utf8");
@@ -5,7 +5,7 @@ import {
5
5
  resolveCloudProvider,
6
6
  resolveManagementDashboardSites,
7
7
  resolveProjectStackName
8
- } from "./chunk-49nmy775.js";
8
+ } from "./chunk-0tfwdnmf.js";
9
9
  import {
10
10
  EC2Client,
11
11
  SSMClient
@@ -3579,7 +3579,9 @@ function releasePaths(base, releaseId) {
3579
3579
  }
3580
3580
  function isFileSharedPath(p) {
3581
3581
  const name = p.split("/").pop() || p;
3582
- return name.startsWith(".") || /\.[a-z0-9]+$/i.test(name);
3582
+ if (name === ".env" || name.startsWith(".env."))
3583
+ return true;
3584
+ return /\.[a-z0-9]+$/i.test(name);
3583
3585
  }
3584
3586
  function buildEnsureReleaseLayout(paths, sharedPaths = DEFAULT_SHARED_PATHS) {
3585
3587
  const lines = [
@@ -3706,7 +3708,7 @@ function buildSiteDeployScript(options) {
3706
3708
  const paths = releasePaths(base, releaseId);
3707
3709
  const unitBase = `${slug}-${siteName}`;
3708
3710
  const serviceName = `${unitBase}.service`;
3709
- const sharedPaths = [".env"];
3711
+ const sharedPaths = [...new Set([".env", ...options.sharedPaths ?? []])];
3710
3712
  const envFile = formatEnvFile(envEntries);
3711
3713
  const preStart = preStartCommands.length > 0 ? [`cd ${paths.release}`, ...preStartCommands] : [];
3712
3714
  const stageRelease = [
@@ -3816,14 +3818,14 @@ function buildLocalArtifactFetch(localPath, siteName) {
3816
3818
  ];
3817
3819
  }
3818
3820
  // src/drivers/shared/compute-deploy.ts
3819
- import { copyFileSync } from "node:fs";
3821
+ import { copyFileSync as copyFileSync2 } from "node:fs";
3820
3822
  import { tmpdir as tmpdir2 } from "node:os";
3821
3823
  import { join as join5 } from "node:path";
3822
3824
 
3823
3825
  // src/deploy/management-dashboard.ts
3824
3826
  import { execSync as execSync2 } from "node:child_process";
3825
3827
  import { randomBytes } from "node:crypto";
3826
- import { chmodSync, existsSync as existsSync2, mkdirSync, readFileSync as readFileSync3, writeFileSync } from "node:fs";
3828
+ import { chmodSync, copyFileSync, existsSync as existsSync2, mkdirSync, readFileSync as readFileSync3, writeFileSync } from "node:fs";
3827
3829
  import { tmpdir } from "node:os";
3828
3830
  import { dirname, isAbsolute, join as join4 } from "node:path";
3829
3831
  import { fileURLToPath } from "node:url";
@@ -3862,6 +3864,57 @@ var noopLogger = { info: () => {}, warn: () => {} };
3862
3864
  function truthy(v) {
3863
3865
  return v != null && v !== "" && v !== "0" && v.toLowerCase() !== "false";
3864
3866
  }
3867
+ var LIVE_STAGE_DIR = join4(".ts-cloud", "dashboard-release");
3868
+ var CONFIG_CANDIDATES = [
3869
+ ["config", "cloud.ts"],
3870
+ ["config", "cloud.js"],
3871
+ ["cloud.config.ts"],
3872
+ ["cloud.config.js"]
3873
+ ];
3874
+ function resolveDashboardVersion() {
3875
+ const explicit = process.env.TS_CLOUD_UI_VERSION?.trim();
3876
+ if (explicit)
3877
+ return explicit;
3878
+ const here = dirname(fileURLToPath(import.meta.url));
3879
+ for (const candidate of [join4(here, "..", "..", "package.json"), join4(here, "..", "package.json")]) {
3880
+ try {
3881
+ const pkg = JSON.parse(readFileSync3(candidate, "utf8"));
3882
+ if (pkg.name === "@stacksjs/ts-cloud" && pkg.version)
3883
+ return `^${pkg.version}`;
3884
+ } catch {}
3885
+ }
3886
+ return "latest";
3887
+ }
3888
+ function stageLiveDashboardRoot(cwd, logger) {
3889
+ const source = CONFIG_CANDIDATES.map((parts) => join4(cwd, ...parts)).find((file) => existsSync2(file));
3890
+ if (!source) {
3891
+ logger.warn("Management dashboard: no cloud config found to ship — skipping the live dashboard.");
3892
+ return null;
3893
+ }
3894
+ const stage = join4(cwd, LIVE_STAGE_DIR);
3895
+ try {
3896
+ mkdirSync(stage, { recursive: true });
3897
+ const isStacksLayout = source.endsWith(join4("config", "cloud.ts")) || source.endsWith(join4("config", "cloud.js"));
3898
+ if (isStacksLayout) {
3899
+ mkdirSync(join4(stage, "config"), { recursive: true });
3900
+ copyFileSync(source, join4(stage, "config", source.endsWith(".ts") ? "cloud.ts" : "cloud.js"));
3901
+ } else {
3902
+ copyFileSync(source, join4(stage, source.endsWith(".ts") ? "cloud.config.ts" : "cloud.config.js"));
3903
+ }
3904
+ const pkg = {
3905
+ name: "ts-cloud-dashboard",
3906
+ private: true,
3907
+ type: "module",
3908
+ dependencies: { "@stacksjs/ts-cloud": resolveDashboardVersion() }
3909
+ };
3910
+ writeFileSync(join4(stage, "package.json"), `${JSON.stringify(pkg, null, 2)}
3911
+ `);
3912
+ return LIVE_STAGE_DIR;
3913
+ } catch (error) {
3914
+ logger.warn(`Management dashboard: could not stage the live release (${error?.message ?? error}) — skipping.`);
3915
+ return null;
3916
+ }
3917
+ }
3865
3918
  function resolveUiSource(cwd) {
3866
3919
  if (existsSync2(join4(cwd, "packages", "ui", "pages")) || existsSync2(join4(cwd, "packages", "ui", "package.json"))) {
3867
3920
  return { uiRoot: "packages/ui/dist", build: "cd packages/ui && bun install && bun run build" };
@@ -3888,6 +3941,32 @@ function ensureManagementDashboard(config, options = {}) {
3888
3941
  }
3889
3942
  if (hasManagementDashboardSite(config))
3890
3943
  return config;
3944
+ const environment = config.environments && Object.keys(config.environments)[0];
3945
+ const domain = process.env.TS_CLOUD_UI_DOMAIN?.trim() || undefined;
3946
+ const port = Number(process.env.TS_CLOUD_UI_PORT) || undefined;
3947
+ const sites = { ...config.sites ?? {} };
3948
+ if (!truthy(process.env.TS_CLOUD_UI_STATIC)) {
3949
+ const uiRoot = stageLiveDashboardRoot(cwd, logger);
3950
+ if (!uiRoot)
3951
+ return config;
3952
+ const resolved2 = resolveManagementDashboardSites(config, environment ?? "production", {
3953
+ uiRoot,
3954
+ build: false,
3955
+ domain,
3956
+ port,
3957
+ live: true
3958
+ });
3959
+ if (resolved2.length === 0) {
3960
+ logger.info("Management dashboard: no domain resolved (set TS_CLOUD_UI_DOMAIN or configure a site domain) — skipping.");
3961
+ return config;
3962
+ }
3963
+ for (const { name, site } of resolved2) {
3964
+ sites[name] = site;
3965
+ logger.info(`Management dashboard → https://${site.domain} (sign in with your ts-cloud account; the first deploy prints an admin password once)`);
3966
+ }
3967
+ config.sites = sites;
3968
+ return config;
3969
+ }
3891
3970
  const ui = resolveUiSource(cwd);
3892
3971
  if (!ui) {
3893
3972
  logger.info("Management dashboard: UI not found (no local ui/ or packaged dist/ui) — skipping auto-deploy.");
@@ -3895,17 +3974,14 @@ function ensureManagementDashboard(config, options = {}) {
3895
3974
  }
3896
3975
  const username = process.env.TS_CLOUD_UI_USERNAME?.trim() || "admin";
3897
3976
  const auth = resolveDashboardAuth(cwd, username, logger);
3898
- const environment = config.environments && Object.keys(config.environments)[0];
3899
- const live = truthy(process.env.TS_CLOUD_UI_LIVE);
3900
- const port = Number(process.env.TS_CLOUD_UI_PORT) || undefined;
3901
3977
  const resolved = resolveManagementDashboardSites(config, environment ?? "production", {
3902
3978
  uiRoot: ui.uiRoot,
3903
3979
  build: ui.build,
3904
- domain: process.env.TS_CLOUD_UI_DOMAIN?.trim() || undefined,
3980
+ domain,
3905
3981
  username,
3906
3982
  password: auth.password,
3907
3983
  realm: process.env.TS_CLOUD_UI_REALM?.trim() || undefined,
3908
- live,
3984
+ live: false,
3909
3985
  port
3910
3986
  });
3911
3987
  if (resolved.length === 0) {
@@ -3913,7 +3989,7 @@ function ensureManagementDashboard(config, options = {}) {
3913
3989
  return config;
3914
3990
  }
3915
3991
  const authNote = auth.source === "public" ? "NO AUTH — TS_CLOUD_UI_PUBLIC is set (dashboard is publicly reachable)" : auth.source === "env" ? "htpasswd-protected (TS_CLOUD_UI_PASSWORD)" : `htpasswd-protected (auto-generated — see ${DASHBOARD_CREDENTIALS_FILE})`;
3916
- const sites = { ...config.sites ?? {} };
3992
+ logger.warn("Management dashboard: static mode — one shared password, and no per-site collaborators. Unset TS_CLOUD_UI_STATIC for the live dashboard.");
3917
3993
  for (const { name, site } of resolved) {
3918
3994
  sites[name] = site;
3919
3995
  logger.info(`Management dashboard → https://${site.domain} (${authNote})`);
@@ -4566,6 +4642,7 @@ async function deploySiteRelease(driver, options, logger = noopLogger2) {
4566
4642
  envEntries: site.env || {},
4567
4643
  port: site.port,
4568
4644
  preStartCommands: site.preStart,
4645
+ sharedPaths: site.sharedPaths,
4569
4646
  zeroDowntime: site.zeroDowntime !== false,
4570
4647
  healthCheckPath: site.healthCheck?.path
4571
4648
  });
@@ -4652,7 +4729,7 @@ async function deployAllComputeSites(options) {
4652
4729
  }
4653
4730
  try {
4654
4731
  const copy = join5(tmpdir2(), `${slug}-${name}-${sha}.tar.gz`);
4655
- copyFileSync(dashboardTarball, copy);
4732
+ copyFileSync2(dashboardTarball, copy);
4656
4733
  dashboardTarballBySite.set(name, copy);
4657
4734
  } catch {
4658
4735
  dashboardTarballBySite.set(name, dashboardTarball);
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Which site settings a member may change, and route-conflict checks.
3
+ *
4
+ * `site:settings` lets a site owner edit their own site — but a site's config
5
+ * is not all equally harmless on a box shared with other tenants:
6
+ *
7
+ * - `build` and `start` are **shell commands run on the box at deploy time**,
8
+ * as root. A member who could set them would own the whole server, and every
9
+ * other tenant's site with it.
10
+ * - `root` is a filesystem path. Pointed at another site's directory (or `/`),
11
+ * it serves someone else's files to the internet.
12
+ * - `port`, `type` and `php` pick runtime and bind ports, which are the box's
13
+ * to allocate, not one tenant's.
14
+ *
15
+ * Those stay with the box owner. What is left is genuinely the tenant's own:
16
+ * TLS, their app's env, their redirects, and their routing — and routing is
17
+ * still checked against other tenants, since claiming `example.com` when
18
+ * someone else already serves it is hijacking their traffic.
19
+ *
20
+ * An allowlist, not a blocklist: a site field added later is admin-only until
21
+ * someone decides it is safe to hand over.
22
+ */
23
+ /** Fields a member holding `site:settings` on the site may change. */
24
+ export declare const MEMBER_EDITABLE_SITE_FIELDS: ReadonlySet<string>;
25
+ /**
26
+ * Fields only the box owner may change, with why — used to explain the refusal
27
+ * rather than a bare 403.
28
+ */
29
+ export declare const ADMIN_ONLY_SITE_FIELDS: Readonly<Record<string, string>>;
30
+ export interface FieldCheck {
31
+ ok: boolean;
32
+ error?: string;
33
+ }
34
+ /**
35
+ * Whether a member may apply `body` to a site. Refuses the whole request if it
36
+ * touches any admin-only field, rather than silently applying the safe subset —
37
+ * a caller who asked to set `start` should be told it did not happen.
38
+ */
39
+ export declare function checkMemberSiteFields(body: Record<string, any>): FieldCheck;
40
+ export interface RouteConflictInput {
41
+ /** The site being edited. */
42
+ siteName: string;
43
+ /** The requested changes (domain / path / aliases). */
44
+ body: Record<string, any>;
45
+ /** All sites on the box, by name. */
46
+ sites: Record<string, any>;
47
+ /** Site names the editor holds a grant on — conflicts with these are theirs. */
48
+ ownSites: string[];
49
+ }
50
+ /**
51
+ * Refuse a routing change that would claim a host another tenant already
52
+ * serves. Conflicts with the editor's own sites are allowed: moving a domain
53
+ * between two sites you control is your business.
54
+ */
55
+ export declare function checkRouteConflict({ siteName, body, sites, ownSites }: RouteConflictInput): FieldCheck;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Login throttling.
3
+ *
4
+ * The dashboard's login is internet-facing, and it is the door to a box that
5
+ * hosts other people's sites. scrypt makes each guess cost ~50ms, which slows
6
+ * an attacker but does not stop one — and while generated passwords are strong,
7
+ * an operator can set their own via `TS_CLOUD_UI_PASSWORD`.
8
+ *
9
+ * Failures are counted per key (username + client address) and lock that key
10
+ * out for a window once they pass a threshold. In-memory on purpose: the
11
+ * dashboard is a single process per box, and a restart clearing the counters is
12
+ * an acceptable trade for having no store to keep.
13
+ *
14
+ * Keyed on username *and* address so one attacker cannot lock a legitimate
15
+ * operator out of their own box by failing logins against their username
16
+ * (a denial of service dressed up as a security control).
17
+ */
18
+ /** Failures before a key is locked out. */
19
+ export declare const MAX_ATTEMPTS: number;
20
+ /** How long a locked key stays locked. */
21
+ export declare const LOCKOUT_MS: number;
22
+ /** Failures older than this stop counting, so occasional typos never lock. */
23
+ export declare const WINDOW_MS: number;
24
+ export interface ThrottleDecision {
25
+ allowed: boolean;
26
+ /** Seconds until the caller may try again. Only set when blocked. */
27
+ retryAfterSeconds?: number;
28
+ }
29
+ export declare class LoginThrottle {
30
+ private readonly maxAttempts;
31
+ private readonly lockoutMs;
32
+ private readonly windowMs;
33
+ private attempts;
34
+ constructor(maxAttempts?: number, lockoutMs?: number, windowMs?: number);
35
+ private key;
36
+ /** Whether a login attempt for this key may proceed. */
37
+ check(username: string, address: string, now?: number): ThrottleDecision;
38
+ /** Record a failed attempt, locking the key once it passes the threshold. */
39
+ recordFailure(username: string, address: string, now?: number): void;
40
+ /** Clear the counter for a key after a successful login. */
41
+ recordSuccess(username: string, address: string): void;
42
+ /** Drop expired entries so the map cannot grow without bound. */
43
+ prune(now?: number): void;
44
+ /** Tracked keys. For tests and diagnostics. */
45
+ get size(): number;
46
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -12,10 +12,10 @@ import {
12
12
  sanitizeCloudConfig,
13
13
  setMaintenance,
14
14
  startLocalDashboardServer
15
- } from "../chunk-qdhn1s7j.js";
15
+ } from "../chunk-js3vrvvn.js";
16
16
  import {
17
17
  buildAndPushServerlessImage
18
- } from "../chunk-qgnyzfmx.js";
18
+ } from "../chunk-5mejsd43.js";
19
19
  import"../chunk-b82pbxyp.js";
20
20
  import"../chunk-hsk6fe6x.js";
21
21
  import {
@@ -30,8 +30,8 @@ import {
30
30
  resolveUiSource,
31
31
  siteInstallBase,
32
32
  validateDeploymentConfig
33
- } from "../chunk-xgnxz5dz.js";
34
- import"../chunk-49nmy775.js";
33
+ } from "../chunk-kcxxwg3j.js";
34
+ import"../chunk-0tfwdnmf.js";
35
35
  import"../chunk-93hjhs78.js";
36
36
  import {
37
37
  deleteStaticSite,