@mysf/plugin-sf-data-export 1.0.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.
@@ -0,0 +1,67 @@
1
+ import { simpleGit } from 'simple-git';
2
+ import { SfCommand, Flags } from '@salesforce/sf-plugins-core';
3
+ import { Messages } from '@salesforce/core';
4
+ Messages.importMessagesDirectoryFromMetaUrl(import.meta.url);
5
+ const messages = Messages.loadMessages('@mysf/plugin-sf-data-export', 'export');
6
+ export class GitDifferencePlugin extends SfCommand {
7
+ // 1. Inherit default flags (json, loglevel, etc.) by extending SfCommand
8
+ static summary = messages.getMessage('gitDiffSummary');
9
+ static examples = messages.getMessages('examples');
10
+ // Custom flags stay here
11
+ static flags = {
12
+ 'base-sha': Flags.string({
13
+ char: 'b',
14
+ summary: messages.getMessage('flags.base-sha.summary'),
15
+ default: 'HEAD~1',
16
+ }),
17
+ };
18
+ // Explicit accessibility modifier (private) for ESLint
19
+ git;
20
+ // Explicit accessibility modifier (public) for ESLint
21
+ // You must include these arguments to satisfy the SfCommand signature
22
+ constructor(argv, config) {
23
+ super(argv, config); // The required super call
24
+ this.git = simpleGit();
25
+ }
26
+ /*
27
+ * Detects filenames changed between two points in Git history.
28
+ * @param baseSha The starting commit (defaults to previous commit)
29
+ * @returns Array of modified filenames
30
+ */
31
+ async getChangedFiles(baseSha = 'HEAD~1') {
32
+ try {
33
+ const isRepo = await this.git.checkIsRepo();
34
+ if (!isRepo) {
35
+ throw new Error('Current directory is not a git repository');
36
+ }
37
+ // --name-only returns just the paths of the files
38
+ const diffString = await this.git.diff(['--name-only', baseSha, 'HEAD']);
39
+ // Filter out empty strings from the resulting array
40
+ return diffString.split('\n').filter(Boolean);
41
+ }
42
+ catch (err) {
43
+ const message = err instanceof Error ? err.message : String(err);
44
+ throw new Error(`Git Detection Failed: ${message}`);
45
+ }
46
+ }
47
+ async run() {
48
+ const { flags } = await this.parse(GitDifferencePlugin);
49
+ this.spinner.start('Analyzing git diff');
50
+ try {
51
+ const changedFiles = await this.getChangedFiles(flags['base-sha']);
52
+ this.spinner.stop();
53
+ if (changedFiles.length === 0) {
54
+ this.log('No files changed.');
55
+ }
56
+ else {
57
+ this.log('Changed files:');
58
+ changedFiles.forEach((f) => this.log(` - ${f}`));
59
+ }
60
+ }
61
+ catch (err) {
62
+ this.spinner.stop('Error');
63
+ this.error(err instanceof Error ? err.message : String(err));
64
+ }
65
+ }
66
+ }
67
+ //# sourceMappingURL=gitDifferencePlugin.js.map
@@ -0,0 +1,59 @@
1
+ import * as fs from 'node:fs';
2
+ import * as path from 'node:path';
3
+ import { SfCommand, Flags } from '@salesforce/sf-plugins-core';
4
+ import { Messages } from '@salesforce/core';
5
+ // Load messages for help text
6
+ Messages.importMessagesDirectoryFromMetaUrl(import.meta.url);
7
+ const messages = Messages.loadMessages('@mysf/plugin-sf-data-export', 'export');
8
+ export default class RestExport extends SfCommand {
9
+ // Enables the use of --target-org
10
+ static requiresOrg = true;
11
+ static summary = messages.getMessage('restExportSummary');
12
+ static examples = messages.getMessages('restExportExamples');
13
+ static flags = {
14
+ 'target-org': Flags.requiredOrg({
15
+ char: 'o',
16
+ summary: messages.getMessage('flags.target-org.summary'),
17
+ required: true,
18
+ }),
19
+ output: Flags.string({
20
+ char: 'f',
21
+ summary: messages.getMessage('flags.output.summary'),
22
+ default: './data/restExport.json',
23
+ }),
24
+ };
25
+ async run() {
26
+ const { flags } = await this.parse(RestExport);
27
+ // EXPLICIT ACCESS: Access the org via the parsed flags
28
+ // This is often more stable in the latest SF CLI versions
29
+ const org = flags['target-org'];
30
+ if (!org) {
31
+ throw new Error('Could not find the target org.');
32
+ }
33
+ const conn = org.getConnection('60.0');
34
+ this.spinner.start('Calling Apex REST Export Resource');
35
+ try {
36
+ // 1. Hit the custom endpoint you created in Org A
37
+ const result = await conn.request({
38
+ method: 'GET',
39
+ url: '/services/apexrest/DataExport/',
40
+ });
41
+ // 2. Prepare the directory
42
+ const filePath = path.resolve(flags['output']);
43
+ const directory = path.dirname(filePath);
44
+ if (!fs.existsSync(directory)) {
45
+ fs.mkdirSync(directory, { recursive: true });
46
+ }
47
+ // 3. Write the JSON data to disk
48
+ fs.writeFileSync(filePath, JSON.stringify(result, null, 2), 'utf8');
49
+ this.spinner.stop('Done');
50
+ this.log(`\nSUCCESS: Data written to ${filePath}`);
51
+ }
52
+ catch (err) {
53
+ this.spinner.stop('Failed');
54
+ const errorMessage = err instanceof Error ? err.message : String(err);
55
+ this.error(`REST Call Failed: ${errorMessage}`);
56
+ }
57
+ }
58
+ }
59
+ //# sourceMappingURL=restExport.js.map
@@ -0,0 +1,54 @@
1
+ import * as fs from 'node:fs';
2
+ import * as path from 'node:path';
3
+ import { SfCommand, Flags } from '@salesforce/sf-plugins-core';
4
+ import { Messages } from '@salesforce/core';
5
+ // Load messages for help text
6
+ Messages.importMessagesDirectoryFromMetaUrl(import.meta.url);
7
+ const messages = Messages.loadMessages('@mysf/plugin-sf-data-export', 'export');
8
+ export default class RestImport extends SfCommand {
9
+ // Enables the use of --target-org
10
+ static requiresOrg = true;
11
+ static summary = messages.getMessage('restImportSummary');
12
+ static examples = messages.getMessages('restImportExamples');
13
+ static flags = {
14
+ 'target-org': Flags.requiredOrg({
15
+ char: 'o',
16
+ summary: messages.getMessage('flags.target-org.summary'),
17
+ required: true,
18
+ }),
19
+ 'input-file': Flags.string({
20
+ char: 'f',
21
+ summary: messages.getMessage('flags.input-file.summary'),
22
+ default: './data/restExport.json',
23
+ }),
24
+ };
25
+ async run() {
26
+ try {
27
+ const { flags } = await this.parse(RestImport);
28
+ const org = flags['target-org'];
29
+ if (!org) {
30
+ throw new Error('Could not find the target org.');
31
+ }
32
+ const conn = org.getConnection('60.0');
33
+ // 1. Read the JSON file from your local data folder
34
+ const data = fs.readFileSync(path.resolve(flags['input-file']), 'utf8');
35
+ // 2. POST the data to Org B's Apex REST endpoint
36
+ const result = await conn.request({
37
+ method: 'POST',
38
+ url: '/services/apexrest/DataImport/',
39
+ body: data,
40
+ headers: {
41
+ 'Content-Type': 'application/json',
42
+ Accept: 'text/plain',
43
+ },
44
+ });
45
+ this.log('Data successfully pushed to Org B');
46
+ this.log(`Response from Salesforce: ${result}`);
47
+ // this.styledJSON(result);
48
+ }
49
+ catch (err) {
50
+ this.error(err instanceof Error ? err.message : String(err));
51
+ }
52
+ }
53
+ }
54
+ //# sourceMappingURL=restImport.js.map
package/lib/index.js ADDED
@@ -0,0 +1,17 @@
1
+ /*
2
+ * Copyright 2026, Salesforce, Inc.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+ export default {};
17
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,37 @@
1
+ {
2
+ "summary": "Export data from an org in JSON format.",
3
+ "bulkSummary": "Export all fields from a specific object.",
4
+ "objectCompareSummary": "Compare fields of an object between two orgs.",
5
+ "callApexClassSummary": "Calling a specific method of the Apex class present in the target org.",
6
+ "gitDiffSummary": "Detects changed files using Git",
7
+ "restExportSummary": "Exports data from Org A via Apex REST for Git versioning.",
8
+ "restImportSummary": "Import data to the Org B via Apex REST for Git versioning.",
9
+
10
+ "examples": [
11
+ "sf data export --soql \"SELECT Name FROM Account\" --target-org my-org",
12
+ "sf data export --soql \"SELECT Id, Status FROM Case\" --output results.json"
13
+ ],
14
+
15
+ "bulkExamples": [
16
+ "sf data bulk --object Account --target-org my-org",
17
+ "sf data bulk --object Contact --target-org my-org"
18
+ ],
19
+
20
+ "compareExamples": ["sf data compare --object Account --source-org \"DevOrg\" --target-org \"ProdOrg\""],
21
+
22
+ "restExportExamples": [
23
+ "sf data restExport --target-org OrgA",
24
+ "sf data restExport --target-org OrgA --output ./custom/path.json"
25
+ ],
26
+
27
+ "restImportExamples": ["sf data restImport --target-org OrgB --input-file ./data/contact.json"],
28
+
29
+ "flags.base-sha.summary": "The commit SHA to compare against",
30
+ "flags.object.summary": "The API name of the object.",
31
+ "flags.soql.summary": "The SOQL query to execute.",
32
+ "flags.output.summary": "The file path to save the results.",
33
+ "flags.input-file.summary": "The file path to be taken as an input for the import.",
34
+ "flags.source-org.summary": "The first org (Source)",
35
+ "flags.target-org.summary": "The second org (Target)",
36
+ "success": "Successfully exported %s records to %s."
37
+ }