@openfn/cli 1.15.2 → 1.16.1

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/index.js CHANGED
@@ -134,6 +134,29 @@ var ensureLogOpts = (opts2) => {
134
134
  };
135
135
  var ensure_log_opts_default = ensureLogOpts;
136
136
 
137
+ // src/util/get-cli-option-object.ts
138
+ function getCLIOptionObject(arg) {
139
+ if (isObject(arg)) {
140
+ return arg;
141
+ } else if (typeof arg === "string") {
142
+ try {
143
+ const p = JSON.parse(arg);
144
+ if (isObject(p))
145
+ return p;
146
+ } catch (e) {
147
+ }
148
+ return Object.fromEntries(
149
+ arg.split(",").map((pair) => {
150
+ const [k, v] = pair.split("=");
151
+ return [k.trim(), v.trim()];
152
+ })
153
+ );
154
+ }
155
+ }
156
+ function isObject(arg) {
157
+ return typeof arg === "object" && arg !== null && !Array.isArray(arg);
158
+ }
159
+
137
160
  // src/options.ts
138
161
  var setDefaultValue = (opts2, key2, value2) => {
139
162
  const v = opts2[key2];
@@ -543,6 +566,21 @@ var workflow = {
543
566
  description: "Name of the workflow to execute"
544
567
  }
545
568
  };
569
+ var removeUnmapped = {
570
+ name: "remove-unmapped",
571
+ yargs: {
572
+ boolean: true,
573
+ description: "Removes all workflows that didn't get mapped from the final project after merge"
574
+ }
575
+ };
576
+ var workflowMappings = {
577
+ name: "workflow-mappings",
578
+ yargs: {
579
+ type: "string",
580
+ coerce: getCLIOptionObject,
581
+ description: "A manual object mapping of which workflows in source and target should be matched for a merge."
582
+ }
583
+ };
546
584
 
547
585
  // src/util/command-builders.ts
548
586
  import c from "chalk";
@@ -1083,9 +1121,24 @@ var checkoutCommand = {
1083
1121
  };
1084
1122
  var command_default12 = checkoutCommand;
1085
1123
 
1124
+ // src/merge/command.ts
1125
+ var options11 = [
1126
+ projectName,
1127
+ projectPath,
1128
+ removeUnmapped,
1129
+ workflowMappings
1130
+ ];
1131
+ var mergeCommand = {
1132
+ command: "merge [project-name]",
1133
+ describe: "Merges the specified project into the checked out project",
1134
+ handler: ensure("merge", options11),
1135
+ builder: (yargs2) => build(options11, yargs2)
1136
+ };
1137
+ var command_default13 = mergeCommand;
1138
+
1086
1139
  // src/cli.ts
1087
1140
  var y = yargs(hideBin(process.argv));
1088
- var cmd = y.command(command_default7).command(command_default3).command(command_default2).command(command_default4).command(install).command(repo).command(command_default10).command(command_default6).command(command_default).command(command_default8).command(command_default5).command(command_default9).command(command_default11).command(command_default12).command({
1141
+ var cmd = y.command(command_default7).command(command_default3).command(command_default2).command(command_default4).command(install).command(repo).command(command_default10).command(command_default6).command(command_default).command(command_default8).command(command_default5).command(command_default9).command(command_default11).command(command_default12).command(command_default13).command({
1089
1142
  command: "version",
1090
1143
  describe: "Show the currently installed version of the CLI, compiler and runtime.",
1091
1144
  handler: (argv) => {
@@ -127,14 +127,14 @@ var callApollo = async (apolloBaseUrl, serviceName, payload, logger) => {
127
127
  });
128
128
  });
129
129
  };
130
- var loadPayload = async (logger, path14) => {
131
- if (!path14) {
130
+ var loadPayload = async (logger, path15) => {
131
+ if (!path15) {
132
132
  logger.warn("No JSON payload provided");
133
133
  logger.warn("Most apollo services require JSON to be uploaded");
134
134
  return {};
135
135
  }
136
- if (path14.endsWith(".json")) {
137
- const str = await readFile(path14, "utf8");
136
+ if (path15.endsWith(".json")) {
137
+ const str = await readFile(path15, "utf8");
138
138
  const json = JSON.parse(str);
139
139
  logger.debug("Loaded JSON payload");
140
140
  return json;
@@ -256,13 +256,13 @@ var execute_default = async (plan, input, opts, logger) => {
256
256
  };
257
257
  function parseAdaptors(plan) {
258
258
  const extractInfo = (specifier) => {
259
- const [module, path14] = specifier.split("=");
259
+ const [module, path15] = specifier.split("=");
260
260
  const { name, version } = getNameAndVersion(module);
261
261
  const info = {
262
262
  name
263
263
  };
264
- if (path14) {
265
- info.path = path14;
264
+ if (path15) {
265
+ info.path = path15;
266
266
  }
267
267
  if (version) {
268
268
  info.version = version;
@@ -522,10 +522,10 @@ var stripVersionSpecifier = (specifier) => {
522
522
  return specifier;
523
523
  };
524
524
  var resolveSpecifierPath = async (pattern, repoDir, log) => {
525
- const [specifier, path14] = pattern.split("=");
526
- if (path14) {
527
- log.debug(`Resolved ${specifier} to path: ${path14}`);
528
- return path14;
525
+ const [specifier, path15] = pattern.split("=");
526
+ if (path15) {
527
+ log.debug(`Resolved ${specifier} to path: ${path15}`);
528
+ return path15;
529
529
  }
530
530
  const repoPath = await getModulePath(specifier, repoDir, log);
531
531
  if (repoPath) {
@@ -544,12 +544,12 @@ var loadTransformOptions = async (opts, log) => {
544
544
  let exports;
545
545
  const [specifier] = adaptorInput.split("=");
546
546
  log.debug(`Trying to preload types for ${specifier}`);
547
- const path14 = await resolveSpecifierPath(adaptorInput, opts.repoDir, log);
548
- if (path14) {
547
+ const path15 = await resolveSpecifierPath(adaptorInput, opts.repoDir, log);
548
+ if (path15) {
549
549
  try {
550
- exports = await preloadAdaptorExports(path14, log);
550
+ exports = await preloadAdaptorExports(path15, log);
551
551
  } catch (e) {
552
- log.error(`Failed to load adaptor typedefs from path ${path14}`);
552
+ log.error(`Failed to load adaptor typedefs from path ${path15}`);
553
553
  log.error(e);
554
554
  }
555
555
  }
@@ -1001,8 +1001,8 @@ var loadXPlan = async (plan, options, logger, defaultName = "") => {
1001
1001
  };
1002
1002
 
1003
1003
  // src/util/assert-path.ts
1004
- var assert_path_default = (path14) => {
1005
- if (!path14) {
1004
+ var assert_path_default = (path15) => {
1005
+ if (!path15) {
1006
1006
  console.error("ERROR: no path provided!");
1007
1007
  console.error("\nUsage:");
1008
1008
  console.error(" open path/to/job");
@@ -1688,20 +1688,20 @@ var RETRY_COUNT = 20;
1688
1688
  var TIMEOUT_MS = 1e3 * 60;
1689
1689
  var actualDocGen = (specifier) => describePackage(specifier, {});
1690
1690
  var ensurePath = (filePath) => mkdirSync(path7.dirname(filePath), { recursive: true });
1691
- var generatePlaceholder = (path14) => {
1692
- writeFileSync(path14, `{ "loading": true, "timestamp": ${Date.now()}}`);
1691
+ var generatePlaceholder = (path15) => {
1692
+ writeFileSync(path15, `{ "loading": true, "timestamp": ${Date.now()}}`);
1693
1693
  };
1694
1694
  var finish = (logger, resultPath) => {
1695
1695
  logger.success("Done! Docs can be found at:\n");
1696
1696
  logger.print(` ${path7.resolve(resultPath)}`);
1697
1697
  };
1698
- var generateDocs = async (specifier, path14, docgen, logger) => {
1698
+ var generateDocs = async (specifier, path15, docgen, logger) => {
1699
1699
  const result = await docgen(specifier);
1700
- await writeFile5(path14, JSON.stringify(result, null, 2));
1701
- finish(logger, path14);
1702
- return path14;
1700
+ await writeFile5(path15, JSON.stringify(result, null, 2));
1701
+ finish(logger, path15);
1702
+ return path15;
1703
1703
  };
1704
- var waitForDocs = async (docs, path14, logger, retryDuration = RETRY_DURATION) => {
1704
+ var waitForDocs = async (docs, path15, logger, retryDuration = RETRY_DURATION) => {
1705
1705
  try {
1706
1706
  if (docs.hasOwnProperty("loading")) {
1707
1707
  logger.info("Docs are being loaded by another process. Waiting.");
@@ -1713,19 +1713,19 @@ var waitForDocs = async (docs, path14, logger, retryDuration = RETRY_DURATION) =
1713
1713
  clearInterval(i);
1714
1714
  reject(new Error("Timed out waiting for docs to load"));
1715
1715
  }
1716
- const updated = JSON.parse(readFileSync(path14, "utf8"));
1716
+ const updated = JSON.parse(readFileSync(path15, "utf8"));
1717
1717
  if (!updated.hasOwnProperty("loading")) {
1718
1718
  logger.info("Docs found!");
1719
1719
  clearInterval(i);
1720
- resolve(path14);
1720
+ resolve(path15);
1721
1721
  }
1722
1722
  count++;
1723
1723
  }, retryDuration);
1724
1724
  });
1725
1725
  } else {
1726
- logger.info(`Docs already written to cache at ${path14}`);
1727
- finish(logger, path14);
1728
- return path14;
1726
+ logger.info(`Docs already written to cache at ${path15}`);
1727
+ finish(logger, path15);
1728
+ return path15;
1729
1729
  }
1730
1730
  } catch (e) {
1731
1731
  logger.error("Existing doc JSON corrupt. Aborting");
@@ -1742,28 +1742,28 @@ var docgenHandler = (options, logger, docgen = actualDocGen, retryDuration = RET
1742
1742
  process.exit(9);
1743
1743
  }
1744
1744
  logger.success(`Generating docs for ${specifier}`);
1745
- const path14 = `${repoDir}/docs/${specifier}.json`;
1746
- ensurePath(path14);
1745
+ const path15 = `${repoDir}/docs/${specifier}.json`;
1746
+ ensurePath(path15);
1747
1747
  const handleError2 = () => {
1748
1748
  logger.info("Removing placeholder");
1749
- rmSync(path14);
1749
+ rmSync(path15);
1750
1750
  };
1751
1751
  try {
1752
- const existing = readFileSync(path14, "utf8");
1752
+ const existing = readFileSync(path15, "utf8");
1753
1753
  const json = JSON.parse(existing);
1754
1754
  if (json && json.timeout && Date.now() - json.timeout >= TIMEOUT_MS) {
1755
1755
  logger.info(`Expired placeholder found. Removing.`);
1756
- rmSync(path14);
1756
+ rmSync(path15);
1757
1757
  throw new Error("TIMEOUT");
1758
1758
  }
1759
- return waitForDocs(json, path14, logger, retryDuration);
1759
+ return waitForDocs(json, path15, logger, retryDuration);
1760
1760
  } catch (e) {
1761
1761
  if (e.message !== "TIMEOUT") {
1762
- logger.info(`Docs JSON not found at ${path14}`);
1762
+ logger.info(`Docs JSON not found at ${path15}`);
1763
1763
  }
1764
1764
  logger.debug("Generating placeholder");
1765
- generatePlaceholder(path14);
1766
- return generateDocs(specifier, path14, docgen, logger).catch((e2) => {
1765
+ generatePlaceholder(path15);
1766
+ return generateDocs(specifier, path15, docgen, logger).catch((e2) => {
1767
1767
  logger.error("Error generating documentation");
1768
1768
  logger.error(e2);
1769
1769
  handleError2();
@@ -1814,7 +1814,7 @@ var docsHandler = async (options, logger) => {
1814
1814
  logger.success(`Showing docs for ${adaptorName} v${version}`);
1815
1815
  }
1816
1816
  logger.info("Generating/loading documentation...");
1817
- const path14 = await handler_default7(
1817
+ const path15 = await handler_default7(
1818
1818
  {
1819
1819
  specifier: `${name}@${version}`,
1820
1820
  repoDir
@@ -1823,8 +1823,8 @@ var docsHandler = async (options, logger) => {
1823
1823
  createNullLogger()
1824
1824
  );
1825
1825
  let didError = false;
1826
- if (path14) {
1827
- const source = await readFile4(path14, "utf8");
1826
+ if (path15) {
1827
+ const source = await readFile4(path15, "utf8");
1828
1828
  const data = JSON.parse(source);
1829
1829
  let desc;
1830
1830
  if (operation) {
@@ -2274,7 +2274,7 @@ var handler_default10 = pullHandler;
2274
2274
  import { Workspace } from "@openfn/project";
2275
2275
  import path11 from "path";
2276
2276
  var projectsHandler = async (options, logger) => {
2277
- const commandPath = path11.resolve(process.cwd(), options.projectPath ?? ".");
2277
+ const commandPath = path11.resolve(options.projectPath ?? ".");
2278
2278
  const workspace = new Workspace(commandPath);
2279
2279
  if (!workspace.valid) {
2280
2280
  logger.error("Command was run in an invalid openfn workspace");
@@ -2300,7 +2300,7 @@ import path12 from "path";
2300
2300
  import fs6 from "fs";
2301
2301
  import { rimraf as rimraf2 } from "rimraf";
2302
2302
  var checkoutHandler = async (options, logger) => {
2303
- const commandPath = path12.resolve(process.cwd(), options.projectPath ?? ".");
2303
+ const commandPath = path12.resolve(options.projectPath ?? ".");
2304
2304
  const workspace = new Workspace2(commandPath);
2305
2305
  if (!workspace.valid) {
2306
2306
  logger.error("Command was run in an invalid openfn workspace");
@@ -2309,7 +2309,7 @@ var checkoutHandler = async (options, logger) => {
2309
2309
  const switchProject = workspace.get(options.projectName);
2310
2310
  if (!switchProject) {
2311
2311
  logger.error(
2312
- `Project with id ${options.projectName} not found in the workspace`
2312
+ `Project with id/name ${options.projectName} not found in the workspace`
2313
2313
  );
2314
2314
  return;
2315
2315
  }
@@ -2330,9 +2330,65 @@ var checkoutHandler = async (options, logger) => {
2330
2330
  };
2331
2331
  var handler_default12 = checkoutHandler;
2332
2332
 
2333
+ // src/merge/handler.ts
2334
+ import Project5, { Workspace as Workspace3 } from "@openfn/project";
2335
+ import path13 from "path";
2336
+ import { promises as fs7 } from "fs";
2337
+ var mergeHandler = async (options, logger) => {
2338
+ const commandPath = path13.resolve(options.projectPath ?? ".");
2339
+ const workspace = new Workspace3(commandPath);
2340
+ if (!workspace.valid) {
2341
+ logger.error("Command was run in an invalid openfn workspace");
2342
+ return;
2343
+ }
2344
+ const checkedProject = workspace.getActiveProject();
2345
+ if (!checkedProject) {
2346
+ logger.error(`No project currently checked out`);
2347
+ return;
2348
+ }
2349
+ const mProject = workspace.get(options.projectName);
2350
+ if (!mProject) {
2351
+ logger.error(
2352
+ `Project with id/name ${options.projectName} not found in the workspace`
2353
+ );
2354
+ return;
2355
+ }
2356
+ if (checkedProject.name === mProject.name) {
2357
+ logger.error("Merging into the same project not allowed");
2358
+ return;
2359
+ }
2360
+ if (!checkedProject.name) {
2361
+ logger.error("The checked out project has no name/id");
2362
+ return;
2363
+ }
2364
+ const finalPath = workspace.getProjectPath(checkedProject.name);
2365
+ if (!finalPath) {
2366
+ logger.error("Path to checked out project not found.");
2367
+ return;
2368
+ }
2369
+ const final = Project5.merge(mProject, checkedProject, {
2370
+ removeUnmapped: options.removeUnmapped,
2371
+ workflowMappings: options.workflowMappings
2372
+ });
2373
+ const yaml = final.serialize("state", { format: "yaml" });
2374
+ await fs7.writeFile(finalPath, yaml);
2375
+ await handler_default12(
2376
+ {
2377
+ command: "checkout",
2378
+ projectPath: commandPath,
2379
+ projectName: final.name || ""
2380
+ },
2381
+ logger
2382
+ );
2383
+ logger.success(
2384
+ `Project ${mProject.name} has been merged into Project ${checkedProject.name} successfully`
2385
+ );
2386
+ };
2387
+ var handler_default13 = mergeHandler;
2388
+
2333
2389
  // src/util/print-versions.ts
2334
2390
  import { readFileSync as readFileSync2 } from "node:fs";
2335
- import path13 from "node:path";
2391
+ import path14 from "node:path";
2336
2392
  import url from "node:url";
2337
2393
  import { getNameAndVersion as getNameAndVersion7 } from "@openfn/runtime";
2338
2394
  import { mainSymbols } from "figures";
@@ -2344,7 +2400,7 @@ var { triangleRightSmall: t } = mainSymbols;
2344
2400
  var loadVersionFromPath = (adaptorPath) => {
2345
2401
  try {
2346
2402
  const pkg = JSON.parse(
2347
- readFileSync2(path13.resolve(adaptorPath, "package.json"), "utf8")
2403
+ readFileSync2(path14.resolve(adaptorPath, "package.json"), "utf8")
2348
2404
  );
2349
2405
  return pkg.version;
2350
2406
  } catch (e) {
@@ -2379,7 +2435,7 @@ var printVersions = async (logger, options = {}, includeComponents = false) => {
2379
2435
  ...[NODE, CLI2, RUNTIME2, COMPILER2, longestAdaptorName].map((s) => s.length)
2380
2436
  );
2381
2437
  const prefix = (str) => ` ${t} ${str.padEnd(longest + 4, " ")}`;
2382
- const dirname3 = path13.dirname(url.fileURLToPath(import.meta.url));
2438
+ const dirname3 = path14.dirname(url.fileURLToPath(import.meta.url));
2383
2439
  const pkg = JSON.parse(readFileSync2(`${dirname3}/../../package.json`, "utf8"));
2384
2440
  const { version, dependencies } = pkg;
2385
2441
  const compilerVersion = dependencies["@openfn/compiler"];
@@ -2434,6 +2490,7 @@ var handlers = {
2434
2490
  pull: handler_default10,
2435
2491
  projects: handler_default11,
2436
2492
  checkout: handler_default12,
2493
+ merge: handler_default13,
2437
2494
  ["collections-get"]: handler_default4.get,
2438
2495
  ["collections-set"]: handler_default4.set,
2439
2496
  ["collections-remove"]: handler_default4.remove,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openfn/cli",
3
- "version": "1.15.2",
3
+ "version": "1.16.1",
4
4
  "description": "CLI devtools for the OpenFn toolchain",
5
5
  "engines": {
6
6
  "node": ">=18",
@@ -47,13 +47,13 @@
47
47
  "undici": "7.12.0",
48
48
  "ws": "^8.18.3",
49
49
  "yargs": "^17.7.2",
50
- "@openfn/compiler": "1.1.4",
50
+ "@openfn/compiler": "1.1.5",
51
51
  "@openfn/deploy": "0.11.3",
52
- "@openfn/logger": "1.0.6",
53
52
  "@openfn/lexicon": "^1.2.3",
54
- "@openfn/project": "^0.4.0",
53
+ "@openfn/describe-package": "0.1.5",
54
+ "@openfn/project": "^0.5.0",
55
55
  "@openfn/runtime": "1.7.3",
56
- "@openfn/describe-package": "0.1.5"
56
+ "@openfn/logger": "1.0.6"
57
57
  },
58
58
  "files": [
59
59
  "dist",