syncodemayo 1.0.8 → 2.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.
Files changed (78) hide show
  1. package/CHANGELOG.md +81 -0
  2. package/README.md +99 -30
  3. package/dist/cli.d.ts +2 -0
  4. package/dist/cli.js +66 -0
  5. package/dist/cli.js.map +1 -0
  6. package/dist/env.d.ts +1 -0
  7. package/dist/env.js +17 -0
  8. package/dist/env.js.map +1 -0
  9. package/dist/index.d.ts +1 -0
  10. package/dist/index.js +2 -0
  11. package/dist/index.js.map +1 -0
  12. package/dist/lib/Args.d.ts +21 -0
  13. package/dist/lib/Args.js +99 -0
  14. package/dist/lib/Args.js.map +1 -0
  15. package/{lib → dist/lib}/Cli.d.ts +5 -0
  16. package/dist/lib/Cli.js +104 -0
  17. package/dist/lib/Cli.js.map +1 -0
  18. package/{lib → dist/lib}/Client.d.ts +5 -3
  19. package/dist/lib/Client.js +73 -0
  20. package/dist/lib/Client.js.map +1 -0
  21. package/dist/lib/Config.d.ts +48 -0
  22. package/dist/lib/Config.js +92 -0
  23. package/dist/lib/Config.js.map +1 -0
  24. package/dist/lib/ConfigSchema.d.ts +3 -0
  25. package/dist/lib/ConfigSchema.js +132 -0
  26. package/dist/lib/ConfigSchema.js.map +1 -0
  27. package/dist/lib/Connection.d.ts +41 -0
  28. package/dist/lib/Connection.js +176 -0
  29. package/dist/lib/Connection.js.map +1 -0
  30. package/dist/lib/Filelist.d.ts +42 -0
  31. package/dist/lib/Filelist.js +156 -0
  32. package/dist/lib/Filelist.js.map +1 -0
  33. package/dist/lib/Log.d.ts +12 -0
  34. package/dist/lib/Log.js +46 -0
  35. package/dist/lib/Log.js.map +1 -0
  36. package/dist/lib/Prompt.d.ts +15 -0
  37. package/dist/lib/Prompt.js +31 -0
  38. package/dist/lib/Prompt.js.map +1 -0
  39. package/dist/lib/RemoteConnection.d.ts +37 -0
  40. package/dist/lib/RemoteConnection.js +2 -0
  41. package/dist/lib/RemoteConnection.js.map +1 -0
  42. package/dist/lib/Sync.d.ts +18 -0
  43. package/dist/lib/Sync.js +127 -0
  44. package/dist/lib/Sync.js.map +1 -0
  45. package/dist/lib/TargetServer.d.ts +31 -0
  46. package/dist/lib/TargetServer.js +76 -0
  47. package/dist/lib/TargetServer.js.map +1 -0
  48. package/dist/lib/index.d.ts +8 -0
  49. package/dist/lib/index.js +9 -0
  50. package/dist/lib/index.js.map +1 -0
  51. package/dist/vendor/strip-json-comments.d.ts +23 -0
  52. package/dist/vendor/strip-json-comments.js +126 -0
  53. package/dist/vendor/strip-json-comments.js.map +1 -0
  54. package/package.json +24 -26
  55. package/.npmignore +0 -9
  56. package/cli.d.ts +0 -0
  57. package/cli.js +0 -93
  58. package/index.d.ts +0 -1
  59. package/index.js +0 -7
  60. package/lib/Cli.js +0 -122
  61. package/lib/Client.js +0 -81
  62. package/lib/Config.d.ts +0 -32
  63. package/lib/Config.js +0 -115
  64. package/lib/ConfigSchema.d.ts +0 -7
  65. package/lib/ConfigSchema.js +0 -65
  66. package/lib/Connection.d.ts +0 -14
  67. package/lib/Connection.js +0 -111
  68. package/lib/Filelist.d.ts +0 -11
  69. package/lib/Filelist.js +0 -59
  70. package/lib/Sync.d.ts +0 -18
  71. package/lib/Sync.js +0 -141
  72. package/lib/TargetServer.d.ts +0 -22
  73. package/lib/TargetServer.js +0 -73
  74. package/lib/index.d.ts +0 -7
  75. package/lib/index.js +0 -13
  76. package/missing-types.d.ts +0 -14
  77. package/tsconfig.json +0 -48
  78. package/yarn.lock +0 -975
@@ -0,0 +1,127 @@
1
+ import * as path from 'path';
2
+ import * as Log from './Log.js';
3
+ import { resolveTarget } from './Config.js';
4
+ import { getRemoteFilelist, buildLocalFilelist, remotePathForKey } from './Filelist.js';
5
+ import { confirmUpload } from './Prompt.js';
6
+ export class Sync {
7
+ constructor(conn, config, target) {
8
+ this._conn = conn;
9
+ this._config = config;
10
+ this._target = resolveTarget(config, target);
11
+ }
12
+ async run(isDryRun = false, forceConfirmation = false) {
13
+ const { localFiles, remoteFiles } = await this._getFilelists(this._config.local, this._target, this._conn);
14
+ const changeset = this._buildChangeset(localFiles, remoteFiles);
15
+ const filesToUpload = [...changeset.added, ...changeset.changed];
16
+ if (isDryRun || (filesToUpload.length === 0 && changeset.removed.length === 0)) {
17
+ return changeset;
18
+ }
19
+ const _verifiedDirs = new Map();
20
+ const verifyDirExists = async (remoteDir) => {
21
+ if (!_verifiedDirs.has(remoteDir)) {
22
+ const isVerified = await this._conn.remoteDirectoryExists(remoteDir);
23
+ _verifiedDirs.set(remoteDir, isVerified);
24
+ }
25
+ return true;
26
+ };
27
+ Log.info("\n%s files to upload...", filesToUpload.length);
28
+ Log.info("%s files to remove...\n", changeset.removed.length);
29
+ const confirmed = forceConfirmation === true
30
+ ? true
31
+ : await this._confirmUpload(this._target, filesToUpload.length);
32
+ if (!confirmed) {
33
+ Log.info("Canceled upload.");
34
+ return null;
35
+ }
36
+ Log.log("Uploading %s files...", filesToUpload.length);
37
+ // The remote path for a cache key, or null (with a warning) when the key is outside the
38
+ // current local.path.
39
+ const remotePathFor = (filePath) => {
40
+ const remotePath = remotePathForKey(this._config.local.path, this._target.path, filePath);
41
+ if (remotePath === null) {
42
+ Log.urgent(` -> Skipping ${filePath}: it isn't under local.path (${this._config.local.path}), so its remote path is unknown.`);
43
+ }
44
+ return remotePath;
45
+ };
46
+ let sequentialUploader = Promise.resolve(true);
47
+ filesToUpload.forEach((filePath) => {
48
+ sequentialUploader = sequentialUploader.then(async () => {
49
+ const remotePath = remotePathFor(filePath);
50
+ if (remotePath === null)
51
+ return true;
52
+ const remoteDir = path.posix.dirname(remotePath);
53
+ const localPath = path.resolve(filePath);
54
+ await verifyDirExists(remoteDir);
55
+ await this._conn.putFile(localPath, remotePath);
56
+ return true;
57
+ });
58
+ });
59
+ if (this._config.local.deleteRemoteFiles && changeset.removed.length > 0) {
60
+ changeset.removed.forEach((filePath) => {
61
+ sequentialUploader = sequentialUploader.then(async () => {
62
+ const remotePath = remotePathFor(filePath);
63
+ if (remotePath === null)
64
+ return true;
65
+ const didDelete = await this._conn.deleteRemoteFile(remotePath);
66
+ if (!didDelete)
67
+ Log.urgent(" -> Error deleting file");
68
+ return true;
69
+ });
70
+ });
71
+ }
72
+ await sequentialUploader;
73
+ const remoteCachePath = `${this._target.path}/${this._target.cache}`;
74
+ await verifyDirExists(path.dirname(remoteCachePath));
75
+ await this._conn.putContent(JSON.stringify(localFiles, null, 2), remoteCachePath);
76
+ return changeset;
77
+ }
78
+ async _confirmUpload(target, fileCount, streams) {
79
+ // If no prompt is specified, auto-confirm upload!
80
+ if (!target.prompt)
81
+ return true;
82
+ const message = typeof target.prompt === 'string'
83
+ ? target.prompt
84
+ : "Confirm upload?";
85
+ return confirmUpload(message, streams);
86
+ }
87
+ async _getFilelists(local, target, conn) {
88
+ // Load/build Filesets in parallel.
89
+ const [localFiles, remoteFiles] = await Promise.all([
90
+ buildLocalFilelist(local),
91
+ getRemoteFilelist(target, conn),
92
+ ]);
93
+ return { localFiles, remoteFiles };
94
+ }
95
+ _buildChangeset(localFiles, remoteFiles) {
96
+ const changeset = {
97
+ added: [],
98
+ removed: [],
99
+ changed: []
100
+ };
101
+ // Build list of files that need to be uploaded...
102
+ Object.keys(localFiles).forEach((localFilename) => {
103
+ const localHash = localFiles[localFilename];
104
+ const remoteHash = remoteFiles[localFilename];
105
+ // Local hash not in remote cache
106
+ if (!(localFilename in remoteFiles)) {
107
+ changeset.added.push(localFilename);
108
+ }
109
+ // Local hash unequal to remote cache
110
+ else if (remoteHash != localHash) {
111
+ changeset.changed.push(localFilename);
112
+ }
113
+ // Local hash and remote hash are equal!
114
+ });
115
+ // Build list of files to be removed from the server...
116
+ Object.keys(remoteFiles).forEach((remoteFilename) => {
117
+ if (!(remoteFilename in localFiles)) {
118
+ changeset.removed.push(remoteFilename);
119
+ }
120
+ });
121
+ return changeset;
122
+ }
123
+ static create(conn, config, target) {
124
+ return new Sync(conn, config, target);
125
+ }
126
+ }
127
+ //# sourceMappingURL=Sync.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Sync.js","sourceRoot":"./","sources":["lib/Sync.ts"],"names":[],"mappings":"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","sourcesContent":["\nimport * as path from 'path'\nimport * as Log from './Log.js'\nimport { IConfig, ILocalConfig, ITargetConfig, resolveTarget } from './Config.js'\nimport { RemoteConnection } from './RemoteConnection.js'\nimport { IFilelist, getRemoteFilelist, buildLocalFilelist, remotePathForKey } from './Filelist.js'\nimport { ConfirmStreams, confirmUpload } from './Prompt.js'\n\nexport interface IChangeset {\n added: string[]\n changed: string[]\n removed: string[]\n}\n\nexport class Sync {\n private _conn: RemoteConnection\n private _config: IConfig\n private _target: ITargetConfig\n\n constructor(conn: RemoteConnection, config: IConfig, target: string | ITargetConfig) {\n this._conn = conn\n this._config = config\n this._target = resolveTarget(config, target)\n }\n\n async run(isDryRun = false, forceConfirmation = false): Promise<IChangeset | null> {\n const { localFiles, remoteFiles } = await this._getFilelists(this._config.local, this._target, this._conn)\n const changeset = this._buildChangeset(localFiles, remoteFiles)\n const filesToUpload = [...changeset.added, ...changeset.changed]\n\n if (isDryRun || (filesToUpload.length === 0 && changeset.removed.length === 0)) {\n return changeset\n }\n\n const _verifiedDirs = new Map();\n\n const verifyDirExists = async (remoteDir: string) => {\n if (!_verifiedDirs.has(remoteDir)) {\n const isVerified = await this._conn.remoteDirectoryExists(remoteDir)\n _verifiedDirs.set(remoteDir, isVerified)\n }\n return true\n }\n\n Log.info(\"\\n%s files to upload...\", filesToUpload.length)\n Log.info(\"%s files to remove...\\n\", changeset.removed.length)\n\n const confirmed = forceConfirmation === true\n ? true\n : await this._confirmUpload(this._target, filesToUpload.length)\n\n if (!confirmed) {\n Log.info(\"Canceled upload.\")\n return null\n }\n\n Log.log(\"Uploading %s files...\", filesToUpload.length)\n\n // The remote path for a cache key, or null (with a warning) when the key is outside the\n // current local.path.\n const remotePathFor = (filePath: string): string | null => {\n const remotePath = remotePathForKey(this._config.local.path, this._target.path, filePath)\n if (remotePath === null) {\n Log.urgent(` -> Skipping ${filePath}: it isn't under local.path (${this._config.local.path}), so its remote path is unknown.`)\n }\n return remotePath\n }\n\n let sequentialUploader: any = Promise.resolve(true)\n\n filesToUpload.forEach((filePath: string) => {\n sequentialUploader = sequentialUploader.then(async () => {\n const remotePath = remotePathFor(filePath)\n if (remotePath === null) return true\n const remoteDir = path.posix.dirname(remotePath)\n const localPath = path.resolve(filePath)\n\n await verifyDirExists(remoteDir)\n await this._conn.putFile(localPath, remotePath)\n return true\n })\n })\n\n if (this._config.local.deleteRemoteFiles && changeset.removed.length > 0) {\n changeset.removed.forEach((filePath: string) => {\n sequentialUploader = sequentialUploader.then(async () => {\n const remotePath = remotePathFor(filePath)\n if (remotePath === null) return true\n const didDelete = await this._conn.deleteRemoteFile(remotePath)\n if (!didDelete) Log.urgent(\" -> Error deleting file\")\n return true\n })\n })\n }\n\n await sequentialUploader\n\n const remoteCachePath = `${this._target.path}/${this._target.cache}`\n await verifyDirExists(path.dirname(remoteCachePath))\n await this._conn.putContent(\n JSON.stringify(localFiles, null, 2),\n remoteCachePath\n )\n\n return changeset\n }\n\n private async _confirmUpload(target: ITargetConfig, fileCount: number, streams?: ConfirmStreams): Promise<boolean> {\n // If no prompt is specified, auto-confirm upload!\n if (!target.prompt) return true\n\n const message = typeof target.prompt === 'string'\n ? target.prompt\n : \"Confirm upload?\"\n\n return confirmUpload(message, streams)\n }\n\n private async _getFilelists(local: ILocalConfig, target: ITargetConfig, conn: RemoteConnection) {\n // Load/build Filesets in parallel.\n const [localFiles, remoteFiles] = await Promise.all([\n buildLocalFilelist(local),\n getRemoteFilelist(target, conn),\n ])\n return { localFiles, remoteFiles }\n }\n\n private _buildChangeset(localFiles: IFilelist, remoteFiles: IFilelist) {\n const changeset: IChangeset = {\n added: [],\n removed: [],\n changed: []\n }\n\n // Build list of files that need to be uploaded...\n Object.keys(localFiles).forEach((localFilename: string) => {\n const localHash = localFiles[localFilename]\n const remoteHash = remoteFiles[localFilename]\n\n // Local hash not in remote cache\n if (!(localFilename in remoteFiles)) {\n changeset.added.push(localFilename)\n }\n // Local hash unequal to remote cache\n else if (remoteHash != localHash) {\n changeset.changed.push(localFilename)\n }\n // Local hash and remote hash are equal!\n })\n\n // Build list of files to be removed from the server...\n Object.keys(remoteFiles).forEach((remoteFilename) => {\n if (!(remoteFilename in localFiles)) {\n changeset.removed.push(remoteFilename)\n }\n })\n\n return changeset\n }\n\n static create(conn: RemoteConnection, config: IConfig, target: string | ITargetConfig) {\n return new Sync(conn, config, target);\n }\n}"]}
@@ -0,0 +1,31 @@
1
+ import { IConfig, ITargetConfig } from './Config.js';
2
+ import { ConnectionFactory } from './RemoteConnection.js';
3
+ import { IChangeset } from './Sync.js';
4
+ export declare class TargetServer {
5
+ private _config;
6
+ private _targetConfig;
7
+ private _connect;
8
+ get config(): ITargetConfig;
9
+ get name(): string;
10
+ /**
11
+ * `connect` opens the connection for each operation. It defaults to FTP/FTPS (`Connection`);
12
+ * tests pass a fake.
13
+ */
14
+ constructor(config: IConfig, target: string | ITargetConfig | undefined, connect?: ConnectionFactory);
15
+ private _withConnection;
16
+ /**
17
+ * Add a cache file, if there's not one. `target.path` must already exist: a missing path is
18
+ * most likely a typo, so this rejects (telling the user to create the folder) and creates
19
+ * nothing.
20
+ */
21
+ prepare(force?: boolean): Promise<boolean>;
22
+ /**
23
+ * Verify the server has a cache file
24
+ */
25
+ verify(): Promise<boolean>;
26
+ /**
27
+ * Sync local files to target server
28
+ */
29
+ sync(isDryRun: boolean, forceConfirmation?: boolean): Promise<IChangeset | null>;
30
+ static create(config: IConfig, targetConfig: string | ITargetConfig | undefined, connect?: ConnectionFactory): TargetServer;
31
+ }
@@ -0,0 +1,76 @@
1
+ import * as Log from './Log.js';
2
+ import { resolveTarget } from './Config.js';
3
+ import { Connection } from './Connection.js';
4
+ import { Sync } from './Sync.js';
5
+ export class TargetServer {
6
+ get config() {
7
+ return this._targetConfig;
8
+ }
9
+ get name() {
10
+ return this.config._name;
11
+ }
12
+ /**
13
+ * `connect` opens the connection for each operation. It defaults to FTP/FTPS (`Connection`);
14
+ * tests pass a fake.
15
+ */
16
+ constructor(config, target, connect = Connection.create) {
17
+ this._config = config;
18
+ this._targetConfig = resolveTarget(config, target);
19
+ this._connect = connect;
20
+ }
21
+ // Opens a connection, runs `task` with it, and closes it, whether `task` resolves or rejects.
22
+ async _withConnection(task) {
23
+ const conn = this._connect(this._targetConfig);
24
+ try {
25
+ return await task(conn);
26
+ }
27
+ finally {
28
+ try {
29
+ await conn.close();
30
+ }
31
+ catch (err) {
32
+ Log.debug("Error closing the connection:", err);
33
+ }
34
+ }
35
+ }
36
+ /**
37
+ * Add a cache file, if there's not one. `target.path` must already exist: a missing path is
38
+ * most likely a typo, so this rejects (telling the user to create the folder) and creates
39
+ * nothing.
40
+ */
41
+ async prepare(force) {
42
+ const remoteCachePath = `${this._targetConfig.path}/${this._targetConfig.cache}`;
43
+ return this._withConnection(async (conn) => {
44
+ const remoteIsConfigured = await conn.remoteFileExists(remoteCachePath);
45
+ if (remoteIsConfigured && force !== true) {
46
+ Log.info(`${this._targetConfig.host} already is initialized.`);
47
+ return false;
48
+ }
49
+ const { path, host } = this._targetConfig;
50
+ const remoteDirFound = await conn.remoteDirectoryFound(path);
51
+ if (!remoteDirFound) {
52
+ throw new Error(`Remote path "${path}" not found on ${host}. ` +
53
+ `Check the target's "path", or create the folder on the server, then run init again.`);
54
+ }
55
+ await conn.putContent("{}", remoteCachePath);
56
+ return true;
57
+ });
58
+ }
59
+ /**
60
+ * Verify the server has a cache file
61
+ */
62
+ async verify() {
63
+ const remoteCachePath = `${this._targetConfig.path}/${this._targetConfig.cache}`;
64
+ return this._withConnection(conn => conn.remoteFileExists(remoteCachePath));
65
+ }
66
+ /**
67
+ * Sync local files to target server
68
+ */
69
+ async sync(isDryRun, forceConfirmation = false) {
70
+ return this._withConnection(conn => Sync.create(conn, this._config, this._targetConfig).run(isDryRun, forceConfirmation));
71
+ }
72
+ static create(config, targetConfig, connect) {
73
+ return new TargetServer(config, targetConfig, connect);
74
+ }
75
+ }
76
+ //# sourceMappingURL=TargetServer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TargetServer.js","sourceRoot":"./","sources":["lib/TargetServer.ts"],"names":[],"mappings":"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","sourcesContent":["import * as Log from './Log.js'\nimport { IConfig, ITargetConfig, resolveTarget } from './Config.js'\nimport { Connection } from './Connection.js'\nimport { ConnectionFactory, RemoteConnection } from './RemoteConnection.js'\nimport { Sync, IChangeset } from './Sync.js'\n\nexport class TargetServer {\n private _config: IConfig\n private _targetConfig: ITargetConfig\n private _connect: ConnectionFactory\n\n get config() {\n return this._targetConfig\n }\n\n get name() {\n return this.config._name\n }\n\n /**\n * `connect` opens the connection for each operation. It defaults to FTP/FTPS (`Connection`);\n * tests pass a fake.\n */\n constructor(config: IConfig, target: string | ITargetConfig | undefined, connect: ConnectionFactory = Connection.create) {\n this._config = config\n this._targetConfig = resolveTarget(config, target)\n this._connect = connect\n }\n\n // Opens a connection, runs `task` with it, and closes it, whether `task` resolves or rejects.\n private async _withConnection<T>(task: (conn: RemoteConnection) => Promise<T>): Promise<T> {\n const conn = this._connect(this._targetConfig)\n try {\n return await task(conn)\n }\n finally {\n try {\n await conn.close()\n }\n catch (err) {\n Log.debug(\"Error closing the connection:\", err)\n }\n }\n }\n\n /**\n * Add a cache file, if there's not one. `target.path` must already exist: a missing path is\n * most likely a typo, so this rejects (telling the user to create the folder) and creates\n * nothing.\n */\n async prepare(force?: boolean): Promise<boolean> {\n const remoteCachePath = `${this._targetConfig.path}/${this._targetConfig.cache}`\n return this._withConnection(async (conn) => {\n const remoteIsConfigured = await conn.remoteFileExists(remoteCachePath)\n\n if (remoteIsConfigured && force !== true) {\n Log.info(`${this._targetConfig.host} already is initialized.`)\n return false\n }\n\n const { path, host } = this._targetConfig\n const remoteDirFound = await conn.remoteDirectoryFound(path)\n if (!remoteDirFound) {\n throw new Error(\n `Remote path \"${path}\" not found on ${host}. ` +\n `Check the target's \"path\", or create the folder on the server, then run init again.`)\n }\n\n await conn.putContent(\"{}\", remoteCachePath)\n\n return true\n })\n }\n\n /**\n * Verify the server has a cache file\n */\n async verify(): Promise<boolean> {\n const remoteCachePath = `${this._targetConfig.path}/${this._targetConfig.cache}`\n return this._withConnection(conn => conn.remoteFileExists(remoteCachePath))\n }\n\n /**\n * Sync local files to target server\n */\n async sync(isDryRun: boolean, forceConfirmation = false): Promise<IChangeset | null> {\n return this._withConnection(conn =>\n Sync.create(conn, this._config, this._targetConfig).run(isDryRun, forceConfirmation))\n }\n\n static create(config: IConfig, targetConfig: string | ITargetConfig | undefined, connect?: ConnectionFactory): TargetServer {\n return new TargetServer(config, targetConfig, connect)\n }\n}\n"]}
@@ -0,0 +1,8 @@
1
+ export * from './Client.js';
2
+ export * from './Config.js';
3
+ export * from './ConfigSchema.js';
4
+ export * from './Connection.js';
5
+ export * from './Filelist.js';
6
+ export * from './RemoteConnection.js';
7
+ export * from './Sync.js';
8
+ export * from './TargetServer.js';
@@ -0,0 +1,9 @@
1
+ export * from './Client.js';
2
+ export * from './Config.js';
3
+ export * from './ConfigSchema.js';
4
+ export * from './Connection.js';
5
+ export * from './Filelist.js';
6
+ export * from './RemoteConnection.js';
7
+ export * from './Sync.js';
8
+ export * from './TargetServer.js';
9
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"./","sources":["lib/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAA;AAC3B,cAAc,aAAa,CAAA;AAC3B,cAAc,mBAAmB,CAAA;AACjC,cAAc,iBAAiB,CAAA;AAC/B,cAAc,eAAe,CAAA;AAC7B,cAAc,uBAAuB,CAAA;AACrC,cAAc,WAAW,CAAA;AACzB,cAAc,mBAAmB,CAAA","sourcesContent":["export * from './Client.js'\nexport * from './Config.js'\nexport * from './ConfigSchema.js'\nexport * from './Connection.js'\nexport * from './Filelist.js'\nexport * from './RemoteConnection.js'\nexport * from './Sync.js'\nexport * from './TargetServer.js'"]}
@@ -0,0 +1,23 @@
1
+ export type Options = {
2
+ /**
3
+ Strip trailing commas in addition to comments.
4
+
5
+ @default false
6
+ */
7
+ readonly trailingCommas?: boolean;
8
+ /**
9
+ Replace comments and trailing commas with whitespace instead of stripping them entirely.
10
+
11
+ @default true
12
+ */
13
+ readonly whitespace?: boolean;
14
+ };
15
+ /**
16
+ Strip comments from JSON. Lets you use comments in your JSON files!
17
+
18
+ It will replace single-line comments `//` and multi-line comments `/**\/` with whitespace. This allows JSON error positions to remain as close as possible to the original source.
19
+
20
+ @param jsonString - Accepts a string with JSON.
21
+ @returns A JSON string without comments.
22
+ */
23
+ export default function stripJsonComments(jsonString: string, { whitespace, trailingCommas }?: Options): string;
@@ -0,0 +1,126 @@
1
+ /*
2
+ MIT License
3
+
4
+ Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (https://sindresorhus.com)
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
7
+
8
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
9
+
10
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
11
+ */
12
+ const singleComment = Symbol('singleComment');
13
+ const multiComment = Symbol('multiComment');
14
+ const stripWithoutWhitespace = (_string, _start, _end) => '';
15
+ // Replace all characters except ASCII spaces, tabs and line endings with regular spaces to ensure valid JSON output.
16
+ const stripWithWhitespace = (string, start, end) => string.slice(start, end).replace(/[^ \t\r\n]/g, ' ');
17
+ const isEscaped = (jsonString, quotePosition) => {
18
+ let index = quotePosition - 1;
19
+ let backslashCount = 0;
20
+ while (jsonString[index] === '\\') {
21
+ index -= 1;
22
+ backslashCount += 1;
23
+ }
24
+ return Boolean(backslashCount % 2);
25
+ };
26
+ /**
27
+ Strip comments from JSON. Lets you use comments in your JSON files!
28
+
29
+ It will replace single-line comments `//` and multi-line comments `/**\/` with whitespace. This allows JSON error positions to remain as close as possible to the original source.
30
+
31
+ @param jsonString - Accepts a string with JSON.
32
+ @returns A JSON string without comments.
33
+ */
34
+ export default function stripJsonComments(jsonString, { whitespace = true, trailingCommas = false } = {}) {
35
+ if (typeof jsonString !== 'string') {
36
+ throw new TypeError(`Expected argument \`jsonString\` to be a \`string\`, got \`${typeof jsonString}\``);
37
+ }
38
+ const strip = whitespace ? stripWithWhitespace : stripWithoutWhitespace;
39
+ let isInsideString = false;
40
+ let isInsideComment = false;
41
+ let offset = 0;
42
+ let buffer = '';
43
+ let result = '';
44
+ let commaIndex = -1;
45
+ for (let index = 0; index < jsonString.length; index++) {
46
+ const currentCharacter = jsonString[index];
47
+ const nextCharacter = jsonString[index + 1];
48
+ if (!isInsideComment && currentCharacter === '"') {
49
+ // Enter or exit string
50
+ const escaped = isEscaped(jsonString, index);
51
+ if (!escaped) {
52
+ isInsideString = !isInsideString;
53
+ }
54
+ }
55
+ if (isInsideString) {
56
+ continue;
57
+ }
58
+ if (!isInsideComment && currentCharacter + nextCharacter === '//') {
59
+ // Enter single-line comment
60
+ buffer += jsonString.slice(offset, index);
61
+ offset = index;
62
+ isInsideComment = singleComment;
63
+ index++;
64
+ }
65
+ else if (isInsideComment === singleComment && currentCharacter + nextCharacter === '\r\n') {
66
+ // Exit single-line comment via \r\n
67
+ index++;
68
+ isInsideComment = false;
69
+ buffer += strip(jsonString, offset, index);
70
+ offset = index;
71
+ continue;
72
+ }
73
+ else if (isInsideComment === singleComment && currentCharacter === '\n') {
74
+ // Exit single-line comment via \n
75
+ isInsideComment = false;
76
+ buffer += strip(jsonString, offset, index);
77
+ offset = index;
78
+ }
79
+ else if (!isInsideComment && currentCharacter + nextCharacter === '/*') {
80
+ // Enter multiline comment
81
+ buffer += jsonString.slice(offset, index);
82
+ offset = index;
83
+ isInsideComment = multiComment;
84
+ index++;
85
+ continue;
86
+ }
87
+ else if (isInsideComment === multiComment && currentCharacter + nextCharacter === '*/') {
88
+ // Exit multiline comment
89
+ index++;
90
+ isInsideComment = false;
91
+ buffer += strip(jsonString, offset, index + 1);
92
+ offset = index + 1;
93
+ continue;
94
+ }
95
+ else if (trailingCommas && !isInsideComment) {
96
+ if (commaIndex !== -1) {
97
+ if (currentCharacter === '}' || currentCharacter === ']') {
98
+ // Strip trailing comma
99
+ buffer += jsonString.slice(offset, index);
100
+ result += strip(buffer, 0, 1) + buffer.slice(1);
101
+ buffer = '';
102
+ offset = index;
103
+ commaIndex = -1;
104
+ }
105
+ else if (currentCharacter !== ' ' && currentCharacter !== '\t' && currentCharacter !== '\r' && currentCharacter !== '\n') {
106
+ // Hit non-whitespace following a comma; comma is not trailing
107
+ buffer += jsonString.slice(offset, index);
108
+ offset = index;
109
+ commaIndex = -1;
110
+ }
111
+ }
112
+ else if (currentCharacter === ',') {
113
+ // Flush buffer prior to this point, and save new comma index
114
+ result += buffer + jsonString.slice(offset, index);
115
+ buffer = '';
116
+ offset = index;
117
+ commaIndex = index;
118
+ }
119
+ }
120
+ }
121
+ const remaining = (isInsideComment === singleComment)
122
+ ? strip(jsonString, offset)
123
+ : jsonString.slice(offset);
124
+ return result + buffer + remaining;
125
+ }
126
+ //# sourceMappingURL=strip-json-comments.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"strip-json-comments.js","sourceRoot":"./","sources":["vendor/strip-json-comments.ts"],"names":[],"mappings":"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","sourcesContent":["/*\nMIT License\n\nCopyright (c) Sindre Sorhus <sindresorhus@gmail.com> (https://sindresorhus.com)\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n*/\n\n// Vendored from strip-json-comments 5.0.3 (https://github.com/sindresorhus/strip-json-comments),\n// index.js and index.d.ts, converted to TypeScript. The logic is unchanged; only type annotations\n// were added. Upstream is ESM-only, so it is vendored here instead of depended on.\n\nexport type Options = {\n\t/**\n\tStrip trailing commas in addition to comments.\n\n\t@default false\n\t*/\n\treadonly trailingCommas?: boolean;\n\n\t/**\n\tReplace comments and trailing commas with whitespace instead of stripping them entirely.\n\n\t@default true\n\t*/\n\treadonly whitespace?: boolean;\n};\n\nconst singleComment = Symbol('singleComment');\nconst multiComment = Symbol('multiComment');\n\ntype CommentState = false | typeof singleComment | typeof multiComment;\n\nconst stripWithoutWhitespace = (_string: string, _start?: number, _end?: number): string => '';\n\n// Replace all characters except ASCII spaces, tabs and line endings with regular spaces to ensure valid JSON output.\nconst stripWithWhitespace = (string: string, start?: number, end?: number): string => string.slice(start, end).replace(/[^ \\t\\r\\n]/g, ' ');\n\nconst isEscaped = (jsonString: string, quotePosition: number): boolean => {\n\tlet index = quotePosition - 1;\n\tlet backslashCount = 0;\n\n\twhile (jsonString[index] === '\\\\') {\n\t\tindex -= 1;\n\t\tbackslashCount += 1;\n\t}\n\n\treturn Boolean(backslashCount % 2);\n};\n\n/**\nStrip comments from JSON. Lets you use comments in your JSON files!\n\nIt will replace single-line comments `//` and multi-line comments `/**\\/` with whitespace. This allows JSON error positions to remain as close as possible to the original source.\n\n@param jsonString - Accepts a string with JSON.\n@returns A JSON string without comments.\n*/\nexport default function stripJsonComments(jsonString: string, {whitespace = true, trailingCommas = false}: Options = {}): string {\n\tif (typeof jsonString !== 'string') {\n\t\tthrow new TypeError(`Expected argument \\`jsonString\\` to be a \\`string\\`, got \\`${typeof jsonString}\\``);\n\t}\n\n\tconst strip = whitespace ? stripWithWhitespace : stripWithoutWhitespace;\n\n\tlet isInsideString = false;\n\tlet isInsideComment: CommentState = false;\n\tlet offset = 0;\n\tlet buffer = '';\n\tlet result = '';\n\tlet commaIndex = -1;\n\n\tfor (let index = 0; index < jsonString.length; index++) {\n\t\tconst currentCharacter = jsonString[index];\n\t\tconst nextCharacter = jsonString[index + 1];\n\n\t\tif (!isInsideComment && currentCharacter === '\"') {\n\t\t\t// Enter or exit string\n\t\t\tconst escaped = isEscaped(jsonString, index);\n\t\t\tif (!escaped) {\n\t\t\t\tisInsideString = !isInsideString;\n\t\t\t}\n\t\t}\n\n\t\tif (isInsideString) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (!isInsideComment && currentCharacter + nextCharacter === '//') {\n\t\t\t// Enter single-line comment\n\t\t\tbuffer += jsonString.slice(offset, index);\n\t\t\toffset = index;\n\t\t\tisInsideComment = singleComment;\n\t\t\tindex++;\n\t\t} else if (isInsideComment === singleComment && currentCharacter + nextCharacter === '\\r\\n') {\n\t\t\t// Exit single-line comment via \\r\\n\n\t\t\tindex++;\n\t\t\tisInsideComment = false;\n\t\t\tbuffer += strip(jsonString, offset, index);\n\t\t\toffset = index;\n\t\t\tcontinue;\n\t\t} else if (isInsideComment === singleComment && currentCharacter === '\\n') {\n\t\t\t// Exit single-line comment via \\n\n\t\t\tisInsideComment = false;\n\t\t\tbuffer += strip(jsonString, offset, index);\n\t\t\toffset = index;\n\t\t} else if (!isInsideComment && currentCharacter + nextCharacter === '/*') {\n\t\t\t// Enter multiline comment\n\t\t\tbuffer += jsonString.slice(offset, index);\n\t\t\toffset = index;\n\t\t\tisInsideComment = multiComment;\n\t\t\tindex++;\n\t\t\tcontinue;\n\t\t} else if (isInsideComment === multiComment && currentCharacter + nextCharacter === '*/') {\n\t\t\t// Exit multiline comment\n\t\t\tindex++;\n\t\t\tisInsideComment = false;\n\t\t\tbuffer += strip(jsonString, offset, index + 1);\n\t\t\toffset = index + 1;\n\t\t\tcontinue;\n\t\t} else if (trailingCommas && !isInsideComment) {\n\t\t\tif (commaIndex !== -1) {\n\t\t\t\tif (currentCharacter === '}' || currentCharacter === ']') {\n\t\t\t\t\t// Strip trailing comma\n\t\t\t\t\tbuffer += jsonString.slice(offset, index);\n\t\t\t\t\tresult += strip(buffer, 0, 1) + buffer.slice(1);\n\t\t\t\t\tbuffer = '';\n\t\t\t\t\toffset = index;\n\t\t\t\t\tcommaIndex = -1;\n\t\t\t\t} else if (currentCharacter !== ' ' && currentCharacter !== '\\t' && currentCharacter !== '\\r' && currentCharacter !== '\\n') {\n\t\t\t\t\t// Hit non-whitespace following a comma; comma is not trailing\n\t\t\t\t\tbuffer += jsonString.slice(offset, index);\n\t\t\t\t\toffset = index;\n\t\t\t\t\tcommaIndex = -1;\n\t\t\t\t}\n\t\t\t} else if (currentCharacter === ',') {\n\t\t\t\t// Flush buffer prior to this point, and save new comma index\n\t\t\t\tresult += buffer + jsonString.slice(offset, index);\n\t\t\t\tbuffer = '';\n\t\t\t\toffset = index;\n\t\t\t\tcommaIndex = index;\n\t\t\t}\n\t\t}\n\t}\n\n\tconst remaining = (isInsideComment === singleComment)\n\t\t? strip(jsonString, offset)\n\t\t: jsonString.slice(offset);\n\n\treturn result + buffer + remaining;\n}\n"]}
package/package.json CHANGED
@@ -1,17 +1,26 @@
1
1
  {
2
2
  "name": "syncodemayo",
3
- "version": "1.0.8",
3
+ "version": "2.0.0",
4
4
  "description": "A simple FTP syncing tool",
5
- "main": "index.js",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
6
8
  "bin": {
7
- "syncodemayo": "./cli.js"
9
+ "syncodemayo": "dist/cli.js"
10
+ },
11
+ "files": [
12
+ "dist",
13
+ "CHANGELOG.md"
14
+ ],
15
+ "engines": {
16
+ "node": ">=22.17"
8
17
  },
9
18
  "scripts": {
10
- "test": "echo \"Error: no test specified\" && exit 1",
11
- "compile": "tsc -p ./tsconfig.json && echo 'Finished compile.'",
19
+ "test": "bun run compile && node --test \"test/**/*.test.mjs\"",
20
+ "compile": "tsc -p ./tsconfig.json && chmod +x dist/cli.js && echo 'Finished compile.'",
12
21
  "compile:watch": "tsc --watch -p ./tsconfig.json",
13
- "version:bump": "npm version patch",
14
- "prepublishOnly": "npm run version:bump && npm run compile"
22
+ "smoke:pack": "node scripts/smoke-pack.mjs",
23
+ "prepublishOnly": "bun run test"
15
24
  },
16
25
  "repository": {
17
26
  "type": "git",
@@ -24,25 +33,14 @@
24
33
  },
25
34
  "homepage": "https://github.com/mattmccray/syncodemayo#readme",
26
35
  "dependencies": {
27
- "commander": "^2.11.0",
28
- "crc": "3.4.1",
29
- "dotenv": "^4.0.0",
30
- "glob": "^7.1.2",
31
- "joi": "^10.6.0",
32
- "jsftp": "1.3.1",
33
- "jsftp-mkdirp": "0.1.2",
34
- "minimatch": "^3.0.4",
35
- "strip-json-comments": "^2.0.1",
36
- "yesno": "^0.0.1"
36
+ "basic-ftp": "^6.2.6"
37
37
  },
38
38
  "devDependencies": {
39
- "@types/commander": "^2.9.1",
40
- "@types/crc": "^3.4.0",
41
- "@types/dotenv": "^4.0.0",
42
- "@types/glob": "^5.0.30",
43
- "@types/joi": "^10.4.0",
44
- "@types/minimatch": "^2.0.29",
45
- "eslint": "^4.3.0",
46
- "typescript": "^2.4.2"
47
- }
39
+ "@types/node": "^22.20.5",
40
+ "ajv": "^8.20.0",
41
+ "ajv-draft-04": "^1.0.0",
42
+ "ftp-srv": "^4.6.3",
43
+ "typescript": "^5.9.3"
44
+ },
45
+ "packageManager": "bun@1.4.4"
48
46
  }
package/.npmignore DELETED
@@ -1,9 +0,0 @@
1
- # ignore the .ts files
2
- *.ts
3
-
4
- # include the .d.ts files
5
- !*.d.ts
6
-
7
- *.map
8
-
9
- .vscode/*
package/cli.d.ts DELETED
File without changes
package/cli.js DELETED
@@ -1,93 +0,0 @@
1
- #!/usr/bin/env node --harmony
2
- "use strict";
3
- Object.defineProperty(exports, "__esModule", { value: true });
4
- const dotenv = require("dotenv");
5
- const CLI = require("./lib/Cli");
6
- const program = require("commander");
7
- dotenv.config();
8
- const defaultTarget = undefined;
9
- let syncTarget = undefined;
10
- let command = 'help';
11
- program
12
- .version('1.0.0')
13
- .option('-c, --config [file]', 'Specify local config [file]');
14
- // .option('-v, --verbose', 'Verbose logging')
15
- program
16
- .command('init [target]')
17
- .description("Configure local folder and/or server to sync")
18
- .action((target) => {
19
- syncTarget = target || defaultTarget;
20
- command = 'init';
21
- });
22
- program
23
- .command('changes [target]')
24
- .alias('diff')
25
- .description("Perform sync dry run and display the changes")
26
- .action((target) => {
27
- syncTarget = target || defaultTarget;
28
- command = 'changes';
29
- });
30
- program
31
- .command('verify [target]')
32
- .description("Verify server is configured")
33
- .action((target) => {
34
- syncTarget = target || defaultTarget;
35
- command = 'check';
36
- });
37
- program
38
- .command('sync [target]')
39
- .description("Perform sync to server")
40
- .option('-f, --force', "Don't prompt before uploading.", false)
41
- .action((target, options) => {
42
- syncTarget = target || defaultTarget;
43
- command = options.force ? 'syncf' : 'sync';
44
- });
45
- program
46
- .command('ls')
47
- .description("List defined targets in config")
48
- .action(() => {
49
- command = "ls";
50
- });
51
- program.parse(process.argv);
52
- switch (command) {
53
- case "changes":
54
- console.log(`Performing '${command}' on target: ${syncTarget || '(default)'}...\n`);
55
- CLI.changes(program.config, syncTarget).then(showDone).catch(showError);
56
- break;
57
- case "check":
58
- console.log(`Performing '${command}' on target: ${syncTarget || '(default)'}...\n`);
59
- CLI.check(program.config, syncTarget).then(showDone).catch(showError);
60
- break;
61
- case "init":
62
- console.log(`Performing '${command}' on target: ${syncTarget || '(default)'}...\n`);
63
- CLI.init(program.config, syncTarget).then(showDone).catch(showError);
64
- break;
65
- case "ls":
66
- console.log(`Performing '${command}'...\n`);
67
- CLI.ls(program.config).then(showDone).catch(showError);
68
- break;
69
- case "sync":
70
- console.log(`Performing '${command}' on target: ${syncTarget || '(default)'}\n...`);
71
- CLI.sync(program.config, syncTarget, false).then(showDone).catch(showError);
72
- break;
73
- case "syncf":
74
- console.log(`Performing (forced) 'sync' on target: ${syncTarget || '(default)'}\n...`);
75
- CLI.sync(program.config, syncTarget, true).then(showDone).catch(showError);
76
- break;
77
- case "help":
78
- default:
79
- syncTarget = "app";
80
- program.help();
81
- }
82
- function showDone() {
83
- console.log("\nDone.");
84
- process.exit(0);
85
- }
86
- function showError(err) {
87
- console.error("\n(!)", err.message, "\n", err);
88
- // if (program.verbose) {
89
- // console.error(err)
90
- // }
91
- process.exit(1);
92
- }
93
- //# sourceMappingURL=cli.js.map
package/index.d.ts DELETED
@@ -1 +0,0 @@
1
- export * from './lib';