@mdfriday/foundry 26.3.8 → 26.3.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -1970,6 +1970,126 @@ var init_license = __esm({
1970
1970
  }
1971
1971
  });
1972
1972
 
1973
+ // internal/domain/identity/value-object/sync-config.ts
1974
+ var SyncConfig;
1975
+ var init_sync_config = __esm({
1976
+ "internal/domain/identity/value-object/sync-config.ts"() {
1977
+ "use strict";
1978
+ SyncConfig = class _SyncConfig {
1979
+ dbEndpoint;
1980
+ dbName;
1981
+ email;
1982
+ dbPassword;
1983
+ userDir;
1984
+ constructor(data) {
1985
+ this.dbEndpoint = data.dbEndpoint;
1986
+ this.dbName = data.dbName;
1987
+ this.email = data.email;
1988
+ this.dbPassword = data.dbPassword;
1989
+ this.userDir = data.userDir;
1990
+ }
1991
+ // ============================================================================
1992
+ // Factory Methods
1993
+ // ============================================================================
1994
+ /**
1995
+ * 创建 SyncConfig 实例
1996
+ */
1997
+ static create(data) {
1998
+ if (!data.dbEndpoint || !data.dbName || !data.email || !data.dbPassword) {
1999
+ throw new Error("Missing required sync configuration fields");
2000
+ }
2001
+ return new _SyncConfig(data);
2002
+ }
2003
+ /**
2004
+ * 从 JSON 数据创建 SyncConfig
2005
+ */
2006
+ static fromJSON(data) {
2007
+ return _SyncConfig.create(data);
2008
+ }
2009
+ // ============================================================================
2010
+ // Getters
2011
+ // ============================================================================
2012
+ /**
2013
+ * 获取数据库端点 URL(包含数据库名)
2014
+ */
2015
+ getDbEndpoint() {
2016
+ return this.dbEndpoint;
2017
+ }
2018
+ /**
2019
+ * 获取数据库名称
2020
+ */
2021
+ getDbName() {
2022
+ return this.dbName;
2023
+ }
2024
+ /**
2025
+ * 获取用户邮箱
2026
+ */
2027
+ getEmail() {
2028
+ return this.email;
2029
+ }
2030
+ /**
2031
+ * 获取数据库密码
2032
+ */
2033
+ getDbPassword() {
2034
+ return this.dbPassword;
2035
+ }
2036
+ /**
2037
+ * 获取用户目录
2038
+ */
2039
+ getUserDir() {
2040
+ return this.userDir;
2041
+ }
2042
+ /**
2043
+ * 获取 CouchDB URI(不包含数据库名)
2044
+ * 例如:https://couch.example.com/dbname -> https://couch.example.com
2045
+ */
2046
+ getCouchDbUri() {
2047
+ return this.dbEndpoint.replace(`/${this.dbName}`, "");
2048
+ }
2049
+ // ============================================================================
2050
+ // Serialization
2051
+ // ============================================================================
2052
+ /**
2053
+ * 序列化为 JSON
2054
+ */
2055
+ toJSON() {
2056
+ return {
2057
+ dbEndpoint: this.dbEndpoint,
2058
+ dbName: this.dbName,
2059
+ email: this.email,
2060
+ dbPassword: this.dbPassword,
2061
+ userDir: this.userDir
2062
+ };
2063
+ }
2064
+ /**
2065
+ * 转换为 Obsidian LiveSync 插件格式
2066
+ * 用于直接配置 Obsidian 的 LiveSync 插件
2067
+ */
2068
+ toObsidianLiveSyncFormat() {
2069
+ return {
2070
+ couchDB_URI: this.getCouchDbUri(),
2071
+ couchDB_DBNAME: this.dbName,
2072
+ couchDB_USER: this.email,
2073
+ couchDB_PASSWORD: this.dbPassword,
2074
+ encrypt: true,
2075
+ syncOnStart: true,
2076
+ syncOnSave: true,
2077
+ liveSync: true
2078
+ };
2079
+ }
2080
+ // ============================================================================
2081
+ // Comparison
2082
+ // ============================================================================
2083
+ /**
2084
+ * 比较两个 SyncConfig 是否相同
2085
+ */
2086
+ equals(other) {
2087
+ return this.dbEndpoint === other.dbEndpoint && this.dbName === other.dbName && this.email === other.email;
2088
+ }
2089
+ };
2090
+ }
2091
+ });
2092
+
1973
2093
  // internal/domain/identity/value-object/device.ts
1974
2094
  var Device;
1975
2095
  var init_device = __esm({
@@ -2184,14 +2304,16 @@ var init_user = __esm({
2184
2304
  token;
2185
2305
  serverConfig;
2186
2306
  license;
2307
+ syncConfig;
2187
2308
  /**
2188
2309
  * 构造函数(私有,通过 Factory 创建)
2189
2310
  */
2190
- constructor(email, token, serverConfig, license = null) {
2311
+ constructor(email, token, serverConfig, license = null, syncConfig = null) {
2191
2312
  this.email = email;
2192
2313
  this.token = token;
2193
2314
  this.serverConfig = serverConfig;
2194
2315
  this.license = license;
2316
+ this.syncConfig = syncConfig;
2195
2317
  }
2196
2318
  // ============================================================================
2197
2319
  // Getters
@@ -2220,6 +2342,12 @@ var init_user = __esm({
2220
2342
  getLicense() {
2221
2343
  return this.license;
2222
2344
  }
2345
+ /**
2346
+ * 获取同步配置
2347
+ */
2348
+ getSyncConfig() {
2349
+ return this.syncConfig;
2350
+ }
2223
2351
  // ============================================================================
2224
2352
  // Business Logic
2225
2353
  // ============================================================================
@@ -2253,6 +2381,18 @@ var init_user = __esm({
2253
2381
  clearLicense() {
2254
2382
  this.license = null;
2255
2383
  }
2384
+ /**
2385
+ * 设置同步配置
2386
+ */
2387
+ setSyncConfig(syncConfig) {
2388
+ this.syncConfig = syncConfig;
2389
+ }
2390
+ /**
2391
+ * 清除同步配置
2392
+ */
2393
+ clearSyncConfig() {
2394
+ this.syncConfig = null;
2395
+ }
2256
2396
  /**
2257
2397
  * 检查是否已认证
2258
2398
  */
@@ -2318,7 +2458,8 @@ var init_user = __esm({
2318
2458
  email: this.email.toJSON(),
2319
2459
  token: this.token?.toJSON() || null,
2320
2460
  serverConfig: this.serverConfig.toJSON(),
2321
- license: this.license?.toJSON() || null
2461
+ license: this.license?.toJSON() || null,
2462
+ syncConfig: this.syncConfig?.toJSON() || null
2322
2463
  };
2323
2464
  }
2324
2465
  };
@@ -2335,6 +2476,7 @@ var init_user_factory = __esm({
2335
2476
  init_token();
2336
2477
  init_server_config();
2337
2478
  init_license();
2479
+ init_sync_config();
2338
2480
  init_device();
2339
2481
  init_log();
2340
2482
  log4 = getDomainLogger("user-factory", { component: "domain" });
@@ -2372,10 +2514,15 @@ var init_user_factory = __esm({
2372
2514
  if (userData.license) {
2373
2515
  user.setLicense(userData.license);
2374
2516
  }
2517
+ if (userData.syncConfig) {
2518
+ const syncConfig = SyncConfig.fromJSON(userData.syncConfig);
2519
+ user.setSyncConfig(syncConfig);
2520
+ }
2375
2521
  log4.debug("User loaded from storage", {
2376
2522
  email: email.getValue(),
2377
2523
  hasToken: !!userData.token,
2378
- hasLicense: !!userData.license
2524
+ hasLicense: !!userData.license,
2525
+ hasSyncConfig: !!userData.syncConfig
2379
2526
  });
2380
2527
  return user;
2381
2528
  } catch (error) {
@@ -2384,24 +2531,27 @@ var init_user_factory = __esm({
2384
2531
  }
2385
2532
  }
2386
2533
  /**
2387
- * 保存用户到存储(合并保存 token, license, server config)
2534
+ * 保存用户到存储(合并保存 token, license, server config, sync config
2388
2535
  */
2389
2536
  async save(user) {
2390
2537
  try {
2391
2538
  const token = user.getToken();
2392
2539
  const license = user.getLicense();
2540
+ const syncConfig = user.getSyncConfig();
2393
2541
  const serverConfig = user.getServerConfig();
2394
2542
  const email = user.getEmail().getValue();
2395
2543
  await this.storageProvider.saveUserData({
2396
2544
  token,
2397
2545
  license,
2546
+ syncConfig,
2398
2547
  serverConfig,
2399
2548
  email
2400
2549
  });
2401
2550
  log4.debug("User saved to storage", {
2402
2551
  email,
2403
2552
  hasToken: !!token,
2404
- hasLicense: !!license
2553
+ hasLicense: !!license,
2554
+ hasSyncConfig: !!syncConfig
2405
2555
  });
2406
2556
  } catch (error) {
2407
2557
  log4.error("Failed to save user to storage", error);
@@ -2665,10 +2815,27 @@ var init_user_factory = __esm({
2665
2815
  Date.now()
2666
2816
  );
2667
2817
  user.setLicense(license);
2818
+ if (data.sync && data.features.sync_enabled) {
2819
+ const syncConfig = SyncConfig.create({
2820
+ dbEndpoint: data.sync.db_endpoint,
2821
+ dbName: data.sync.db_name,
2822
+ email: data.sync.email,
2823
+ dbPassword: data.sync.db_password,
2824
+ userDir: data.user.user_dir
2825
+ });
2826
+ user.setSyncConfig(syncConfig);
2827
+ log4.info("Sync configuration saved", {
2828
+ dbName: syncConfig.getDbName(),
2829
+ email: syncConfig.getEmail(),
2830
+ userDir: syncConfig.getUserDir()
2831
+ });
2832
+ }
2668
2833
  await this.save(user);
2669
2834
  log4.info("License activated successfully", {
2670
2835
  plan: license.getPlan(),
2671
- expires: license.getFormattedExpiresAt()
2836
+ expires: license.getFormattedExpiresAt(),
2837
+ syncEnabled: data.features.sync_enabled,
2838
+ hasSyncConfig: user.getSyncConfig() !== null
2672
2839
  });
2673
2840
  return user;
2674
2841
  } catch (error) {
@@ -3113,6 +3280,7 @@ __export(identity_exports, {
3113
3280
  Email: () => Email,
3114
3281
  License: () => License,
3115
3282
  ServerConfig: () => ServerConfig,
3283
+ SyncConfig: () => SyncConfig,
3116
3284
  Token: () => Token,
3117
3285
  User: () => User,
3118
3286
  UserFactory: () => UserFactory
@@ -3124,6 +3292,7 @@ var init_identity = __esm({
3124
3292
  init_token();
3125
3293
  init_server_config();
3126
3294
  init_license();
3295
+ init_sync_config();
3127
3296
  init_device();
3128
3297
  init_user();
3129
3298
  init_user_factory();
@@ -9173,20 +9342,15 @@ var init_publish2 = __esm({
9173
9342
  }
9174
9343
  /**
9175
9344
  * Publish project to specified platform
9345
+ *
9346
+ * @param project - 项目实例
9347
+ * @param config - 已合并的发布配置(由调用方准备)
9348
+ * @param options - 发布选项
9349
+ * @param onProgress - 进度回调
9176
9350
  */
9177
- async publish(project, method, options, onProgress) {
9178
- log17.info(`Publishing project: ${project.getName()} to ${method}`);
9351
+ async publish(project, config, options, onProgress) {
9352
+ log17.info(`Publishing project: ${project.getName()} to ${config.type}`);
9179
9353
  try {
9180
- const workspace = await this.workspaceService.loadWorkspace();
9181
- const globalConfig = await workspace.getAllConfig();
9182
- const publishConfig = WorkspacePublishConfig.fromGlobalConfig(globalConfig);
9183
- let config;
9184
- if (options?.config) {
9185
- const mergedConfig = publishConfig.merge(options.config, method);
9186
- config = await this.getMethodConfig(mergedConfig, method);
9187
- } else {
9188
- config = await this.getMethodConfig(publishConfig, method);
9189
- }
9190
9354
  const publicDir = await project.getPublishDir();
9191
9355
  const publisher = this.publisherFactory.create(
9192
9356
  project.getId(),
@@ -9205,27 +9369,59 @@ var init_publish2 = __esm({
9205
9369
  const result = await publisher.publish(publicDir, publishOptions);
9206
9370
  return result;
9207
9371
  } catch (error) {
9208
- log17.error(`Publish failed: ${method}`, error);
9372
+ log17.error(`Publish failed: ${config.type}`, error);
9209
9373
  throw error;
9210
9374
  }
9211
9375
  }
9212
9376
  /**
9213
9377
  * Test connection
9378
+ *
9379
+ * @param project - 项目实例
9380
+ * @param config - 已合并的发布配置(由调用方准备)
9214
9381
  */
9215
- async testConnection(project, method) {
9216
- const workspace = await this.workspaceService.loadWorkspace();
9382
+ async testConnection(project, config) {
9383
+ log17.info(`Testing ${config.type} connection for project: ${project.getName()}`);
9384
+ try {
9385
+ const publisher = this.publisherFactory.create(
9386
+ project.getId(),
9387
+ project.getPath(),
9388
+ config
9389
+ );
9390
+ if (publisher.testConnection) {
9391
+ return await publisher.testConnection();
9392
+ }
9393
+ return { success: true };
9394
+ } catch (error) {
9395
+ log17.error(`Test connection failed: ${config.type}`, error);
9396
+ return {
9397
+ success: false,
9398
+ error: error.message
9399
+ };
9400
+ }
9401
+ }
9402
+ /**
9403
+ * 准备最终的发布配置
9404
+ *
9405
+ * 配置查找优先级(两级):
9406
+ * 1. 项目配置 (project.config.publish)
9407
+ * 2. 全局配置 (workspace global config)
9408
+ *
9409
+ * @param workspace - Workspace 实例
9410
+ * @param project - Project 实例
9411
+ * @param method - 发布方法
9412
+ * @returns 最终的发布配置
9413
+ */
9414
+ async prepareFinalConfig(workspace, project, method) {
9217
9415
  const globalConfig = await workspace.getAllConfig();
9218
- const publishConfig = WorkspacePublishConfig.fromGlobalConfig(globalConfig);
9219
- const config = await this.getMethodConfig(publishConfig, method);
9220
- const publisher = this.publisherFactory.create(
9221
- project.getId(),
9222
- project.getPath(),
9223
- config
9224
- );
9225
- if (publisher.testConnection) {
9226
- return await publisher.testConnection();
9416
+ let publishConfig = WorkspacePublishConfig.fromGlobalConfig(globalConfig);
9417
+ await project.loadConfig();
9418
+ const projectConfig = project.getConfig();
9419
+ if (projectConfig?.publish) {
9420
+ log17.debug("Merging project publish config");
9421
+ const projectPublishData = projectConfig.publish;
9422
+ publishConfig = publishConfig.merge(projectPublishData, method);
9227
9423
  }
9228
- return { success: true };
9424
+ return this.getMethodConfig(publishConfig, method, workspace);
9229
9425
  }
9230
9426
  // ============================================================================
9231
9427
  // Private Methods
@@ -9233,7 +9429,7 @@ var init_publish2 = __esm({
9233
9429
  /**
9234
9430
  * Get method-specific config from WorkspacePublishConfig
9235
9431
  */
9236
- async getMethodConfig(publishConfig, method) {
9432
+ async getMethodConfig(publishConfig, method, workspace) {
9237
9433
  switch (method) {
9238
9434
  case "ftp":
9239
9435
  const ftpConfig = publishConfig.getFTPConfig();
@@ -9267,15 +9463,14 @@ var init_publish2 = __esm({
9267
9463
  const licenseKey = mdConfig.licenseKey || "";
9268
9464
  if (!licenseKey) {
9269
9465
  throw new Error(
9270
- "\u274C MDFriday license key is required for publishing.\n\nDifferent licenses deploy to different servers.\nYou must explicitly configure or provide a license key:\n\n1\uFE0F\u20E3 Configure license globally (recommended):\n mdf config:set publish.mdfriday.licenseKey MDF-XXXX-XXXX-XXXX --global\n\n2\uFE0F\u20E3 Pass license via CLI:\n mdf publish mdfriday --license-key MDF-XXXX-XXXX-XXXX\n\n\u{1F4A1} Note: License login (mdf license:login) only gets TOKEN.\n Publishing requires explicitly setting a license key."
9466
+ "\u274C MDFriday license key is required for publishing.\n\nDifferent licenses deploy to different servers.\nYou must explicitly configure a license key:\n\nConfigure license globally (recommended):\n mdf config:set publish.mdfriday.licenseKey MDF-XXXX-XXXX-XXXX --global\n\nOr configure for project only:\n mdf config:set publish.mdfriday.licenseKey MDF-XXXX-XXXX-XXXX\n\n\u{1F4A1} Note: License login (mdf license:login) only gets TOKEN.\n Publishing requires explicitly setting a license key."
9271
9467
  );
9272
9468
  }
9273
- const workspace = await this.workspaceService.loadWorkspace();
9274
9469
  const { createIdentityService: createIdentityService2 } = await Promise.resolve().then(() => (init_container(), container_exports));
9275
9470
  let accessToken = null;
9276
9471
  let apiUrl = "https://app.mdfriday.com";
9277
9472
  try {
9278
- const identityService = await createIdentityService2();
9473
+ const identityService = await createIdentityService2(workspace.getInfo().path);
9279
9474
  await identityService.initialize();
9280
9475
  accessToken = identityService.getToken();
9281
9476
  const serverConfig = identityService.getServerConfig();
@@ -9288,33 +9483,30 @@ var init_publish2 = __esm({
9288
9483
  }
9289
9484
  if (!accessToken) {
9290
9485
  throw new Error(
9291
- "\u274C MDFriday access token not found.\n\nPlease login first to get an access token:\n\nOption 1 - Login with email/password:\n mdf auth:login --email <email> --password <password>\n\nOption 2 - Login with license key:\n mdf license:login --key MDF-XXXX-XXXX-XXXX"
9486
+ "\u274C Not authenticated.\n\nPlease login first:\n mdf auth:login\n\nOr use license login:\n mdf license:login --key YOUR-LICENSE-KEY"
9292
9487
  );
9293
9488
  }
9294
9489
  return {
9295
9490
  type: "mdfriday",
9296
- deploymentType: mdConfig.type || "share",
9297
- // Already merged with CLI override
9298
9491
  licenseKey,
9299
- // Required, from merged config
9300
- enabled: mdConfig.enabled,
9301
9492
  accessToken,
9302
- // From Identity Service
9303
- apiUrl
9304
- // From Identity Service (ServerConfig)
9493
+ apiUrl,
9494
+ deploymentType: mdConfig.type || "share",
9495
+ enabled: true
9305
9496
  };
9306
9497
  default:
9307
- throw new Error(`Unknown publish method: ${method}`);
9498
+ throw new Error(`Unsupported publish method: ${method}`);
9308
9499
  }
9309
9500
  }
9310
9501
  };
9311
9502
  }
9312
9503
  });
9313
9504
 
9314
- // internal/interfaces/cli/container.ts
9505
+ // internal/application/container.ts
9315
9506
  var container_exports = {};
9316
9507
  __export(container_exports, {
9317
9508
  createIdentityService: () => createIdentityService,
9509
+ createIdentityServiceForObsidian: () => createIdentityServiceForObsidian,
9318
9510
  createPublishAppService: () => createPublishAppService,
9319
9511
  createWorkspaceAppService: () => createWorkspaceAppService,
9320
9512
  createWorkspaceFactory: () => createWorkspaceFactory
@@ -9334,19 +9526,30 @@ function createWorkspaceAppService() {
9334
9526
  workspaceFactory
9335
9527
  });
9336
9528
  }
9337
- async function createIdentityService(projectPath) {
9529
+ async function createIdentityService(workspacePath) {
9338
9530
  const httpClient = new NodeHttpClient();
9339
9531
  const workspaceAppService = createWorkspaceAppService();
9340
- const workspace = await workspaceAppService.loadWorkspace(projectPath);
9532
+ const workspace = await workspaceAppService.loadWorkspace(workspacePath);
9341
9533
  const storageProvider = workspaceAppService.createIdentityStorage(workspace);
9342
9534
  const userFactory = new UserFactory({
9343
9535
  httpClient,
9344
9536
  storageProvider
9345
9537
  });
9346
- const identityAppService = new IdentityAppService({
9538
+ return new IdentityAppService({
9539
+ userFactory
9540
+ });
9541
+ }
9542
+ async function createIdentityServiceForObsidian(workspacePath, httpClient) {
9543
+ const workspaceAppService = createWorkspaceAppService();
9544
+ const workspace = await workspaceAppService.loadWorkspace(workspacePath);
9545
+ const storageProvider = workspaceAppService.createIdentityStorage(workspace);
9546
+ const userFactory = new UserFactory({
9547
+ httpClient,
9548
+ storageProvider
9549
+ });
9550
+ return new IdentityAppService({
9347
9551
  userFactory
9348
9552
  });
9349
- return identityAppService;
9350
9553
  }
9351
9554
  function createPublishAppService() {
9352
9555
  const { PublishAppService: PublishAppService2 } = (init_publish2(), __toCommonJS(publish_exports2));
@@ -9362,7 +9565,7 @@ function createPublishAppService() {
9362
9565
  );
9363
9566
  }
9364
9567
  var init_container = __esm({
9365
- "internal/interfaces/cli/container.ts"() {
9568
+ "internal/application/container.ts"() {
9366
9569
  "use strict";
9367
9570
  init_workspace2();
9368
9571
  init_workspace3();
@@ -9372,6 +9575,22 @@ var init_container = __esm({
9372
9575
  }
9373
9576
  });
9374
9577
 
9578
+ // internal/interfaces/cli/container.ts
9579
+ var container_exports2 = {};
9580
+ __export(container_exports2, {
9581
+ createIdentityService: () => createIdentityService,
9582
+ createIdentityServiceForObsidian: () => createIdentityServiceForObsidian,
9583
+ createPublishAppService: () => createPublishAppService,
9584
+ createWorkspaceAppService: () => createWorkspaceAppService,
9585
+ createWorkspaceFactory: () => createWorkspaceFactory
9586
+ });
9587
+ var init_container2 = __esm({
9588
+ "internal/interfaces/cli/container.ts"() {
9589
+ "use strict";
9590
+ init_container();
9591
+ }
9592
+ });
9593
+
9375
9594
  // internal/domain/config/type.ts
9376
9595
  var ConfigError, ErrConfigNotFound, ErrInvalidConfig, ErrWorkspaceNotFound, ErrConfigFileNotFound, ErrInvalidConfigFormat;
9377
9596
  var init_type4 = __esm({
@@ -50558,7 +50777,7 @@ __export(cli_exports, {
50558
50777
  module.exports = __toCommonJS(cli_exports);
50559
50778
 
50560
50779
  // internal/interfaces/cli/router.ts
50561
- init_container();
50780
+ init_container2();
50562
50781
 
50563
50782
  // internal/interfaces/cli/commands/workspace.ts
50564
50783
  init_log();
@@ -52870,17 +53089,7 @@ var ServeCommand = class {
52870
53089
  },
52871
53090
  onSuccess: async () => {
52872
53091
  if (options.publish && this.publishAppService) {
52873
- const publishOptions = {};
52874
- if (options.type)
52875
- publishOptions.type = options.type;
52876
- if (options.licenseKey)
52877
- publishOptions.licenseKey = options.licenseKey;
52878
- await this.autoPublish(
52879
- project,
52880
- options.publish,
52881
- options.publishDelay,
52882
- publishOptions
52883
- );
53092
+ await this.autoPublish(workspace, project, options.publish, options.publishDelay);
52884
53093
  }
52885
53094
  }
52886
53095
  };
@@ -52980,7 +53189,7 @@ Press Ctrl+C to stop`,
52980
53189
  /**
52981
53190
  * 自动发布(带防抖)
52982
53191
  */
52983
- async autoPublish(project, method, delayMs, options) {
53192
+ async autoPublish(workspace, project, method, delayMs) {
52984
53193
  if (this.publishTimer) {
52985
53194
  clearTimeout(this.publishTimer);
52986
53195
  }
@@ -52995,17 +53204,11 @@ Press Ctrl+C to stop`,
52995
53204
  clearOnComplete: true
52996
53205
  });
52997
53206
  const startTime = Date.now();
52998
- const publishOptions = { incremental: true };
52999
- if (options && (options.type || options.licenseKey)) {
53000
- publishOptions.config = {
53001
- type: options.type,
53002
- licenseKey: options.licenseKey
53003
- };
53004
- }
53207
+ const config = await this.publishAppService.prepareFinalConfig(workspace, project, method);
53005
53208
  const result = await this.publishAppService.publish(
53006
53209
  project,
53007
- method,
53008
- publishOptions,
53210
+ config,
53211
+ { incremental: true },
53009
53212
  (progress) => {
53010
53213
  progressBar.update(progress.percentage, progress.message);
53011
53214
  }
@@ -53186,7 +53389,7 @@ var SnapshotDeleteCommand = class {
53186
53389
  };
53187
53390
 
53188
53391
  // internal/interfaces/cli/commands/auth.ts
53189
- init_container();
53392
+ init_container2();
53190
53393
  init_log();
53191
53394
  var log90 = getDomainLogger("auth-command", { component: "cli" });
53192
53395
  async function getIdentityService() {
@@ -54335,18 +54538,22 @@ var PublishCommand = class {
54335
54538
  description = "Publish project to hosting platform";
54336
54539
  async execute(args, options) {
54337
54540
  try {
54338
- const method = args[0];
54541
+ const subcommand = args[0];
54542
+ if (subcommand === "test") {
54543
+ return await this.executeTest(args.slice(1), options);
54544
+ }
54545
+ const method = subcommand;
54339
54546
  if (!method || !["ftp", "netlify", "mdfriday"].includes(method)) {
54340
54547
  return {
54341
54548
  success: false,
54342
- message: "Please specify publish method: ftp, netlify, or mdfriday\n\nUsage:\n mdf publish ftp [options]\n mdf publish netlify [options]\n mdf publish mdfriday [options]\n\nOptions:\n --project <name> Project to publish\n --force Force full publish (ignore incremental)\n --verbose Show detailed progress\n\nFTP Options:\n --host <host> FTP server host\n --username <user> FTP username\n --password <pass> FTP password\n --remote-path <path> Remote directory path\n\nExamples:\n mdf publish ftp\n mdf publish ftp --project my-blog\n mdf publish ftp --host ftp.example.com --username admin"
54549
+ message: "Please specify publish method: ftp, netlify, or mdfriday\n\nUsage:\n mdf publish ftp [options]\n mdf publish netlify [options]\n mdf publish mdfriday [options]\n mdf publish test <method>\n\nOptions:\n --project <name> Project to publish\n --force Force full publish (ignore incremental)\n --verbose Show detailed progress\n\nConfiguration:\n All publish settings are configured via config command:\n mdf config:set publish.<method>.<key> <value> --global\n mdf config:set publish.<method>.<key> <value> (project-level)\n\nExamples:\n mdf publish ftp\n mdf publish ftp --project my-blog --force\n mdf publish test ftp"
54343
54550
  };
54344
54551
  }
54345
54552
  const { workspace, project } = await this.workspaceAppService.loadWorkspaceAndProject(
54346
54553
  options.project
54347
54554
  );
54348
54555
  log92.info(`Publishing project: ${project.getName()} to ${method}`);
54349
- const configOverride = this.parseConfigOverride(method, options);
54556
+ const config = await this.publishService.prepareFinalConfig(workspace, project, method);
54350
54557
  const progressBar = new ProgressBar({
54351
54558
  width: 30,
54352
54559
  showPercentage: true,
@@ -54354,17 +54561,10 @@ var PublishCommand = class {
54354
54561
  clearOnComplete: true
54355
54562
  });
54356
54563
  const startTime = Date.now();
54357
- const appPublishOptions = {};
54358
- if (options.force) {
54359
- appPublishOptions.force = true;
54360
- }
54361
- if (configOverride) {
54362
- appPublishOptions.config = configOverride;
54363
- }
54364
54564
  const result = await this.publishService.publish(
54365
54565
  project,
54366
- method,
54367
- appPublishOptions,
54566
+ config,
54567
+ options.force ? { force: true } : {},
54368
54568
  (progress) => {
54369
54569
  if (options.verbose) {
54370
54570
  console.log(
@@ -54424,40 +54624,48 @@ var PublishCommand = class {
54424
54624
  }
54425
54625
  }
54426
54626
  /**
54427
- * Parse config override from command line options
54627
+ * Execute test connection command
54428
54628
  */
54429
- parseConfigOverride(method, options) {
54430
- switch (method) {
54431
- case "ftp":
54432
- if (options.host || options.username) {
54433
- return {
54434
- host: options.host,
54435
- port: options.port || 21,
54436
- username: options.username,
54437
- password: options.password,
54438
- remotePath: options.remotePath || "/",
54439
- secure: options.secure !== false
54440
- };
54441
- }
54442
- break;
54443
- case "netlify":
54444
- if (options.siteId || options.token) {
54445
- return {
54446
- siteId: options.siteId,
54447
- accessToken: options.token
54448
- };
54449
- }
54450
- break;
54451
- case "mdfriday":
54452
- if (options.type || options.licenseKey) {
54453
- return {
54454
- type: options.type,
54455
- licenseKey: options.licenseKey
54456
- };
54457
- }
54458
- break;
54629
+ async executeTest(args, options) {
54630
+ try {
54631
+ const method = args[0];
54632
+ if (!method || !["ftp", "netlify", "mdfriday"].includes(method)) {
54633
+ return {
54634
+ success: false,
54635
+ message: "Please specify publish method to test: ftp, netlify, or mdfriday\n\nUsage:\n mdf publish test ftp\n mdf publish test netlify\n mdf publish test mdfriday\n\nOptions:\n --project <name> Project to test (optional)\n\nConfiguration:\n All publish settings are configured via config command:\n mdf config:set publish.<method>.<key> <value> --global\n mdf config:set publish.<method>.<key> <value> (project-level)\n\nExamples:\n mdf publish test ftp\n mdf publish test netlify"
54636
+ };
54637
+ }
54638
+ const { workspace, project } = await this.workspaceAppService.loadWorkspaceAndProject(
54639
+ options.project
54640
+ );
54641
+ log92.info(`Testing ${method} connection for project: ${project.getName()}`);
54642
+ const config = await this.publishService.prepareFinalConfig(workspace, project, method);
54643
+ console.log(`
54644
+ Testing ${method.toUpperCase()} connection...`);
54645
+ console.log("Configuration priority: Project config > Global config\n");
54646
+ const result = await this.publishService.testConnection(project, config);
54647
+ if (result.success) {
54648
+ return {
54649
+ success: true,
54650
+ message: `
54651
+ \u2713 ${method.toUpperCase()} connection test passed!`
54652
+ };
54653
+ } else {
54654
+ return {
54655
+ success: false,
54656
+ message: `
54657
+ \u2717 ${method.toUpperCase()} connection test failed: ${result.error}`,
54658
+ error: new Error(result.error)
54659
+ };
54660
+ }
54661
+ } catch (error) {
54662
+ log92.error("Test connection command failed", error);
54663
+ return {
54664
+ success: false,
54665
+ message: `Test connection failed: ${error.message}`,
54666
+ error
54667
+ };
54459
54668
  }
54460
- return void 0;
54461
54669
  }
54462
54670
  };
54463
54671
 
@@ -54480,7 +54688,7 @@ var CommandRegistry = class {
54480
54688
  this.register(new ProjectShowCommand(workspaceAppService));
54481
54689
  this.register(new ProjectDeleteCommand(workspaceAppService));
54482
54690
  this.register(new BuildCommand(workspaceAppService));
54483
- const { createPublishAppService: createPublishAppService2 } = (init_container(), __toCommonJS(container_exports));
54691
+ const { createPublishAppService: createPublishAppService2 } = (init_container2(), __toCommonJS(container_exports2));
54484
54692
  const publishAppService = createPublishAppService2();
54485
54693
  this.register(new PublishCommand(publishAppService, workspaceAppService));
54486
54694
  this.register(new ServeCommand(workspaceAppService, publishAppService));
@@ -54832,7 +55040,7 @@ For more information, visit: https://help.mdfriday.com
54832
55040
  * Show version
54833
55041
  */
54834
55042
  showVersion() {
54835
- const version = "26.3.8";
55043
+ const version = "26.3.10";
54836
55044
  return {
54837
55045
  success: true,
54838
55046
  message: `MDFriday CLI v${version}`