@tywalk/pcf-helper 1.4.24 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -25,3 +25,9 @@ This tool requires the following:
25
25
  "init": "pcf-helper-init --path <path to pcf project folder (optional)> --name <name of the pcf project> --publisher-name <powerapps publisher name> --publisher-prefix <powerapps publisher prefix>"
26
26
  },
27
27
  ```
28
+
29
+ ## Contributing
30
+
31
+ ### Deployment
32
+
33
+
@@ -13,7 +13,7 @@ test('build displays version', (done) => {
13
13
  });
14
14
  task.on('close', (code) => {
15
15
  console.log(output);
16
- expect(output).toContain(package_json_1.version);
16
+ expect(output).toContain(`v${package_json_1.version}`);
17
17
  expect(code).toBe(0);
18
18
  done();
19
19
  });
@@ -13,7 +13,7 @@ test('deploy displays version', (done) => {
13
13
  });
14
14
  task.on('close', (code) => {
15
15
  console.log(output);
16
- expect(output).toContain(package_json_1.version);
16
+ expect(output).toContain(`v${package_json_1.version}`);
17
17
  expect(code).toBe(0);
18
18
  done();
19
19
  });
@@ -10,7 +10,7 @@ test('import displays version', (done) => {
10
10
  });
11
11
  task.on('close', (code) => {
12
12
  console.log(output);
13
- expect(output).toContain(package_json_1.version);
13
+ expect(output).toContain(`v${package_json_1.version}`);
14
14
  expect(code).toBe(0);
15
15
  done();
16
16
  });
@@ -10,7 +10,7 @@ test('init displays version', (done) => {
10
10
  });
11
11
  task.on('close', (code) => {
12
12
  console.log(output);
13
- expect(output).toContain(package_json_1.version);
13
+ expect(output).toContain(`v${package_json_1.version}`);
14
14
  expect(code).toBe(0);
15
15
  done();
16
16
  });
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const child_process_1 = require("child_process");
4
+ const package_json_1 = require("../package.json");
5
+ test('session displays version', (done) => {
6
+ const task = (0, child_process_1.spawn)('node', ['./dist/bin/session.js', '-v']);
7
+ let output = '';
8
+ task.stdout.on('data', (data) => {
9
+ output += data.toString();
10
+ });
11
+ task.on('close', (code) => {
12
+ console.log(output);
13
+ expect(output).toContain(`v${package_json_1.version}`);
14
+ expect(code).toBe(0);
15
+ done();
16
+ });
17
+ });
18
+ test('session errors if no args are provided', (done) => {
19
+ const task = (0, child_process_1.spawn)('node', ['./dist/bin/session.js', '-e']);
20
+ let output = '';
21
+ task.stdout.on('data', (data) => {
22
+ output += data.toString();
23
+ });
24
+ task.stderr.on('data', (data) => {
25
+ console.error(`stderr: ${data}`);
26
+ });
27
+ task.on('close', (code) => {
28
+ console.log(output);
29
+ expect(code).toBe(1);
30
+ done();
31
+ });
32
+ });
@@ -10,7 +10,7 @@ test('upgrade displays version', (done) => {
10
10
  });
11
11
  task.on('close', (code) => {
12
12
  console.log(output);
13
- expect(output).toContain(package_json_1.version);
13
+ expect(output).toContain(`v${package_json_1.version}`);
14
14
  expect(code).toBe(0);
15
15
  done();
16
16
  });
package/dist/bin/build.js CHANGED
@@ -36,34 +36,29 @@ var __importStar = (this && this.__importStar) || (function () {
36
36
  var __importDefault = (this && this.__importDefault) || function (mod) {
37
37
  return (mod && mod.__esModule) ? mod : { "default": mod };
38
38
  };
39
- var _a, _b;
40
39
  Object.defineProperty(exports, "__esModule", { value: true });
41
40
  const task = __importStar(require("../tasks/build-pcf"));
42
41
  const package_json_1 = require("../package.json");
43
42
  const color_logger_1 = __importDefault(require("@tywalk/color-logger"));
44
- const argumentUtil_1 = require("../util/argumentUtil");
45
- const [, , ...args] = process.argv;
46
- const commandArgument = (_b = (_a = args.at(0)) === null || _a === void 0 ? void 0 : _a.toLowerCase()) !== null && _b !== void 0 ? _b : '';
47
- if (['-v', '--version'].includes(commandArgument)) {
48
- console.log('v%s', package_json_1.version);
49
- process.exit(0);
50
- }
51
- const timeout = (0, argumentUtil_1.getArgValue)(args, ['-t', '--timeout']);
52
- if (typeof timeout !== 'undefined') {
53
- const timeoutNumber = Number(timeout);
54
- if (isNaN(timeoutNumber) || timeoutNumber <= 0) {
55
- color_logger_1.default.error('Timeout argument must be a positive number representing milliseconds.');
56
- process.exit(1);
43
+ const commander_1 = require("commander");
44
+ const program = new commander_1.Command();
45
+ program
46
+ .name('pcf-helper-build')
47
+ .description('Build PCF controls')
48
+ .version(package_json_1.version, '-v, --version')
49
+ .option('-V, --verbose', 'enable verbose logging')
50
+ .option('-t, --timeout <milliseconds>', 'timeout in milliseconds', (value) => {
51
+ const num = Number(value);
52
+ if (isNaN(num) || num <= 0) {
53
+ throw new Error('Timeout must be a positive number');
57
54
  }
58
- }
59
- const verboseArgument = args.find(a => ['-v', '--verbose'].includes(a));
60
- if (typeof verboseArgument !== 'undefined') {
55
+ return value;
56
+ })
57
+ .requiredOption('-p, --path <path>', 'path to solution folder')
58
+ .parse();
59
+ const options = program.opts();
60
+ if (options.verbose) {
61
61
  color_logger_1.default.setDebug(true);
62
62
  }
63
63
  color_logger_1.default.log('PCF Helper version', package_json_1.version);
64
- const path = (0, argumentUtil_1.getArgValue)(args, ['-p', '--path']);
65
- if (typeof path === 'undefined') {
66
- color_logger_1.default.error('Path argument is required. Use --path to specify the path to solution folder.');
67
- process.exit(1);
68
- }
69
- task.runBuild(path, verboseArgument !== undefined, typeof timeout !== 'undefined' ? Number(timeout) : undefined);
64
+ task.runBuild(options.path, options.verbose || false, options.timeout ? Number(options.timeout) : undefined);
@@ -36,7 +36,6 @@ var __importStar = (this && this.__importStar) || (function () {
36
36
  var __importDefault = (this && this.__importDefault) || function (mod) {
37
37
  return (mod && mod.__esModule) ? mod : { "default": mod };
38
38
  };
39
- var _a, _b, _c;
40
39
  Object.defineProperty(exports, "__esModule", { value: true });
41
40
  const upgradeTask = __importStar(require("../tasks/upgrade-pcf"));
42
41
  const buildTask = __importStar(require("../tasks/build-pcf"));
@@ -44,39 +43,29 @@ const importTask = __importStar(require("../tasks/import-pcf"));
44
43
  const performanceUtil_1 = require("../util/performanceUtil");
45
44
  const package_json_1 = require("../package.json");
46
45
  const color_logger_1 = __importDefault(require("@tywalk/color-logger"));
46
+ const commander_1 = require("commander");
47
47
  const argumentUtil_1 = require("../util/argumentUtil");
48
- const [, , ...args] = process.argv;
49
- const commandArgument = (_b = (_a = args.at(0)) === null || _a === void 0 ? void 0 : _a.toLowerCase()) !== null && _b !== void 0 ? _b : '';
50
- if (['-v', '--version'].includes(commandArgument)) {
51
- console.log('v%s', package_json_1.version);
52
- process.exit(0);
53
- }
54
- const verboseArgument = args.find(a => ['-v', '--verbose'].includes(a));
55
- if (typeof verboseArgument !== 'undefined') {
56
- color_logger_1.default.setDebug(true);
57
- }
48
+ // Apply argument preprocessing for backward compatibility
49
+ const { hadDeprecatedEnv } = (0, argumentUtil_1.applyArgumentPreprocessing)(process.argv);
50
+ const program = new commander_1.Command();
51
+ (0, argumentUtil_1.addPathAndEnvironmentOptions)(program)
52
+ .name('pcf-helper-deploy')
53
+ .description('Deploy PCF controls (runs upgrade, build, and import)')
54
+ .version(package_json_1.version, '-v, --version')
55
+ .parse();
56
+ const options = program.opts();
57
+ (0, argumentUtil_1.setupLogging)(options.verbose);
58
58
  color_logger_1.default.log('PCF Helper version', package_json_1.version);
59
- const pathArgument = args.find(a => ['-p', '--path'].includes(a));
60
- if (typeof pathArgument === 'undefined') {
61
- color_logger_1.default.error('Path argument is required. Use --path to specify the path to solution folder.');
62
- process.exit(1);
63
- }
64
- const pathIndex = args.indexOf(pathArgument) + 1;
65
- const path = args.at(pathIndex);
66
- if (typeof path === 'undefined') {
67
- color_logger_1.default.error('Path argument is required. Use --path to specify the path to solution folder.');
68
- process.exit(1);
69
- }
59
+ const env = (0, argumentUtil_1.resolveEnvironment)(options, hadDeprecatedEnv);
70
60
  const tick = performance.now();
71
- const env = (_c = (0, argumentUtil_1.getArgValue)(args, ['-env', '--environment'])) !== null && _c !== void 0 ? _c : '';
72
61
  function executeTasks() {
73
- const upgradeResult = upgradeTask.runUpgrade(path, typeof verboseArgument !== 'undefined');
62
+ const upgradeResult = upgradeTask.runUpgrade(options.path, options.verbose || false);
74
63
  if (upgradeResult === 1)
75
64
  return 1;
76
- const buildResult = buildTask.runBuild(path, typeof verboseArgument !== 'undefined');
65
+ const buildResult = buildTask.runBuild(options.path, options.verbose || false, options.timeout ? Number(options.timeout) : undefined);
77
66
  if (buildResult === 1)
78
67
  return 1;
79
- const importResult = importTask.runImport(path, env, typeof verboseArgument !== 'undefined');
68
+ const importResult = importTask.runImport(options.path, env, options.verbose || false, options.timeout ? Number(options.timeout) : undefined);
80
69
  if (importResult === 1)
81
70
  return 1;
82
71
  return 0;
@@ -89,7 +78,7 @@ try {
89
78
  }
90
79
  }
91
80
  catch (e) {
92
- color_logger_1.default.error('One or more tasks failed while deploying: ', (e && e.message) || 'unkown error');
81
+ color_logger_1.default.error('One or more tasks failed while deploying: ', (e && e.message) || 'unknown error');
93
82
  result = 1;
94
83
  }
95
84
  finally {
@@ -36,35 +36,22 @@ var __importStar = (this && this.__importStar) || (function () {
36
36
  var __importDefault = (this && this.__importDefault) || function (mod) {
37
37
  return (mod && mod.__esModule) ? mod : { "default": mod };
38
38
  };
39
- var _a, _b, _c;
40
39
  Object.defineProperty(exports, "__esModule", { value: true });
41
40
  const task = __importStar(require("../tasks/import-pcf"));
42
41
  const package_json_1 = require("../package.json");
43
42
  const color_logger_1 = __importDefault(require("@tywalk/color-logger"));
43
+ const commander_1 = require("commander");
44
44
  const argumentUtil_1 = require("../util/argumentUtil");
45
- const [, , ...args] = process.argv;
46
- const commandArgument = (_b = (_a = args.at(0)) === null || _a === void 0 ? void 0 : _a.toLowerCase()) !== null && _b !== void 0 ? _b : '';
47
- if (['-v', '--version'].includes(commandArgument)) {
48
- console.log('v%s', package_json_1.version);
49
- process.exit(0);
50
- }
51
- const timeout = (0, argumentUtil_1.getArgValue)(args, ['-t', '--timeout']);
52
- if (typeof timeout !== 'undefined') {
53
- const timeoutNumber = Number(timeout);
54
- if (isNaN(timeoutNumber) || timeoutNumber <= 0) {
55
- color_logger_1.default.error('Timeout argument must be a positive number representing milliseconds.');
56
- process.exit(1);
57
- }
58
- }
59
- const verboseArgument = args.find(a => ['-v', '--verbose'].includes(a));
60
- if (typeof verboseArgument !== 'undefined') {
61
- color_logger_1.default.setDebug(true);
62
- }
45
+ // Apply argument preprocessing for backward compatibility
46
+ const { hadDeprecatedEnv } = (0, argumentUtil_1.applyArgumentPreprocessing)(process.argv);
47
+ const program = new commander_1.Command();
48
+ (0, argumentUtil_1.addPathAndEnvironmentOptions)(program)
49
+ .name('pcf-helper-import')
50
+ .description('Import PCF controls to Dataverse')
51
+ .version(package_json_1.version, '-v, --version')
52
+ .parse();
53
+ const options = program.opts();
54
+ (0, argumentUtil_1.setupLogging)(options.verbose);
63
55
  color_logger_1.default.log('PCF Helper version', package_json_1.version);
64
- const path = (0, argumentUtil_1.getArgValue)(args, ['-p', '--path']);
65
- if (typeof path === 'undefined') {
66
- color_logger_1.default.error('Path argument is required. Use --path to specify the path to solution folder.');
67
- process.exit(1);
68
- }
69
- const env = (_c = (0, argumentUtil_1.getArgValue)(args, ['-env', '--environment'])) !== null && _c !== void 0 ? _c : '';
70
- task.runImport(path, env, verboseArgument !== undefined, typeof timeout !== 'undefined' ? Number(timeout) : undefined);
56
+ const env = (0, argumentUtil_1.resolveEnvironment)(options, hadDeprecatedEnv);
57
+ task.runImport(options.path, env, options.verbose || false, options.timeout ? Number(options.timeout) : undefined);
package/dist/bin/init.js CHANGED
@@ -36,30 +36,26 @@ var __importStar = (this && this.__importStar) || (function () {
36
36
  var __importDefault = (this && this.__importDefault) || function (mod) {
37
37
  return (mod && mod.__esModule) ? mod : { "default": mod };
38
38
  };
39
- var _a, _b, _c, _d, _e;
40
39
  Object.defineProperty(exports, "__esModule", { value: true });
41
40
  const task = __importStar(require("../tasks/init-pcf"));
42
41
  const package_json_1 = require("../package.json");
43
42
  const color_logger_1 = __importDefault(require("@tywalk/color-logger"));
44
- const argumentUtil_1 = require("../util/argumentUtil");
45
- const [, , ...args] = process.argv;
46
- const commandArgument = (_b = (_a = args.at(0)) === null || _a === void 0 ? void 0 : _a.toLowerCase()) !== null && _b !== void 0 ? _b : '';
47
- if (['-v', '--version'].includes(commandArgument)) {
48
- console.log('v%s', package_json_1.version);
49
- process.exit(0);
50
- }
51
- const verboseArgument = args.find(a => ['-v', '--verbose'].includes(a));
52
- if (typeof verboseArgument !== 'undefined') {
43
+ const commander_1 = require("commander");
44
+ const program = new commander_1.Command();
45
+ program
46
+ .name('pcf-helper-init')
47
+ .description('Initialize a new PCF project')
48
+ .version(package_json_1.version, '-v, --version')
49
+ .option('-V, --verbose', 'enable verbose logging')
50
+ .requiredOption('-n, --name <name>', 'name of the PCF control')
51
+ .option('--publisher-name <publisherName>', 'publisher name')
52
+ .option('--publisher-prefix <publisherPrefix>', 'publisher prefix')
53
+ .option('-p, --path <path>', 'path to create the PCF project')
54
+ .option('--run-npm-install', 'run npm install after initialization', true)
55
+ .parse();
56
+ const options = program.opts();
57
+ if (options.verbose) {
53
58
  color_logger_1.default.setDebug(true);
54
59
  }
55
60
  color_logger_1.default.log('PCF Helper version', package_json_1.version);
56
- const name = (0, argumentUtil_1.getArgValue)(args, ['-n', '--name']);
57
- if (typeof name === 'undefined') {
58
- color_logger_1.default.error('Name argument is required. Use --name to specify the name of the PCF control.');
59
- process.exit(1);
60
- }
61
- const publisherName = (_c = (0, argumentUtil_1.getArgValue)(args, ['-pn', '--publisher-name'])) !== null && _c !== void 0 ? _c : '';
62
- const publisherPrefix = (_d = (0, argumentUtil_1.getArgValue)(args, ['-pp', '--publisher-prefix'])) !== null && _d !== void 0 ? _d : '';
63
- const path = (_e = (0, argumentUtil_1.getArgValue)(args, ['-p', '--path'])) !== null && _e !== void 0 ? _e : '';
64
- const npm = (0, argumentUtil_1.getArgValue)(args, ['-npm', '--run-npm-install'], 'true');
65
- task.runInit(path, name, publisherName, publisherPrefix, npm === 'true', verboseArgument !== undefined);
61
+ task.runInit(options.path || '', options.name, options.publisherName || '', options.publisherPrefix || '', options.runNpmInstall !== false, options.verbose || false);
@@ -0,0 +1,64 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
4
+ if (k2 === undefined) k2 = k;
5
+ var desc = Object.getOwnPropertyDescriptor(m, k);
6
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
7
+ desc = { enumerable: true, get: function() { return m[k]; } };
8
+ }
9
+ Object.defineProperty(o, k2, desc);
10
+ }) : (function(o, m, k, k2) {
11
+ if (k2 === undefined) k2 = k;
12
+ o[k2] = m[k];
13
+ }));
14
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
15
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
16
+ }) : function(o, v) {
17
+ o["default"] = v;
18
+ });
19
+ var __importStar = (this && this.__importStar) || (function () {
20
+ var ownKeys = function(o) {
21
+ ownKeys = Object.getOwnPropertyNames || function (o) {
22
+ var ar = [];
23
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
24
+ return ar;
25
+ };
26
+ return ownKeys(o);
27
+ };
28
+ return function (mod) {
29
+ if (mod && mod.__esModule) return mod;
30
+ var result = {};
31
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
32
+ __setModuleDefault(result, mod);
33
+ return result;
34
+ };
35
+ })();
36
+ var __importDefault = (this && this.__importDefault) || function (mod) {
37
+ return (mod && mod.__esModule) ? mod : { "default": mod };
38
+ };
39
+ Object.defineProperty(exports, "__esModule", { value: true });
40
+ const task = __importStar(require("../tasks/session-pcf"));
41
+ const package_json_1 = require("../package.json");
42
+ const color_logger_1 = __importDefault(require("@tywalk/color-logger"));
43
+ const commander_1 = require("commander");
44
+ const program = new commander_1.Command();
45
+ program
46
+ .name('pcf-helper-session')
47
+ .description('Run development session')
48
+ .version(package_json_1.version, '-v, --version')
49
+ .option('-V, --verbose', 'enable verbose logging')
50
+ .option('-u, --url <url>', 'remote environment URL')
51
+ .option('-s, --script <script>', 'remote script to intercept')
52
+ .option('-t, --stylesheet <stylesheet>', 'remote stylesheet to intercept')
53
+ .option('-b, --bundle <path>', 'local bundle path')
54
+ .option('-c, --css <path>', 'local CSS path')
55
+ .option('-f, --config <path>', 'config file path', 'dev-config.json')
56
+ .parse();
57
+ const options = program.opts();
58
+ if (options.verbose) {
59
+ color_logger_1.default.setDebug(true);
60
+ color_logger_1.default.debug('Verbose logging enabled');
61
+ }
62
+ color_logger_1.default.log('PCF Helper version', package_json_1.version);
63
+ const config = task.loadConfig(options.config);
64
+ task.runSession(config.remoteEnvironmentUrl, config.remoteScriptToIntercept, config.remoteStylesheetToIntercept, config.localBundlePath, config.localCssPath);
@@ -36,26 +36,22 @@ var __importStar = (this && this.__importStar) || (function () {
36
36
  var __importDefault = (this && this.__importDefault) || function (mod) {
37
37
  return (mod && mod.__esModule) ? mod : { "default": mod };
38
38
  };
39
- var _a, _b;
40
39
  Object.defineProperty(exports, "__esModule", { value: true });
41
40
  const task = __importStar(require("../tasks/upgrade-pcf"));
42
41
  const package_json_1 = require("../package.json");
43
42
  const color_logger_1 = __importDefault(require("@tywalk/color-logger"));
44
- const argumentUtil_1 = require("../util/argumentUtil");
45
- const [, , ...args] = process.argv;
46
- const commandArgument = (_b = (_a = args.at(0)) === null || _a === void 0 ? void 0 : _a.toLowerCase()) !== null && _b !== void 0 ? _b : '';
47
- if (['-v', '--version'].includes(commandArgument)) {
48
- console.log('v%s', package_json_1.version);
49
- process.exit(0);
50
- }
51
- const verboseArgument = args.find(a => ['-v', '--verbose'].includes(a));
52
- if (typeof verboseArgument !== 'undefined') {
43
+ const commander_1 = require("commander");
44
+ const program = new commander_1.Command();
45
+ program
46
+ .name('pcf-helper-upgrade')
47
+ .description('Upgrade PCF controls')
48
+ .version(package_json_1.version, '-v, --version')
49
+ .option('-V, --verbose', 'enable verbose logging')
50
+ .requiredOption('-p, --path <path>', 'path to solution folder')
51
+ .parse();
52
+ const options = program.opts();
53
+ if (options.verbose) {
53
54
  color_logger_1.default.setDebug(true);
54
55
  }
55
56
  color_logger_1.default.log('PCF Helper version', package_json_1.version);
56
- const path = (0, argumentUtil_1.getArgValue)(args, ['-p', '--path']);
57
- if (typeof path === 'undefined') {
58
- color_logger_1.default.error('Path argument is required. Use --path to specify the path to solution folder.');
59
- process.exit(1);
60
- }
61
- task.runUpgrade(path, verboseArgument !== undefined);
57
+ task.runUpgrade(options.path, options.verbose || false);
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tywalk/pcf-helper",
3
- "version": "1.4.23",
3
+ "version": "1.5.0",
4
4
  "description": "Command line helper for building and publishing PCF controls to Dataverse.",
5
5
  "main": "dist/index.js",
6
6
  "types": "./types/",
@@ -26,7 +26,8 @@
26
26
  "pcf-helper-build": "dist/bin/build.js",
27
27
  "pcf-helper-import": "dist/bin/import.js",
28
28
  "pcf-helper-deploy": "dist/bin/deploy.js",
29
- "pcf-helper-init": "dist/bin/init.js"
29
+ "pcf-helper-init": "dist/bin/init.js",
30
+ "pcf-helper-session": "dist/bin/session.js"
30
31
  },
31
32
  "devDependencies": {
32
33
  "@types/jest": "^29.5.14",
@@ -36,6 +37,8 @@
36
37
  "typescript": "^5.8.2"
37
38
  },
38
39
  "dependencies": {
39
- "@tywalk/color-logger": "^1.0.3"
40
+ "@tywalk/color-logger": "^1.0.3",
41
+ "commander": "^14.0.3",
42
+ "playwright": "^1.58.2"
40
43
  }
41
44
  }
@@ -18,3 +18,4 @@ __exportStar(require("./build-pcf"), exports);
18
18
  __exportStar(require("./import-pcf"), exports);
19
19
  __exportStar(require("./init-pcf"), exports);
20
20
  __exportStar(require("./upgrade-pcf"), exports);
21
+ __exportStar(require("./session-pcf"), exports);
@@ -0,0 +1,236 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.runSession = runSession;
16
+ exports.loadConfig = loadConfig;
17
+ const color_logger_1 = __importDefault(require("@tywalk/color-logger"));
18
+ const path_1 = __importDefault(require("path"));
19
+ const fs_1 = __importDefault(require("fs"));
20
+ const playwright_1 = require("playwright");
21
+ function loadConfig(config) {
22
+ // Load file config if exists
23
+ let fileConfig = {};
24
+ const configPath = path_1.default.join(__dirname, config || 'dev-config.json');
25
+ console.log(`📁 Looking for config file at: ${configPath}`);
26
+ if (fs_1.default.existsSync(configPath)) {
27
+ fileConfig = JSON.parse(fs_1.default.readFileSync(configPath, 'utf8'));
28
+ console.log(`✅ Loaded config file: ${JSON.stringify(fileConfig, null, 2)}`);
29
+ }
30
+ else {
31
+ console.log(`⚠️ Config file not found, using defaults or CLI/env options.`);
32
+ }
33
+ // Get the base URL first
34
+ const remoteEnvironmentUrl = process.env.REMOTE_ENVIRONMENT_URL ||
35
+ fileConfig.remoteEnvironmentUrl ||
36
+ 'https://app.your-remote-environment.com';
37
+ // Handle script argument - support both relative paths and full URLs
38
+ let remoteScriptToIntercept = process.env.REMOTE_SCRIPT_TO_INTERCEPT ||
39
+ fileConfig.remoteScriptToIntercept ||
40
+ 'https://app.your-remote-environment.com/static/js/remote-control-bundle.js';
41
+ // If script is a relative path (doesn't start with http/https), combine with base URL
42
+ if (remoteScriptToIntercept && !remoteScriptToIntercept.startsWith('http')) {
43
+ // Normalize the base URL (remove trailing slash)
44
+ const baseUrl = remoteEnvironmentUrl.replace(/\/$/, '');
45
+ // Normalize the script path (ensure it starts with /)
46
+ const scriptPath = remoteScriptToIntercept.startsWith('/')
47
+ ? remoteScriptToIntercept
48
+ : '/' + remoteScriptToIntercept;
49
+ remoteScriptToIntercept = `${baseUrl}${scriptPath}`;
50
+ }
51
+ let remoteStylesheetToIntercept = process.env.REMOTE_STYLESHEET_TO_INTERCEPT ||
52
+ fileConfig.remoteStylesheetToIntercept ||
53
+ 'https://app.your-remote-environment.com/static/css/remote-control-styles.css';
54
+ // If stylesheet is a relative path (doesn't start with http/https), combine with base URL
55
+ if (remoteStylesheetToIntercept && !remoteStylesheetToIntercept.startsWith('http')) {
56
+ // Normalize the base URL (remove trailing slash)
57
+ const baseUrl = remoteEnvironmentUrl.replace(/\/$/, '');
58
+ // Normalize the stylesheet path (ensure it starts with /)
59
+ const stylesheetPath = remoteStylesheetToIntercept.startsWith('/')
60
+ ? remoteStylesheetToIntercept
61
+ : '/' + remoteStylesheetToIntercept;
62
+ remoteStylesheetToIntercept = `${baseUrl}${stylesheetPath}`;
63
+ }
64
+ // Priority: CLI args > env vars > config file > defaults
65
+ return {
66
+ remoteEnvironmentUrl: remoteEnvironmentUrl,
67
+ remoteScriptToIntercept: remoteScriptToIntercept,
68
+ remoteStylesheetToIntercept: remoteStylesheetToIntercept,
69
+ localCssPath: process.env.LOCAL_CSS_PATH ||
70
+ fileConfig.localCssPath ||
71
+ path_1.default.join(__dirname, 'dist', 'local-control-styles.css'),
72
+ localBundlePath: process.env.LOCAL_BUNDLE_PATH ||
73
+ fileConfig.localBundlePath ||
74
+ path_1.default.join(__dirname, 'dist', 'local-control-bundle.js')
75
+ };
76
+ }
77
+ function runSession(remoteEnvironmentUrl, remoteScriptToIntercept, remoteStylesheetToIntercept, localBundlePath, localCssPath) {
78
+ const REMOTE_ENVIRONMENT_URL = remoteEnvironmentUrl;
79
+ const REMOTE_SCRIPT_TO_INTERCEPT = remoteScriptToIntercept;
80
+ const REMOTE_STYLESHEET_TO_INTERCEPT = remoteStylesheetToIntercept;
81
+ const LOCAL_BUNDLE_PATH = path_1.default.resolve(localBundlePath);
82
+ const LOCAL_CSS_PATH = path_1.default.resolve(localCssPath);
83
+ // Debug logging for URL construction
84
+ color_logger_1.default.debug('🔍 Debug - Final URLs:');
85
+ color_logger_1.default.debug(` Remote Environment: ${REMOTE_ENVIRONMENT_URL}`);
86
+ color_logger_1.default.debug(` Script to intercept: ${REMOTE_SCRIPT_TO_INTERCEPT}`);
87
+ color_logger_1.default.debug(` CSS to intercept: ${REMOTE_STYLESHEET_TO_INTERCEPT}`);
88
+ color_logger_1.default.debug(` Local bundle path: ${LOCAL_BUNDLE_PATH}`);
89
+ color_logger_1.default.debug(` Local CSS path: ${LOCAL_CSS_PATH}`);
90
+ color_logger_1.default.debug('');
91
+ // Path to store your session cookies
92
+ const AUTH_DIR = path_1.default.join(__dirname, '.auth');
93
+ const STATE_FILE = path_1.default.join(AUTH_DIR, 'state.json');
94
+ (() => __awaiter(this, void 0, void 0, function* () {
95
+ color_logger_1.default.log('🚀 Starting ephemeral browser session...');
96
+ // 1. Prepare context options (load session if it exists)
97
+ let contextOptions = {};
98
+ if (fs_1.default.existsSync(STATE_FILE)) {
99
+ color_logger_1.default.log('🔓 Loading previous login session...');
100
+ contextOptions.storageState = STATE_FILE;
101
+ }
102
+ else {
103
+ color_logger_1.default.log('⚠️ No previous session found. You may need to log in.');
104
+ }
105
+ // 2. Launch browser and apply context
106
+ const browser = yield playwright_1.chromium.launch({ headless: false });
107
+ const context = yield browser.newContext(Object.assign(Object.assign({}, contextOptions), { viewport: null // Use the actual browser window size
108
+ }));
109
+ // Shared cleanup function to save state and close browser
110
+ const cleanup = (...args_1) => __awaiter(this, [...args_1], void 0, function* (reason = 'unknown') {
111
+ try {
112
+ color_logger_1.default.log(`💾 Saving session state (${reason})...`);
113
+ // Ensure the .auth directory exists before saving
114
+ if (!fs_1.default.existsSync(AUTH_DIR)) {
115
+ fs_1.default.mkdirSync(AUTH_DIR, { recursive: true });
116
+ }
117
+ if (!browser.isConnected()) {
118
+ color_logger_1.default.log('Browser already disconnected.');
119
+ }
120
+ else {
121
+ // Save the cookies and local storage to the JSON file
122
+ yield context.storageState({ path: STATE_FILE });
123
+ color_logger_1.default.log('🛑 Tearing down rules and session.');
124
+ yield browser.close();
125
+ }
126
+ }
127
+ catch (error) {
128
+ color_logger_1.default.error('Error during cleanup:', error);
129
+ }
130
+ });
131
+ // Handle process exit signals
132
+ process.on('SIGINT', () => __awaiter(this, void 0, void 0, function* () {
133
+ yield cleanup('SIGINT');
134
+ process.exit(0);
135
+ }));
136
+ process.on('SIGTERM', () => __awaiter(this, void 0, void 0, function* () {
137
+ yield cleanup('SIGTERM');
138
+ process.exit(0);
139
+ }));
140
+ process.on('beforeExit', () => __awaiter(this, void 0, void 0, function* () {
141
+ yield cleanup('beforeExit');
142
+ }));
143
+ // Handle uncaught exceptions
144
+ process.on('uncaughtException', (error) => __awaiter(this, void 0, void 0, function* () {
145
+ color_logger_1.default.error('Uncaught exception:', error);
146
+ yield cleanup('uncaughtException');
147
+ process.exit(1);
148
+ }));
149
+ process.on('unhandledRejection', (reason) => __awaiter(this, void 0, void 0, function* () {
150
+ color_logger_1.default.error('Unhandled promise rejection:', reason);
151
+ yield cleanup('unhandledRejection');
152
+ process.exit(1);
153
+ }));
154
+ // Handle browser disconnect
155
+ browser.on('disconnected', () => __awaiter(this, void 0, void 0, function* () {
156
+ color_logger_1.default.log('Browser disconnected');
157
+ yield cleanup('browser disconnected');
158
+ }));
159
+ // Handle context close (when all pages in context are closed)
160
+ context.on('close', () => __awaiter(this, void 0, void 0, function* () {
161
+ color_logger_1.default.log('Browser context closed');
162
+ yield cleanup('context closed');
163
+ process.exit(0);
164
+ }));
165
+ // 3. Programmatically apply your network interception rule with pattern matching
166
+ // Handle dynamic version segments in CRM URLs like /version?/webresources/...
167
+ const scriptPattern = REMOTE_SCRIPT_TO_INTERCEPT.replace(/^https?:\/\/[^\/]+/, '');
168
+ const stylesheetPattern = REMOTE_STYLESHEET_TO_INTERCEPT.replace(/^https?:\/\/[^\/]+/, '');
169
+ color_logger_1.default.debug(`📡 Setting up interception patterns:`);
170
+ color_logger_1.default.debug(` Script pattern: **${scriptPattern}`);
171
+ color_logger_1.default.debug(` CSS pattern: **${stylesheetPattern}`);
172
+ yield context.route(route => {
173
+ if (!route.href) {
174
+ return false;
175
+ }
176
+ // Match script URLs that end with the same path structure
177
+ return route.href.includes(scriptPattern) && route.href.includes('bundle.js');
178
+ }, (route) => __awaiter(this, void 0, void 0, function* () {
179
+ color_logger_1.default.log(`✅ Intercepted script request: ${route.request().url()}`);
180
+ color_logger_1.default.log(` Serving local file: ${LOCAL_BUNDLE_PATH}`);
181
+ route.fulfill({
182
+ status: 200,
183
+ contentType: 'application/javascript',
184
+ body: fs_1.default.readFileSync(LOCAL_BUNDLE_PATH)
185
+ });
186
+ }));
187
+ yield context.route(route => {
188
+ if (!route.href) {
189
+ return false;
190
+ }
191
+ // Match CSS URLs that end with the same path structure
192
+ return route.href.includes(stylesheetPattern) && route.href.includes('ItemDescriptionPCF.css');
193
+ }, (route) => __awaiter(this, void 0, void 0, function* () {
194
+ color_logger_1.default.log(`✅ Intercepted CSS request: ${route.request().url()}`);
195
+ color_logger_1.default.log(` Serving local file: ${LOCAL_CSS_PATH}`);
196
+ route.fulfill({
197
+ status: 200,
198
+ contentType: 'text/css',
199
+ body: fs_1.default.readFileSync(LOCAL_CSS_PATH)
200
+ });
201
+ }));
202
+ // 4. Open a new tab and navigate to your remote environment
203
+ const page = yield context.newPage();
204
+ yield page.goto(REMOTE_ENVIRONMENT_URL);
205
+ // 5. Clean up and save state when the page is closed (but others may still be open)
206
+ page.on('close', () => __awaiter(this, void 0, void 0, function* () {
207
+ const pages = context.pages();
208
+ if (pages.length <= 1) {
209
+ // This was the last page, trigger full cleanup
210
+ yield cleanup('last page closed');
211
+ process.exit(0);
212
+ }
213
+ else {
214
+ color_logger_1.default.debug(`Page closed, but ${pages.length - 1} pages still open. Keeping session alive.`);
215
+ }
216
+ }));
217
+ // 6. Watch the local bundle for changes and auto-reload
218
+ let reloadTimeout;
219
+ fs_1.default.watch(LOCAL_BUNDLE_PATH, (eventType) => {
220
+ if (eventType === 'change') {
221
+ // Clear the previous timer if the file changes again quickly
222
+ clearTimeout(reloadTimeout);
223
+ // Wait 300ms for the bundler to finish writing the file before reloading
224
+ reloadTimeout = setTimeout(() => __awaiter(this, void 0, void 0, function* () {
225
+ color_logger_1.default.log(`\n🔄 Local bundle updated! Reloading the page...`);
226
+ try {
227
+ yield page.reload();
228
+ }
229
+ catch (err) {
230
+ color_logger_1.default.error('⚠️ Could not reload page (browser might be closed).', err);
231
+ }
232
+ }), 300);
233
+ }
234
+ });
235
+ }))();
236
+ }
@@ -1,7 +1,17 @@
1
1
  "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
2
5
  Object.defineProperty(exports, "__esModule", { value: true });
3
6
  exports.getArg = getArg;
4
7
  exports.getArgValue = getArgValue;
8
+ exports.preprocessArgs = preprocessArgs;
9
+ exports.resolveEnvironment = resolveEnvironment;
10
+ exports.applyArgumentPreprocessing = applyArgumentPreprocessing;
11
+ exports.addCommonOptions = addCommonOptions;
12
+ exports.addPathAndEnvironmentOptions = addPathAndEnvironmentOptions;
13
+ exports.setupLogging = setupLogging;
14
+ const color_logger_1 = __importDefault(require("@tywalk/color-logger"));
5
15
  function getArg(args, arg) {
6
16
  const index = args.indexOf(arg);
7
17
  if (index !== -1 && index + 1 < args.length) {
@@ -18,3 +28,90 @@ function getArgValue(args, argOpts, defaultIfExists) {
18
28
  const argIndex = args.indexOf(arg) + 1;
19
29
  return (_a = args.at(argIndex)) !== null && _a !== void 0 ? _a : defaultIfExists;
20
30
  }
31
+ /**
32
+ * Preprocesses command line arguments to handle deprecated flags
33
+ * @param args - Raw command line arguments
34
+ * @returns Object containing processed args and deprecation flags
35
+ */
36
+ function preprocessArgs(args) {
37
+ const processed = [...args];
38
+ let hadDeprecatedEnv = false;
39
+ // Handle deprecated -env flag (single dash) by converting to --env (double dash)
40
+ for (let i = 0; i < processed.length; i++) {
41
+ if (processed[i] === '-env') {
42
+ hadDeprecatedEnv = true;
43
+ processed[i] = '--env';
44
+ }
45
+ }
46
+ return { args: processed, hadDeprecatedEnv };
47
+ }
48
+ /**
49
+ * Resolves environment value from options with proper deprecation warnings
50
+ * @param options - Parsed Commander.js options
51
+ * @param hadDeprecatedEnv - Whether the deprecated -env flag was used
52
+ * @returns Resolved environment string
53
+ */
54
+ function resolveEnvironment(options, hadDeprecatedEnv) {
55
+ // Check if deprecated --env flag was used
56
+ if (options.env && options.environment) {
57
+ color_logger_1.default.warn('⚠️ Both --env (deprecated) and --environment flags provided. Using --environment value.');
58
+ return options.environment;
59
+ }
60
+ else if (options.env) {
61
+ if (hadDeprecatedEnv) {
62
+ color_logger_1.default.warn('⚠️ The -env flag is DEPRECATED. Please use -e or --environment instead.');
63
+ }
64
+ else {
65
+ color_logger_1.default.warn('⚠️ The --env flag is DEPRECATED. Please use -e or --environment instead.');
66
+ }
67
+ return options.env;
68
+ }
69
+ else {
70
+ return options.environment || '';
71
+ }
72
+ }
73
+ /**
74
+ * Applies argument preprocessing for backward compatibility
75
+ * @param originalArgv - The original process.argv
76
+ */
77
+ function applyArgumentPreprocessing(originalArgv) {
78
+ const { args: processedArgs, hadDeprecatedEnv } = preprocessArgs(originalArgv.slice(2));
79
+ process.argv = [...originalArgv.slice(0, 2), ...processedArgs];
80
+ return { hadDeprecatedEnv };
81
+ }
82
+ /**
83
+ * Adds common CLI options to a Commander.js command
84
+ * @param command - Commander.js command instance
85
+ * @returns The command with common options added
86
+ */
87
+ function addCommonOptions(command) {
88
+ return command
89
+ .option('-V, --verbose', 'enable verbose logging')
90
+ .option('-t, --timeout <milliseconds>', 'timeout in milliseconds', (value) => {
91
+ const num = Number(value);
92
+ if (isNaN(num) || num <= 0) {
93
+ throw new Error('Timeout must be a positive number');
94
+ }
95
+ return value;
96
+ });
97
+ }
98
+ /**
99
+ * Adds path and environment options to a Commander.js command
100
+ * @param command - Commander.js command instance
101
+ * @returns The command with path and environment options added
102
+ */
103
+ function addPathAndEnvironmentOptions(command) {
104
+ return addCommonOptions(command)
105
+ .requiredOption('-p, --path <path>', 'path to solution folder')
106
+ .option('-e, --environment <environment>', 'environment name')
107
+ .option('--env <environment>', '[DEPRECATED: use -e/--environment] environment name (deprecated)');
108
+ }
109
+ /**
110
+ * Sets up logging based on verbose option
111
+ * @param verbose - Whether verbose logging is enabled
112
+ */
113
+ function setupLogging(verbose) {
114
+ if (verbose) {
115
+ color_logger_1.default.setDebug(true);
116
+ }
117
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tywalk/pcf-helper",
3
- "version": "1.4.24",
3
+ "version": "1.5.0",
4
4
  "description": "Command line helper for building and publishing PCF controls to Dataverse.",
5
5
  "main": "dist/index.js",
6
6
  "types": "./types/",
@@ -26,7 +26,8 @@
26
26
  "pcf-helper-build": "dist/bin/build.js",
27
27
  "pcf-helper-import": "dist/bin/import.js",
28
28
  "pcf-helper-deploy": "dist/bin/deploy.js",
29
- "pcf-helper-init": "dist/bin/init.js"
29
+ "pcf-helper-init": "dist/bin/init.js",
30
+ "pcf-helper-session": "dist/bin/session.js"
30
31
  },
31
32
  "devDependencies": {
32
33
  "@types/jest": "^29.5.14",
@@ -36,6 +37,8 @@
36
37
  "typescript": "^5.8.2"
37
38
  },
38
39
  "dependencies": {
39
- "@tywalk/color-logger": "^1.0.3"
40
+ "@tywalk/color-logger": "^1.0.3",
41
+ "commander": "^14.0.3",
42
+ "playwright": "^1.58.2"
40
43
  }
41
44
  }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
@@ -2,3 +2,4 @@ export * from './build-pcf';
2
2
  export * from './import-pcf';
3
3
  export * from './init-pcf';
4
4
  export * from './upgrade-pcf';
5
+ export * from './session-pcf';
@@ -0,0 +1,9 @@
1
+ declare function loadConfig(config?: string): {
2
+ remoteEnvironmentUrl: any;
3
+ remoteScriptToIntercept: any;
4
+ remoteStylesheetToIntercept: any;
5
+ localCssPath: any;
6
+ localBundlePath: any;
7
+ };
8
+ declare function runSession(remoteEnvironmentUrl: string, remoteScriptToIntercept: string, remoteStylesheetToIntercept: string, localBundlePath: string, localCssPath: string): void;
9
+ export { runSession, loadConfig };
@@ -1,2 +1,46 @@
1
+ import { Command } from 'commander';
1
2
  export declare function getArg(args: string[], arg: string): string | undefined;
2
3
  export declare function getArgValue(args: string[], argOpts: string[], defaultIfExists?: any): string | undefined;
4
+ /**
5
+ * Preprocesses command line arguments to handle deprecated flags
6
+ * @param args - Raw command line arguments
7
+ * @returns Object containing processed args and deprecation flags
8
+ */
9
+ export declare function preprocessArgs(args: string[]): {
10
+ args: string[];
11
+ hadDeprecatedEnv: boolean;
12
+ };
13
+ /**
14
+ * Resolves environment value from options with proper deprecation warnings
15
+ * @param options - Parsed Commander.js options
16
+ * @param hadDeprecatedEnv - Whether the deprecated -env flag was used
17
+ * @returns Resolved environment string
18
+ */
19
+ export declare function resolveEnvironment(options: {
20
+ env?: string;
21
+ environment?: string;
22
+ }, hadDeprecatedEnv: boolean): string;
23
+ /**
24
+ * Applies argument preprocessing for backward compatibility
25
+ * @param originalArgv - The original process.argv
26
+ */
27
+ export declare function applyArgumentPreprocessing(originalArgv: string[]): {
28
+ hadDeprecatedEnv: boolean;
29
+ };
30
+ /**
31
+ * Adds common CLI options to a Commander.js command
32
+ * @param command - Commander.js command instance
33
+ * @returns The command with common options added
34
+ */
35
+ export declare function addCommonOptions(command: Command): Command;
36
+ /**
37
+ * Adds path and environment options to a Commander.js command
38
+ * @param command - Commander.js command instance
39
+ * @returns The command with path and environment options added
40
+ */
41
+ export declare function addPathAndEnvironmentOptions(command: Command): Command;
42
+ /**
43
+ * Sets up logging based on verbose option
44
+ * @param verbose - Whether verbose logging is enabled
45
+ */
46
+ export declare function setupLogging(verbose?: boolean): void;