@shoper/cli 0.5.2-5 → 0.5.2-7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. package/build/cli/auth/cli_auth_errors_factory.js +1 -1
  2. package/build/cli/auth/cli_auth_initializer.js +6 -1
  3. package/build/cli/auth/service/cli_auth_service.js +11 -2
  4. package/build/cli/auth/tokens/cli_auth_tokens_errors_factory.js +1 -1
  5. package/build/cli/auth/tokens/cli_auth_tokens_initalizer.js +6 -1
  6. package/build/cli/auth/tokens/service/cli_auth_tokens_service.js +8 -1
  7. package/build/cli/class/base_command.js +15 -1
  8. package/build/cli/class/errors/file_system_errors_factory.js +3 -3
  9. package/build/cli/class/errors/http/http_errors_factory.js +2 -2
  10. package/build/cli/commands/auth/cli_auth_add_token_command.js +5 -0
  11. package/build/cli/commands/auth/cli_auth_list_tokens_command.js +20 -11
  12. package/build/cli/commands/auth/cli_auth_logout_command.js +5 -0
  13. package/build/cli/commands/auth/cli_auth_remove_token_command.js +5 -0
  14. package/build/cli/commands/auth/cli_auth_switch_token_command.js +8 -0
  15. package/build/cli/commands/cli_update_command.js +5 -0
  16. package/build/cli/core/cli_setup.js +11 -18
  17. package/build/cli/features/execution_context/execution_context_service.js +2 -0
  18. package/build/cli/features/version/service/cli_version_service.js +2 -2
  19. package/build/cli/hooks/authorization/ensure_authorization_hook.js +9 -2
  20. package/build/cli/hooks/ensure_cli_initialized_hook.js +1 -6
  21. package/build/cli/hooks/ensure_logs_flushed_hook.js +7 -0
  22. package/build/cli/index.js +0 -1
  23. package/build/cli/{features → utilities/features}/http_requester/http_client.js +2 -2
  24. package/build/cli/{features → utilities/features}/http_requester/http_requester_initializer.js +1 -1
  25. package/build/cli/utilities/features/logger/api/logger_api.js +28 -0
  26. package/build/cli/utilities/features/logger/logger_constants.js +7 -0
  27. package/build/cli/utilities/features/logger/logger_initializer.js +44 -0
  28. package/build/cli/utilities/features/logger/logs/app_error.js +14 -0
  29. package/build/cli/utilities/features/logger/logs/app_log.js +40 -0
  30. package/build/cli/{class/errors/app/app_error_constants.js → utilities/features/logger/logs/app_logs_constants.js} +1 -1
  31. package/build/cli/utilities/features/logger/service/logger_service.js +108 -0
  32. package/build/cli/utilities/features/logger/transports/log_object_map_transport.js +15 -22
  33. package/build/index.js +13 -2
  34. package/build/theme/class/archive/theme_archive.js +45 -0
  35. package/build/theme/{features/theme/utils/archive/theme_archive_utils_errors_factory.js → class/archive/theme_archive_errors_factory.js} +2 -2
  36. package/build/theme/class/checksums/theme_checksums.js +34 -52
  37. package/build/theme/class/checksums/theme_checksums_error_factory.js +3 -3
  38. package/build/theme/class/fetch_resources/fetch_resources.js +34 -4
  39. package/build/theme/class/fetch_resources/fetch_resources_errors_factory.js +1 -1
  40. package/build/theme/commands/delete/theme_delete_command.js +3 -2
  41. package/build/theme/commands/info/theme_info_command.js +3 -0
  42. package/build/theme/commands/init/theme_init_command.js +10 -1
  43. package/build/theme/commands/list/theme_list_command.js +22 -8
  44. package/build/theme/commands/publish/theme_publish_command.js +5 -1
  45. package/build/theme/commands/pull/theme_pull_command.js +18 -7
  46. package/build/theme/commands/push/theme_push_command.js +15 -21
  47. package/build/theme/commands/theme_verify_command.js +9 -4
  48. package/build/theme/commands/ui/theme_error.js +3 -3
  49. package/build/theme/features/theme/actions/service/theme_actions_service.js +42 -2
  50. package/build/theme/features/theme/actions/theme_actions_constants.js +1 -2
  51. package/build/theme/features/theme/actions/theme_actions_initializer.js +3 -1
  52. package/build/theme/features/theme/actions/theme_actions_utils.js +5 -31
  53. package/build/theme/features/theme/delete/service/theme_delete_service.js +12 -1
  54. package/build/theme/features/theme/delete/theme_delete_initalizer.js +6 -1
  55. package/build/theme/features/theme/fetch/service/theme_fetch_service.js +37 -8
  56. package/build/theme/features/theme/fetch/theme_fetch_initializer.js +3 -0
  57. package/build/theme/features/theme/init/service/theme_init_service.js +34 -8
  58. package/build/theme/features/theme/init/theme_init_initializer.js +4 -1
  59. package/build/theme/features/theme/merge/service/theme_merge_service.js +40 -6
  60. package/build/theme/features/theme/merge/theme_merge_initializer.js +3 -1
  61. package/build/theme/features/theme/push/api/theme_push_api.js +2 -2
  62. package/build/theme/features/theme/push/service/theme_push_service.js +59 -90
  63. package/build/theme/features/theme/push/theme_push_errors_factory.js +2 -2
  64. package/build/theme/features/theme/push/theme_push_initializer.js +3 -1
  65. package/build/theme/features/theme/push/theme_push_utils.js +3 -3
  66. package/build/theme/features/theme/utils/files_structure/theme_file_structure_errors_factory.js +9 -0
  67. package/build/theme/features/theme/utils/{files/theme_files_utils.js → files_structure/theme_files_structure_utils.js} +23 -34
  68. package/build/theme/features/theme/utils/hidden_directory/hidden_directory_utils.js +5 -2
  69. package/build/theme/features/theme/utils/meta_data/theme_meta_data_error_factory.js +1 -1
  70. package/build/theme/features/theme/utils/resources/theme_resources_with_id_directory_utils.js +5 -2
  71. package/build/theme/features/theme/verify/theme_verify_initializer.js +4 -3
  72. package/build/theme/features/theme/verify/verify/theme_verify_service.js +30 -18
  73. package/build/theme/features/themes/list/services/themes_list_service.js +36 -5
  74. package/build/theme/features/themes/list/themes_list_initializer.js +3 -1
  75. package/build/theme/hooks/ensure_theme_meta_data_untouched_hook.js +4 -2
  76. package/build/theme/hooks/theme_checksums/ensure_theme_current_checksums_up_to_date_hook.js +7 -2
  77. package/build/theme/hooks/themes_actions/ensure_themes_actions_hook.js +2 -3
  78. package/build/ui/hooks/stream_hook.js +26 -0
  79. package/build/utils/array_utils.js +0 -3
  80. package/build/utils/download_file/download_file_errors_factory.js +1 -1
  81. package/build/utils/download_file/download_file_utils.js +19 -1
  82. package/build/utils/fs/errors/stream_read_error.js +7 -5
  83. package/build/utils/fs/errors/stream_write_error.js +7 -5
  84. package/build/utils/fs/fs_utils.js +1 -1
  85. package/build/utils/get_api.js +9 -0
  86. package/build/utils/use_api.js +5 -0
  87. package/build/utils/zip/create_zip_utils.js +21 -9
  88. package/build/utils/zip/errors/create_zip_error.js +7 -5
  89. package/build/utils/zip/errors/open_zip_error.js +7 -5
  90. package/build/utils/zip/extract_zip_utils.js +90 -15
  91. package/oclif.config.js +2 -1
  92. package/package.json +13 -13
  93. package/build/cli/class/errors/app/app_error.js +0 -17
  94. package/build/theme/features/theme/utils/archive/theme_archive_utils.js +0 -24
  95. package/build/theme/features/theme/utils/files/them_files_constants.js +0 -1
  96. package/build/utils/fs/fs_constants.js +0 -6
@@ -0,0 +1,44 @@
1
+ import { AsyncFeatureInitializer, FEATURE_CORES_TYPES } from '@dreamcommerce/star_core';
2
+ import { LoggerService } from './service/logger_service.js';
3
+ import { LoggerApi } from './api/logger_api.js';
4
+ import { EXECUTION_CONTEXT_API_NAME, EXECUTION_CONTEXTS } from '../../../features/execution_context/execution_context_constants.js';
5
+ import { LOGGER_DEBUG_MODES, LOGGER_FEATURE_NAME } from './logger_constants.js';
6
+ import { CLI_VERSION_API_NAME } from '../../../features/version/cli_version_constants.js';
7
+ import { join } from '../../../../utils/path_utils.js';
8
+ export class LoggerInitializer extends AsyncFeatureInitializer {
9
+ static featureName = LOGGER_FEATURE_NAME;
10
+ async init() {
11
+ const executionContextApi = this.getApiSync(EXECUTION_CONTEXT_API_NAME);
12
+ const cliVersionApi = this.getApiSync(CLI_VERSION_API_NAME);
13
+ const executionContext = await executionContextApi.getExecutionContext();
14
+ const service = new LoggerService({
15
+ executionContext,
16
+ cliVersion: await cliVersionApi.getGloballyInstalledVersion(),
17
+ debugMode: this._getDebugMode(executionContext.command),
18
+ logFile: this.getLogFilePath(executionContext)
19
+ });
20
+ return {
21
+ cores: [
22
+ {
23
+ type: FEATURE_CORES_TYPES.api,
24
+ instance: new LoggerApi(service)
25
+ }
26
+ ]
27
+ };
28
+ }
29
+ _getDebugMode(command) {
30
+ if (command.includes('--vv'))
31
+ return LOGGER_DEBUG_MODES.detailed;
32
+ if (command.includes('--v'))
33
+ return LOGGER_DEBUG_MODES.standard;
34
+ return LOGGER_DEBUG_MODES.none;
35
+ }
36
+ getLogFilePath(executionContext) {
37
+ const logFileName = 'shoper_cli_debug_log.json';
38
+ if (executionContext.command.includes('--log-file')) {
39
+ return executionContext.type === EXECUTION_CONTEXTS.theme
40
+ ? join(executionContext.themeRootDir, logFileName)
41
+ : join(process.cwd(), logFileName);
42
+ }
43
+ }
44
+ }
@@ -0,0 +1,14 @@
1
+ import { AppLog } from './app_log.js';
2
+ import { APP_LOGS_TYPES } from './app_logs_constants.js';
3
+ export class AppError extends AppLog {
4
+ constructor({ details, message, level = APP_LOGS_TYPES.error, tags, error, code }) {
5
+ super({ message, level, tags, details });
6
+ this.tags = {
7
+ ...tags,
8
+ code
9
+ };
10
+ this.level = level;
11
+ if (error?.stack)
12
+ this.stacktrace = error.stack;
13
+ }
14
+ }
@@ -0,0 +1,40 @@
1
+ export class AppLog {
2
+ level;
3
+ message;
4
+ user;
5
+ /**
6
+ * Example:
7
+ * {
8
+ * userId: 123,
9
+ * shopId: 456,
10
+ * }
11
+ */
12
+ stacktrace;
13
+ // jakie wartosci dopuszczamy w value tagu?
14
+ tags;
15
+ /**
16
+ * Example:
17
+ * {
18
+ * // always added tags:
19
+ * environment: 'MacOS',
20
+ * appVersion: '1.0.0',
21
+ * nodeVersion: 'v16.0.0',
22
+ * traceId: 'unique-trace-id',
23
+ * command: 'theme init 2'
24
+ * // other tags not always added, spefic to the log, EXAMPLE ONLY added quickly:
25
+ * errorCode: 'E_THEME_NOT_FOUND',
26
+ * filePath: '/path/to/file',
27
+ * responseTimeMs: 250,
28
+ * }
29
+ */
30
+ details;
31
+ // user timestamp in ISO format or server timestamp added when proxied to elastic?
32
+ timestamp = new Date().toISOString();
33
+ constructor({ details, message, level, tags, stacktrace }) {
34
+ this.details = details;
35
+ this.message = message;
36
+ this.level = level;
37
+ this.tags = tags;
38
+ this.stacktrace = stacktrace;
39
+ }
40
+ }
@@ -1,4 +1,4 @@
1
- export const APP_ERRORS_LEVELS = {
1
+ export const APP_LOGS_TYPES = {
2
2
  fatal: 'fatal',
3
3
  error: 'error',
4
4
  warn: 'warn',
@@ -0,0 +1,108 @@
1
+ import pino from 'pino';
2
+ import os from 'node:os';
3
+ import { v4 as uuid } from 'uuid';
4
+ import { LOGGER_DEBUG_MODES } from '../logger_constants.js';
5
+ export class LoggerService {
6
+ #logger;
7
+ #transport;
8
+ #traceId = uuid();
9
+ #executionContext;
10
+ #cliVersion;
11
+ constructor({ executionContext, cliVersion, debugMode, logFile }) {
12
+ this.#executionContext = executionContext;
13
+ this.#cliVersion = cliVersion;
14
+ const { logger, transport } = this._getLoggerInstance(debugMode, logFile);
15
+ this.#logger = logger;
16
+ this.#transport = transport;
17
+ }
18
+ _getLoggerInstance(debugMode, logFile) {
19
+ const targets = [];
20
+ if (logFile) {
21
+ targets.push({
22
+ target: 'pino/file',
23
+ level: 'debug',
24
+ options: {
25
+ destination: logFile,
26
+ mkdir: true,
27
+ append: false
28
+ }
29
+ });
30
+ }
31
+ const prettyTransport = {
32
+ target: 'pino-pretty',
33
+ options: {
34
+ messageKey: 'message',
35
+ colorize: true,
36
+ translateTime: 'SYS:standard',
37
+ ignore: debugMode === LOGGER_DEBUG_MODES.standard
38
+ ? 'pid,hostname,message,traceId,cliVersion,nodeVersion,environment,command,details'
39
+ : ''
40
+ }
41
+ };
42
+ /*
43
+ * To bd potem zastąpine customowym transportem który wysyła logi do backendu
44
+ */
45
+ // const logObjectMapTransport = {
46
+ // target: '../transports/log_object_map_transport.js',
47
+ // options: {
48
+ // traceId: this.#traceId
49
+ // }
50
+ // };
51
+ // TODO docelowo logObjectMapTransport bd wysyłał logi do serwera jezeli bedzie gdzies error a apce
52
+ // targets: debugMode !== LOGGER_DEBUG_MODES.none ? [prettyTransport, logObjectMapTransport] : [logObjectMapTransport]
53
+ if (debugMode !== LOGGER_DEBUG_MODES.none) {
54
+ targets.push(prettyTransport);
55
+ }
56
+ const transport = targets.length
57
+ ? pino.transport({
58
+ targets
59
+ })
60
+ : undefined;
61
+ /**
62
+ * Przy dodawaniu logowania na backend trzeba to usunac
63
+ */
64
+ if (!transport)
65
+ return {};
66
+ const logger = pino({
67
+ level: 'debug',
68
+ base: {
69
+ pid: process.pid,
70
+ hostname: os.hostname(),
71
+ cliVersion: this.#cliVersion,
72
+ nodeVersion: process.version,
73
+ environment: os.type(),
74
+ command: this.#executionContext.command.slice(2).join(' '),
75
+ traceId: this.#traceId
76
+ },
77
+ timestamp: pino.stdTimeFunctions.isoTime,
78
+ messageKey: 'message'
79
+ }, transport);
80
+ return { logger, transport };
81
+ }
82
+ debug(message, props) {
83
+ this.#logger?.debug(props || {}, message);
84
+ }
85
+ info(message, props) {
86
+ this.#logger?.info(props || {}, message);
87
+ }
88
+ warn(message, props) {
89
+ this.#logger?.warn(props || {}, message);
90
+ }
91
+ error(message, props) {
92
+ this.#logger?.error(props || {}, message);
93
+ }
94
+ fatal(message, props) {
95
+ this.#logger?.fatal(props || {}, message);
96
+ }
97
+ async flush() {
98
+ if (!this.#transport || this.#transport?.destroyed) {
99
+ return;
100
+ }
101
+ return new Promise((resolve) => {
102
+ // The 'close' event is emitted when the stream has been fully closed.
103
+ this.#transport?.on('close', resolve);
104
+ this.#logger?.flush(); // Synchronously flush buffered logs
105
+ this.#transport?.end(); // End the stream, which will trigger 'close'
106
+ });
107
+ }
108
+ }
@@ -1,22 +1,15 @@
1
- // import { Writable } from 'node:stream';
2
- //
3
- // export default async function (opts) {
4
- // console.log('opts', opts);
5
- //
6
- // return new Writable({
7
- // objectMode: true,
8
- // write(chunk, enc, cb) {
9
- // const mapped = mapLog(chunk);
10
- //
11
- // cb();
12
- //
13
- // return mapped;
14
- // }
15
- // });
16
- // }
17
- //
18
- // function mapLog(obj) {
19
- // //TODO map for backend structure
20
- // return obj;
21
- // }
22
- export {};
1
+ import { Writable } from 'node:stream';
2
+ export default async function (opts) {
3
+ return new Writable({
4
+ objectMode: true,
5
+ write(chunk, enc, cb) {
6
+ const mapped = mapLog(chunk);
7
+ cb();
8
+ return mapped;
9
+ }
10
+ });
11
+ }
12
+ function mapLog(obj) {
13
+ //TODO map for backend structure
14
+ return obj;
15
+ }
package/build/index.js CHANGED
@@ -9,6 +9,8 @@ import { CliAuthRemoveTokenCommand } from './cli/commands/auth/cli_auth_remove_t
9
9
  import { CliAuthSwitchTokenCommand } from './cli/commands/auth/cli_auth_switch_token_command.js';
10
10
  import { CliUIDumpCommand } from './cli/commands/cli_ui_dump_command.js';
11
11
  import { CliAuthLogoutCommand } from './cli/commands/auth/cli_auth_logout_command.js';
12
+ import { getApiSync } from './utils/get_api.js';
13
+ import { LOGGER_API_NAME } from './cli/utilities/features/logger/logger_constants.js';
12
14
  //TODO
13
15
  //@ts-ignore
14
16
  if (typeof global.crypto !== 'object') {
@@ -24,12 +26,21 @@ function getRandomValues(array) {
24
26
  return crypto.webcrypto.getRandomValues(array);
25
27
  }
26
28
  process.on('uncaughtException', (err) => {
27
- //TODO handle error
28
29
  console.error('Uncaught Exception:', err);
30
+ const loggerApi = getApiSync(LOGGER_API_NAME);
31
+ if (loggerApi)
32
+ loggerApi.fatal('Uncaught Exception', { error: err });
29
33
  process.exit(1);
30
34
  });
31
- process.on('unhandledRejection', (reason, promise) => {
35
+ process.on('unhandledRejection', (reason) => {
32
36
  console.error('Unhandled Rejection:', reason);
37
+ const loggerApi = getApiSync(LOGGER_API_NAME);
38
+ if (loggerApi)
39
+ loggerApi.fatal('Unhandled Rejection', {
40
+ details: {
41
+ reason
42
+ }
43
+ });
33
44
  process.exit(1);
34
45
  });
35
46
  const signals = ['SIGINT', 'SIGTERM', 'SIGQUIT'];
@@ -0,0 +1,45 @@
1
+ import globs from 'fast-glob';
2
+ import { createZip } from '../../../utils/zip/create_zip_utils.js';
3
+ import { ThemeFilesStructureUtils } from '../../features/theme/utils/files_structure/theme_files_structure_utils.js';
4
+ import { extname, join } from '../../../utils/path_utils.js';
5
+ import { formatJSONFile } from '../../../utils/fs/fs_utils.js';
6
+ import { ThemeArchiveErrorsFactory } from './theme_archive_errors_factory.js';
7
+ import { ThemeFileStructureErrorsFactory } from '../../features/theme/utils/files_structure/theme_file_structure_errors_factory.js';
8
+ import { ThemeActionsUtils } from '../../features/theme/actions/theme_actions_utils.js';
9
+ export class ThemeArchive {
10
+ #themeRootDir;
11
+ constructor(themeRootDir) {
12
+ this.#themeRootDir = themeRootDir;
13
+ }
14
+ async createFullArchive({ dist, actionValue, actionType, logger }) {
15
+ const filesStructure = await ThemeFilesStructureUtils.getThemeFilesStructure(this.#themeRootDir);
16
+ if (!filesStructure)
17
+ throw ThemeFileStructureErrorsFactory.createNoFilesStructureError();
18
+ try {
19
+ const filesInThemeDirectory = await globs(ThemeActionsUtils.getFilesGlobsThatMatchesActionName({
20
+ actionType,
21
+ actionValue,
22
+ filesStructure
23
+ }), {
24
+ suppressErrors: true,
25
+ onlyFiles: true,
26
+ cwd: this.#themeRootDir
27
+ });
28
+ await this._formatJsonFiles(this.#themeRootDir, filesInThemeDirectory);
29
+ return createZip({
30
+ files: filesInThemeDirectory,
31
+ logger,
32
+ baseDir: this.#themeRootDir,
33
+ dist
34
+ });
35
+ }
36
+ catch (err) {
37
+ throw ThemeArchiveErrorsFactory.createErrorWhileCreatingThemeArchive(err);
38
+ }
39
+ }
40
+ async _formatJsonFiles(themeRootDir, filesToUpload) {
41
+ await Promise.all(filesToUpload
42
+ .filter((path) => extname(path).toLowerCase() === '.json')
43
+ .map((jsonFile) => formatJSONFile(join(themeRootDir, jsonFile))));
44
+ }
45
+ }
@@ -1,11 +1,11 @@
1
- import { AppError } from '../../../../../cli/class/errors/app/app_error.js';
1
+ import { AppError } from '../../../cli/utilities/features/logger/logs/app_error.js';
2
2
  export class ThemeArchiveErrorsFactory {
3
3
  static createErrorWhileCreatingThemeArchive(error) {
4
4
  return new AppError({
5
5
  code: 'theme_archive.error_creating_archive',
6
6
  message: `Error while creating theme archive`,
7
7
  level: 'error',
8
- stack: error.stack
8
+ error
9
9
  });
10
10
  }
11
11
  }
@@ -10,17 +10,18 @@ import { normalize } from 'node:path';
10
10
  import { ThemePushUtils } from '../../features/theme/push/theme_push_utils.js';
11
11
  import { SHOPER_THEME_METADATA_DIR } from '../../constants/directory_contstants.js';
12
12
  import { THEME_CURRENT_CHECKSUMS_FILE_NAME, THEME_CURRENT_CHECKSUMS_VERITY_FILE_NAME, THEME_INITIAL_CHECKSUMS_FILE_NAME, THEME_INITIAL_CHECKSUMS_VERITY_FILE_NAME } from '../../features/theme/theme_constants.js';
13
- import { FILE_STATES } from '../../../utils/fs/fs_constants.js';
14
13
  export class ThemeChecksums {
15
14
  #themeDir;
15
+ #loggerApi;
16
16
  #currentChecksumsFilePath;
17
17
  #currentChecksumsVerificationFilePath;
18
18
  #initialChecksumsFilePath;
19
19
  #initialChecksumsVerificationFilePath;
20
20
  #initialChecksums;
21
21
  #currentChecksums;
22
- constructor(themeDir) {
22
+ constructor({ loggerApi, themeDir }) {
23
23
  this.#themeDir = themeDir;
24
+ this.#loggerApi = loggerApi;
24
25
  this.#currentChecksumsFilePath = ThemeChecksumsUtils.getCurrentThemeChecksumsFilePath(themeDir);
25
26
  this.#currentChecksumsVerificationFilePath = ThemeChecksumsUtils.getCurrentThemeChecksumsVerificationFilePath(themeDir);
26
27
  this.#initialChecksumsFilePath = ThemeChecksumsUtils.getInitialThemeChecksumsFilePath(themeDir);
@@ -32,6 +33,12 @@ export class ThemeChecksums {
32
33
  this.#initialChecksums = await this._getChecksums(this.#initialChecksumsFilePath);
33
34
  return this.#initialChecksums;
34
35
  }
36
+ getInitialChecksumsSync() {
37
+ if (this.#initialChecksums)
38
+ return this.#initialChecksums;
39
+ this.#initialChecksums = this._getChecksumsSync(this.#initialChecksumsFilePath);
40
+ return this.#initialChecksums;
41
+ }
35
42
  async getCurrentChecksums() {
36
43
  if (this.#currentChecksums)
37
44
  return this.#currentChecksums;
@@ -65,22 +72,10 @@ export class ThemeChecksums {
65
72
  const initialChecksum = await this.getInitialChecksumFromPath(path);
66
73
  return !initialChecksum && (await fileExists(join(this.#themeDir, path)));
67
74
  }
68
- async getFileState(path) {
69
- const initialChecksum = await this.getInitialChecksumFromPath(path);
75
+ async hasThemeFileBeenModified(path) {
70
76
  const currentChecksum = await this.getCurrentChecksumFromPath(path);
71
- const fileExistsInFs = await fileExists(join(this.#themeDir, path));
72
- if (!initialChecksum && fileExistsInFs) {
73
- return FILE_STATES.created;
74
- }
75
- else if (initialChecksum && !fileExistsInFs) {
76
- return FILE_STATES.deleted;
77
- }
78
- else if (initialChecksum && currentChecksum && initialChecksum !== currentChecksum) {
79
- return FILE_STATES.modified;
80
- }
81
- else {
82
- return FILE_STATES.unchanged;
83
- }
77
+ const initialChecksum = await this.getInitialChecksumFromPath(path);
78
+ return !!currentChecksum && !!initialChecksum && currentChecksum !== initialChecksum;
84
79
  }
85
80
  async verify() {
86
81
  const initialChecksumFilePath = this.#initialChecksumsFilePath;
@@ -90,6 +85,7 @@ export class ThemeChecksums {
90
85
  return initialChecksum === initialChecksumVerify;
91
86
  }
92
87
  async updateAllChecksums() {
88
+ this.#loggerApi?.debug('Updating all checksums.');
93
89
  const checksums = await this.computeThemeChecksums();
94
90
  return new Promise((resolve, reject) => {
95
91
  const currentChecksumFilePath = this.#currentChecksumsFilePath;
@@ -99,20 +95,24 @@ export class ThemeChecksums {
99
95
  checksumStream.end();
100
96
  checksumStream
101
97
  .on('finish', async () => {
98
+ this.#loggerApi?.debug('Finished writing initial checksums file. Creating verification files.');
102
99
  const initialChecksumVerifyFilePath = this.#initialChecksumsVerificationFilePath;
103
100
  const currentChecksumVerifyFilePath = this.#currentChecksumsVerificationFilePath;
104
101
  await this._createThemeChecksumVerifyFile(initialChecksumFilePath, initialChecksumVerifyFilePath);
105
102
  await copyFile(initialChecksumFilePath, currentChecksumFilePath);
106
103
  await copyFile(initialChecksumVerifyFilePath, currentChecksumVerifyFilePath);
104
+ this.#loggerApi?.debug('All checksums and verification files updated.');
107
105
  resolve();
108
106
  })
109
107
  .on('error', async (err) => {
108
+ this.#loggerApi?.debug('Error writing checksums file. Cleaning up.', { details: { file: initialChecksumFilePath } });
110
109
  await removeFile(initialChecksumFilePath, { force: true });
111
- reject(ThemeChecksumsErrorFactory.createThemeChecksumError(err.stack));
110
+ reject(ThemeChecksumsErrorFactory.createThemeChecksumError(err));
112
111
  });
113
112
  });
114
113
  }
115
114
  async updateCurrentChecksums() {
115
+ this.#loggerApi?.debug('Updating current checksums.');
116
116
  const checksums = await this.computeThemeChecksums();
117
117
  return new Promise((resolve, reject) => {
118
118
  const currentChecksumFilePath = this.#currentChecksumsFilePath;
@@ -121,17 +121,23 @@ export class ThemeChecksums {
121
121
  checksumStream.end();
122
122
  checksumStream
123
123
  .on('finish', async () => {
124
+ this.#loggerApi?.debug('Finished writing current checksums file. Creating verification file.');
124
125
  const currentChecksumVerifyFilePath = this.#currentChecksumsVerificationFilePath;
125
126
  await this._createThemeChecksumVerifyFile(currentChecksumFilePath, currentChecksumVerifyFilePath);
127
+ this.#loggerApi?.debug('Current checksums and verification file updated.');
126
128
  resolve();
127
129
  })
128
130
  .on('error', async (err) => {
131
+ this.#loggerApi?.debug('Error writing current checksums file. Cleaning up.', {
132
+ details: { file: currentChecksumFilePath }
133
+ });
129
134
  await removeFile(currentChecksumFilePath, { force: true });
130
- reject(ThemeChecksumsErrorFactory.createThemeChecksumError(err.stack));
135
+ reject(ThemeChecksumsErrorFactory.createThemeChecksumError(err));
131
136
  });
132
137
  });
133
138
  }
134
139
  async computeThemeChecksums() {
140
+ this.#loggerApi?.debug('Computing theme checksums.');
135
141
  const filesToIgnoreInChecksums = [
136
142
  join(SHOPER_THEME_METADATA_DIR, THEME_CURRENT_CHECKSUMS_FILE_NAME),
137
143
  join(SHOPER_THEME_METADATA_DIR, THEME_CURRENT_CHECKSUMS_VERITY_FILE_NAME),
@@ -141,40 +147,12 @@ export class ThemeChecksums {
141
147
  const filesToComputeChecksums = (await ThemePushUtils.getAllFilesThatAreSendToRemote(this.#themeDir))
142
148
  .filter((path) => !filesToIgnoreInChecksums.some((ignorePath) => normalize(path) === ignorePath))
143
149
  .map((relativePath) => join(this.#themeDir, relativePath));
150
+ this.#loggerApi?.debug('Computing checksums from file structure.', { details: { count: filesToComputeChecksums.length } });
144
151
  const { filesChecksumsInDirectories, filesChecksums } = await computeChecksumsFromFilesStructure(filesToComputeChecksums, this.#themeDir);
145
152
  const directoriesChecksums = computeDirectoriesChecksums(filesChecksumsInDirectories);
146
- return { ...filesChecksums, ...directoriesChecksums };
147
- }
148
- async groupFilesByStatus(files) {
149
- const initialChecksums = await this.getInitialChecksums();
150
- const currentChecksums = await this.getCurrentChecksums();
151
- return files.reduce((acc, filePath) => {
152
- const initialChecksum = initialChecksums[toCurrentPlatformPath(filePath)];
153
- const currentChecksum = currentChecksums[toCurrentPlatformPath(filePath)];
154
- if (!initialChecksum) {
155
- acc.created.push(filePath);
156
- }
157
- else if (currentChecksum && initialChecksum !== currentChecksum) {
158
- acc.modified.push(filePath);
159
- }
160
- else if (initialChecksum && !currentChecksum) {
161
- acc.deleted.push(filePath);
162
- }
163
- else {
164
- acc.unchanged.push(filePath);
165
- }
166
- return acc;
167
- }, {
168
- created: [],
169
- modified: [],
170
- unchanged: [],
171
- deleted: []
172
- });
173
- }
174
- async getDeletedFiles() {
175
- const initialChecksums = await this.getInitialChecksums();
176
- const currentChecksums = await this.getCurrentChecksums();
177
- return Object.keys(initialChecksums).filter((filePath) => !currentChecksums[filePath]);
153
+ const allChecksums = { ...filesChecksums, ...directoriesChecksums };
154
+ this.#loggerApi?.debug('Finished computing theme checksums.', { details: { count: Object.keys(allChecksums).length } });
155
+ return allChecksums;
178
156
  }
179
157
  async _getInitialChecksumsVerification() {
180
158
  return await readJSONFile(this.#initialChecksumsVerificationFilePath);
@@ -195,12 +173,16 @@ export class ThemeChecksums {
195
173
  return mapKeysPathsToWindowPlatform(checksums);
196
174
  }
197
175
  async _createThemeChecksumVerifyFile(checksumFilePath, checksumVerifyFullPath) {
176
+ this.#loggerApi?.debug('Creating checksum verification file.', { details: { path: checksumVerifyFullPath } });
198
177
  const checksumVerifyStream = createWriteStream(checksumVerifyFullPath);
199
178
  const checksumVerify = await computeFileChecksum(checksumFilePath);
200
179
  checksumVerifyStream
201
180
  .on('error', async (err) => {
181
+ this.#loggerApi?.debug('Error creating checksum verification file. Cleaning up.', {
182
+ details: { file: checksumFilePath }
183
+ });
202
184
  await removeFile(checksumFilePath, { force: true });
203
- throw ThemeChecksumsErrorFactory.createThemeChecksumError(err.stack);
185
+ throw ThemeChecksumsErrorFactory.createThemeChecksumError(err);
204
186
  })
205
187
  .write(JSON.stringify(checksumVerify));
206
188
  checksumVerifyStream.end();
@@ -1,10 +1,10 @@
1
- import { AppError } from '../../../cli/class/errors/app/app_error.js';
1
+ import { AppError } from '../../../cli/utilities/features/logger/logs/app_error.js';
2
2
  export class ThemeChecksumsErrorFactory {
3
- static createThemeChecksumError(stack) {
3
+ static createThemeChecksumError(error) {
4
4
  return new AppError({
5
5
  message: 'Theme checksum error',
6
6
  code: 'theme_checksum_error',
7
- stack
7
+ error
8
8
  });
9
9
  }
10
10
  }
@@ -12,16 +12,23 @@ import { move } from 'fs-extra';
12
12
  export class FetchResources {
13
13
  #urlsAttempts = {};
14
14
  #httpApi;
15
- constructor(httpApi) {
15
+ #loggerApi;
16
+ constructor({ httpApi, loggerApi }) {
16
17
  this.#httpApi = httpApi;
18
+ this.#loggerApi = loggerApi;
17
19
  }
18
20
  async fetchResources({ dist, resourcesPart, parts, shopUrl }) {
21
+ this.#loggerApi.debug('Traversing resources part.', { details: { parts } });
19
22
  if (Array.isArray(resourcesPart)) {
20
23
  for (let i = 0; i < resourcesPart.length; i++) {
21
24
  const resource = resourcesPart[i];
22
25
  if (isResourceObject(resource)) {
26
+ const resourceUrl = isRelativeUrl(resource.url) ? `${shopUrl}${resource.url}` : resource.url;
27
+ this.#loggerApi.debug('Found resource object. Fetching resource.', {
28
+ details: { url: resourceUrl, parts: [...parts, i] }
29
+ });
23
30
  await this._fetchResource({
24
- url: isRelativeUrl(resource.url) ? `${shopUrl}${resource.url}` : resource.url,
31
+ url: resourceUrl,
25
32
  dist,
26
33
  parts: [...parts, i],
27
34
  shopUrl,
@@ -40,8 +47,12 @@ export class FetchResources {
40
47
  }
41
48
  if (typeof resourcesPart === 'object') {
42
49
  if (isResourceObject(resourcesPart)) {
50
+ const resourceUrl = isRelativeUrl(resourcesPart.url) ? `${shopUrl}${resourcesPart.url}` : resourcesPart.url;
51
+ this.#loggerApi.debug('Found resource object. Fetching resource.', {
52
+ details: { url: resourceUrl, parts }
53
+ });
43
54
  await this._fetchResource({
44
- url: isRelativeUrl(resourcesPart.url) ? `${shopUrl}${resourcesPart.url}` : resourcesPart.url,
55
+ url: resourceUrl,
45
56
  dist,
46
57
  parts,
47
58
  shopUrl,
@@ -61,6 +72,7 @@ export class FetchResources {
61
72
  return;
62
73
  }
63
74
  async _getResourcesFileContent(resourcePath) {
75
+ this.#loggerApi.debug('Reading and mapping resources file content.', { details: { resourcePath } });
64
76
  return mapResourcesToTree(await readJSONFile(resourcePath));
65
77
  }
66
78
  _getResourcesPart(resources, parts) {
@@ -68,28 +80,42 @@ export class FetchResources {
68
80
  }
69
81
  async _fetchResource({ url, shopUrl, parts, dist, isPrivate }) {
70
82
  this.#urlsAttempts[url] = (this.#urlsAttempts[url] ?? 0) + 1;
83
+ this.#loggerApi.debug('Fetching resource.', {
84
+ details: { url, attempt: this.#urlsAttempts[url], parts, isPrivate }
85
+ });
71
86
  if (this.#urlsAttempts[url] > MAX_REQUEST_FOR_RESOURCES)
72
87
  throw FetchResourcesErrorsFactory.createErrorFetchingResource(shopUrl, [
73
88
  `Failed to fetch ${url} after 3 attempts, for: ${parts.join('')}`
74
89
  ]);
75
90
  const { path: tmpDir } = await tmp.dir({ unsafeCleanup: true, prefix: 'theme_resources_tmp' });
91
+ this.#loggerApi.debug('Created temporary directory for resource fetch.', { details: { tmpDir, url } });
76
92
  try {
93
+ this.#loggerApi.debug('Downloading resource file.', { details: { url, dist: tmpDir } });
77
94
  const { filename, ext, basename } = await downloadFile({
78
95
  dist: tmpDir,
96
+ logger: this.#loggerApi,
79
97
  request: this._getRequest({ url, isPrivate }).response
80
98
  });
99
+ this.#loggerApi.debug('Resource file downloaded.', { details: { url, filename } });
81
100
  if (ext === '.zip') {
101
+ this.#loggerApi.debug('Resource is a zip archive. Extracting.', { details: { source: filename, dist: basename } });
82
102
  await extractZip({
103
+ logger: this.#loggerApi,
83
104
  source: join(tmpDir, filename),
84
105
  dist: join(tmpDir, basename)
85
106
  });
107
+ this.#loggerApi.debug('Zip archive extracted.', { details: { filename } });
86
108
  if (!(await fileExists(getResourcesPath(join(tmpDir, basename)))))
87
109
  return;
110
+ this.#loggerApi.debug('Copying extracted files.', {
111
+ details: { from: join(tmpDir, basename), to: dist }
112
+ });
88
113
  await copyFile(join(tmpDir, basename), dist, {
89
114
  recursive: true,
90
115
  force: true
91
116
  });
92
117
  const newResources = await this._getResourcesFileContent(getResourcesPath(join(tmpDir, basename)));
118
+ this.#loggerApi.debug('Recursively fetching resources from extracted archive.', { details: { parts } });
93
119
  await this.fetchResources({
94
120
  dist,
95
121
  resourcesPart: this._getResourcesPart(newResources, parts),
@@ -98,7 +124,11 @@ export class FetchResources {
98
124
  });
99
125
  }
100
126
  else {
101
- await move(join(tmpDir, filename), join(dist, this._getFilepathFromParts(parts), filename), { overwrite: true });
127
+ const destinationPath = join(dist, this._getFilepathFromParts(parts), filename);
128
+ this.#loggerApi.debug('Moving downloaded file.', {
129
+ details: { from: join(tmpDir, filename), to: destinationPath }
130
+ });
131
+ await move(join(tmpDir, filename), destinationPath, { overwrite: true });
102
132
  }
103
133
  }
104
134
  catch (err) {
@@ -1,4 +1,4 @@
1
- import { AppError } from '../../../cli/class/errors/app/app_error.js';
1
+ import { AppError } from '../../../cli/utilities/features/logger/logs/app_error.js';
2
2
  export class FetchResourcesErrorsFactory {
3
3
  static createErrorFetchingResource(shopUrl, messages) {
4
4
  return new AppError({