@syncat-dev/cli 0.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.
- package/LICENSE +9 -0
- package/README.md +7 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +88 -0
- package/dist/cli.js.map +1 -0
- package/dist/dist-Bn-gPTzR.js +294 -0
- package/dist/dist-Bn-gPTzR.js.map +1 -0
- package/dist/index.d.ts +38 -0
- package/dist/index.js +12 -0
- package/dist/index.js.map +1 -0
- package/package.json +47 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 bingtsingw
|
|
4
|
+
|
|
5
|
+
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:
|
|
6
|
+
|
|
7
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
8
|
+
|
|
9
|
+
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.
|
package/README.md
ADDED
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { a as loadSyncatConfig, n as buildSyncPlan, r as createUnifiedDiff, t as applySyncPlan } from "./dist-Bn-gPTzR.js";
|
|
3
|
+
import { cac } from "cac";
|
|
4
|
+
//#region package.json
|
|
5
|
+
var version = "0.0.0";
|
|
6
|
+
//#endregion
|
|
7
|
+
//#region src/commands.ts
|
|
8
|
+
async function runCheck(options) {
|
|
9
|
+
const config = await loadSyncatConfig({ configPath: options.config });
|
|
10
|
+
const plan = await buildSyncPlan(config);
|
|
11
|
+
if (options.json) writeJson("check", plan);
|
|
12
|
+
else writeCheckReport(plan, options.diff ?? false);
|
|
13
|
+
if (plan.drift.length > 0) process.exitCode = 1;
|
|
14
|
+
}
|
|
15
|
+
async function runWrite(options) {
|
|
16
|
+
const config = await loadSyncatConfig({ configPath: options.config });
|
|
17
|
+
const plan = await buildSyncPlan(config);
|
|
18
|
+
const result = await applySyncPlan(plan, { dryRun: options.dryRun ?? false });
|
|
19
|
+
if (options.json) {
|
|
20
|
+
process.stdout.write(`${JSON.stringify({
|
|
21
|
+
command: "write",
|
|
22
|
+
configFile: plan.config.configFile,
|
|
23
|
+
dryRun: result.dryRun,
|
|
24
|
+
summary: { changed: result.written.length },
|
|
25
|
+
files: result.written.map(({ path, status }) => ({
|
|
26
|
+
path,
|
|
27
|
+
status
|
|
28
|
+
}))
|
|
29
|
+
}, null, 2)}\n`);
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
if (result.written.length === 0) {
|
|
33
|
+
process.stdout.write("syncat: target is already synchronized.\n");
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
const verb = options.dryRun ? "would write" : "wrote";
|
|
37
|
+
for (const entry of result.written) process.stdout.write(`${verb} ${entry.path}\n`);
|
|
38
|
+
process.stdout.write(`syncat: ${result.written.length} file(s) ${options.dryRun ? "would change" : "updated"}.\n`);
|
|
39
|
+
}
|
|
40
|
+
function writeCheckReport(plan, includeDiff) {
|
|
41
|
+
if (plan.drift.length === 0) {
|
|
42
|
+
process.stdout.write(`syncat: synchronized (${plan.entries.length} file(s)).\n`);
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
for (const entry of plan.drift) {
|
|
46
|
+
process.stdout.write(`${entry.status} ${entry.path}\n`);
|
|
47
|
+
if (includeDiff && entry.status === "changed" && entry.targetContent) process.stdout.write(createUnifiedDiff(entry.path, entry.targetContent, entry.desiredContent));
|
|
48
|
+
}
|
|
49
|
+
process.stdout.write(`syncat: ${plan.drift.length} file(s) differ.\n`);
|
|
50
|
+
}
|
|
51
|
+
function writeJson(command, plan) {
|
|
52
|
+
process.stdout.write(`${JSON.stringify({
|
|
53
|
+
command,
|
|
54
|
+
configFile: plan.config.configFile,
|
|
55
|
+
source: plan.config.sourceDir,
|
|
56
|
+
target: plan.config.targetDir,
|
|
57
|
+
summary: {
|
|
58
|
+
total: plan.entries.length,
|
|
59
|
+
synchronized: plan.entries.filter((entry) => entry.status === "synced").length,
|
|
60
|
+
drift: plan.drift.length
|
|
61
|
+
},
|
|
62
|
+
files: plan.entries.map(({ path, status }) => ({
|
|
63
|
+
path,
|
|
64
|
+
status
|
|
65
|
+
}))
|
|
66
|
+
}, null, 2)}\n`);
|
|
67
|
+
}
|
|
68
|
+
//#endregion
|
|
69
|
+
//#region src/cli.ts
|
|
70
|
+
const cli = cac("syncat");
|
|
71
|
+
cli.command("check", "Check whether target files match the rendered source files.").option("-c, --config <path>", "Path to syncat.config.ts").option("--diff", "Print a unified diff for each differing text file.").option("--json", "Print machine-readable JSON.").action((options) => {
|
|
72
|
+
runCheck(options).catch(reportError);
|
|
73
|
+
});
|
|
74
|
+
cli.command("write", "Write differing source files into the target project.").option("-c, --config <path>", "Path to syncat.config.ts").option("--dry-run", "Print planned writes without changing files.").option("--json", "Print machine-readable JSON.").action((options) => {
|
|
75
|
+
runWrite(options).catch(reportError);
|
|
76
|
+
});
|
|
77
|
+
cli.help();
|
|
78
|
+
cli.version(version);
|
|
79
|
+
cli.parse();
|
|
80
|
+
function reportError(error) {
|
|
81
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
82
|
+
process.stderr.write(`syncat: ${message}\n`);
|
|
83
|
+
process.exitCode = 2;
|
|
84
|
+
}
|
|
85
|
+
//#endregion
|
|
86
|
+
export {};
|
|
87
|
+
|
|
88
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","names":["packageJson.version"],"sources":["../package.json","../src/commands.ts","../src/cli.ts"],"sourcesContent":["","import { applySyncPlan, buildSyncPlan, createUnifiedDiff, loadSyncatConfig, type SyncPlan } from '@syncat-dev/core';\n\ninterface CheckOptions {\n config?: string;\n diff?: boolean;\n json?: boolean;\n}\n\ninterface WriteOptions {\n config?: string;\n dryRun?: boolean;\n json?: boolean;\n}\n\nexport async function runCheck(options: CheckOptions): Promise<void> {\n const config = await loadSyncatConfig({ configPath: options.config });\n const plan = await buildSyncPlan(config);\n\n if (options.json) {\n writeJson('check', plan);\n } else {\n writeCheckReport(plan, options.diff ?? false);\n }\n\n if (plan.drift.length > 0) {\n process.exitCode = 1;\n }\n}\n\nexport async function runWrite(options: WriteOptions): Promise<void> {\n const config = await loadSyncatConfig({ configPath: options.config });\n const plan = await buildSyncPlan(config);\n const result = await applySyncPlan(plan, { dryRun: options.dryRun ?? false });\n\n if (options.json) {\n process.stdout.write(\n `${JSON.stringify(\n {\n command: 'write',\n configFile: plan.config.configFile,\n dryRun: result.dryRun,\n summary: { changed: result.written.length },\n files: result.written.map(({ path, status }) => ({ path, status })),\n },\n null,\n 2,\n )}\\n`,\n );\n return;\n }\n\n if (result.written.length === 0) {\n process.stdout.write('syncat: target is already synchronized.\\n');\n return;\n }\n\n const verb = options.dryRun ? 'would write' : 'wrote';\n for (const entry of result.written) {\n process.stdout.write(`${verb} ${entry.path}\\n`);\n }\n process.stdout.write(`syncat: ${result.written.length} file(s) ${options.dryRun ? 'would change' : 'updated'}.\\n`);\n}\n\nfunction writeCheckReport(plan: SyncPlan, includeDiff: boolean): void {\n if (plan.drift.length === 0) {\n process.stdout.write(`syncat: synchronized (${plan.entries.length} file(s)).\\n`);\n return;\n }\n\n for (const entry of plan.drift) {\n process.stdout.write(`${entry.status} ${entry.path}\\n`);\n if (includeDiff && entry.status === 'changed' && entry.targetContent) {\n process.stdout.write(createUnifiedDiff(entry.path, entry.targetContent, entry.desiredContent));\n }\n }\n process.stdout.write(`syncat: ${plan.drift.length} file(s) differ.\\n`);\n}\n\nfunction writeJson(command: 'check', plan: SyncPlan): void {\n process.stdout.write(\n `${JSON.stringify(\n {\n command,\n configFile: plan.config.configFile,\n source: plan.config.sourceDir,\n target: plan.config.targetDir,\n summary: {\n total: plan.entries.length,\n synchronized: plan.entries.filter((entry) => entry.status === 'synced').length,\n drift: plan.drift.length,\n },\n files: plan.entries.map(({ path, status }) => ({ path, status })),\n },\n null,\n 2,\n )}\\n`,\n );\n}\n","#!/usr/bin/env node\n\nimport { cac } from 'cac';\nimport packageJson from '../package.json';\nimport { runCheck, runWrite } from './commands';\n\nconst cli = cac('syncat');\n\ncli\n .command('check', 'Check whether target files match the rendered source files.')\n .option('-c, --config <path>', 'Path to syncat.config.ts')\n .option('--diff', 'Print a unified diff for each differing text file.')\n .option('--json', 'Print machine-readable JSON.')\n .action((options) => {\n void runCheck(options).catch(reportError);\n });\n\ncli\n .command('write', 'Write differing source files into the target project.')\n .option('-c, --config <path>', 'Path to syncat.config.ts')\n .option('--dry-run', 'Print planned writes without changing files.')\n .option('--json', 'Print machine-readable JSON.')\n .action((options) => {\n void runWrite(options).catch(reportError);\n });\n\ncli.help();\ncli.version(packageJson.version);\ncli.parse();\n\nfunction reportError(error: unknown): void {\n const message = error instanceof Error ? error.message : String(error);\n process.stderr.write(`syncat: ${message}\\n`);\n process.exitCode = 2;\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
import { basename, dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
2
|
+
import { loadConfig } from "c12";
|
|
3
|
+
import { chmod, lstat, mkdir, readFile, realpath, rename, rm, stat, writeFile } from "node:fs/promises";
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
import { randomUUID } from "node:crypto";
|
|
6
|
+
import { TextDecoder } from "node:util";
|
|
7
|
+
import fastGlob from "fast-glob";
|
|
8
|
+
//#region ../core/dist/index.js
|
|
9
|
+
var SyncatError = class extends Error {
|
|
10
|
+
constructor(message) {
|
|
11
|
+
super(message);
|
|
12
|
+
this.name = "SyncatError";
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
const replaceRuleSchema = z.object({
|
|
16
|
+
from: z.string().min(1, "Replacement \"from\" must not be empty."),
|
|
17
|
+
to: z.string(),
|
|
18
|
+
all: z.boolean().optional()
|
|
19
|
+
}).strict();
|
|
20
|
+
const strategySchema = z.discriminatedUnion("type", [z.object({ type: z.literal("copy") }).strict(), z.object({
|
|
21
|
+
type: z.literal("text-replace"),
|
|
22
|
+
replacements: z.array(replaceRuleSchema).min(1, "A text-replace strategy needs at least one replacement.")
|
|
23
|
+
}).strict()]);
|
|
24
|
+
const syncatConfigSchema = z.object({
|
|
25
|
+
source: z.string().min(1),
|
|
26
|
+
target: z.string().min(1),
|
|
27
|
+
files: z.array(z.object({
|
|
28
|
+
path: z.string().min(1),
|
|
29
|
+
strategy: strategySchema.optional()
|
|
30
|
+
}).strict()).min(1, "At least one file rule is required.")
|
|
31
|
+
}).strict();
|
|
32
|
+
function defineConfig(config) {
|
|
33
|
+
return config;
|
|
34
|
+
}
|
|
35
|
+
function resolveSyncatConfig(rawConfig, configFile) {
|
|
36
|
+
const parsed = syncatConfigSchema.safeParse(rawConfig);
|
|
37
|
+
if (!parsed.success) throw new SyncatError(`Invalid syncat config:\n${z.prettifyError(parsed.error)}`);
|
|
38
|
+
const configDir = dirname(configFile);
|
|
39
|
+
const sourceDir = resolveConfigPath(parsed.data.source, configDir);
|
|
40
|
+
const targetDir = resolveConfigPath(parsed.data.target, configDir);
|
|
41
|
+
for (const rule of parsed.data.files) assertSafeRulePath(rule.path);
|
|
42
|
+
return {
|
|
43
|
+
config: parsed.data,
|
|
44
|
+
configFile,
|
|
45
|
+
sourceDir,
|
|
46
|
+
targetDir
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
function resolveConfigPath(value, cwd) {
|
|
50
|
+
const home = process.env["HOME"] ?? process.env["USERPROFILE"];
|
|
51
|
+
const expanded = value === "~" && home ? home : value.startsWith("~/") && home ? resolve(home, value.slice(2)) : value;
|
|
52
|
+
return resolve(cwd, expanded);
|
|
53
|
+
}
|
|
54
|
+
function assertSafeRulePath(value) {
|
|
55
|
+
if (isAbsolute(value) || value.includes("\\")) throw new SyncatError(`File rule path must be a relative POSIX path: ${value}`);
|
|
56
|
+
if (value.split("/").some((segment) => segment === "..") || value === "." || value.startsWith("../")) throw new SyncatError(`File rule path must not escape its project root: ${value}`);
|
|
57
|
+
}
|
|
58
|
+
function assertPathInside(root, candidate) {
|
|
59
|
+
const pathRelative = relative(root, candidate);
|
|
60
|
+
if (pathRelative === "" || !pathRelative.startsWith(`..${sep}`) && pathRelative !== ".." && !isAbsolute(pathRelative)) return;
|
|
61
|
+
throw new SyncatError(`Resolved path escapes its project root: ${candidate}`);
|
|
62
|
+
}
|
|
63
|
+
async function assertRealPathInside(root, candidate) {
|
|
64
|
+
const [realRoot, realCandidate] = await Promise.all([realpath(root), realpath(candidate)]);
|
|
65
|
+
assertPathInside(realRoot, realCandidate);
|
|
66
|
+
}
|
|
67
|
+
async function assertExistingAncestorInside(root, candidate) {
|
|
68
|
+
const realRoot = await realpath(root);
|
|
69
|
+
let ancestor = candidate;
|
|
70
|
+
while (true) try {
|
|
71
|
+
assertPathInside(realRoot, await realpath(ancestor));
|
|
72
|
+
return;
|
|
73
|
+
} catch (error) {
|
|
74
|
+
if (!isNotFound$2(error)) throw error;
|
|
75
|
+
const parent = dirname(ancestor);
|
|
76
|
+
if (parent === ancestor) throw new SyncatError(`Could not find an existing ancestor for path: ${candidate}`);
|
|
77
|
+
ancestor = parent;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
function isNotFound$2(error) {
|
|
81
|
+
return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
|
|
82
|
+
}
|
|
83
|
+
async function applySyncPlan(plan, options) {
|
|
84
|
+
const written = plan.drift;
|
|
85
|
+
if (options.dryRun) return {
|
|
86
|
+
dryRun: true,
|
|
87
|
+
written
|
|
88
|
+
};
|
|
89
|
+
for (const entry of written) await writeEntry(plan.config.targetDir, entry.sourcePath, entry.targetPath, entry.desiredContent);
|
|
90
|
+
return {
|
|
91
|
+
dryRun: false,
|
|
92
|
+
written
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
async function writeEntry(targetRoot, sourcePath, targetPath, contents) {
|
|
96
|
+
await mkdir(dirname(targetPath), { recursive: true });
|
|
97
|
+
await assertRealPathInside(targetRoot, dirname(targetPath));
|
|
98
|
+
await rejectTargetSymlink(targetPath);
|
|
99
|
+
const sourceStat = await stat(sourcePath);
|
|
100
|
+
const temporaryPath = join(dirname(targetPath), `.${basename(targetPath)}.syncat-${randomUUID()}.tmp`);
|
|
101
|
+
try {
|
|
102
|
+
await writeFile(temporaryPath, contents, { mode: sourceStat.mode });
|
|
103
|
+
await chmod(temporaryPath, sourceStat.mode);
|
|
104
|
+
await rename(temporaryPath, targetPath);
|
|
105
|
+
} catch (error) {
|
|
106
|
+
await rm(temporaryPath, { force: true });
|
|
107
|
+
throw new SyncatError(`Failed to write ${targetPath}: ${error instanceof Error ? error.message : String(error)}`);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
async function rejectTargetSymlink(path) {
|
|
111
|
+
try {
|
|
112
|
+
if ((await lstat(path)).isSymbolicLink()) throw new SyncatError(`Refusing to overwrite symbolic link target: ${path}`);
|
|
113
|
+
} catch (error) {
|
|
114
|
+
if (isNotFound$1(error)) return;
|
|
115
|
+
throw error;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
function isNotFound$1(error) {
|
|
119
|
+
return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
|
|
120
|
+
}
|
|
121
|
+
const decoder = new TextDecoder("utf-8", { fatal: true });
|
|
122
|
+
const maximumComparisonCells = 1e6;
|
|
123
|
+
function createUnifiedDiff(path, actual, expected) {
|
|
124
|
+
let actualText;
|
|
125
|
+
let expectedText;
|
|
126
|
+
try {
|
|
127
|
+
actualText = decoder.decode(actual);
|
|
128
|
+
expectedText = decoder.decode(expected);
|
|
129
|
+
} catch {
|
|
130
|
+
return `Binary files differ: ${path}\n`;
|
|
131
|
+
}
|
|
132
|
+
const before = actualText.split("\n");
|
|
133
|
+
const after = expectedText.split("\n");
|
|
134
|
+
if (before.length * after.length > maximumComparisonCells) return `Diff omitted for ${path}: files are too large for a line-by-line comparison.\n`;
|
|
135
|
+
const table = Array.from({ length: before.length + 1 }, () => new Uint32Array(after.length + 1));
|
|
136
|
+
for (let beforeIndex = before.length - 1; beforeIndex >= 0; beforeIndex -= 1) for (let afterIndex = after.length - 1; afterIndex >= 0; afterIndex -= 1) {
|
|
137
|
+
const beforeLine = getLine(before, beforeIndex);
|
|
138
|
+
const afterLine = getLine(after, afterIndex);
|
|
139
|
+
getRow(table, beforeIndex)[afterIndex] = beforeLine === afterLine ? getCell(table, beforeIndex + 1, afterIndex + 1) + 1 : Math.max(getCell(table, beforeIndex + 1, afterIndex), getCell(table, beforeIndex, afterIndex + 1));
|
|
140
|
+
}
|
|
141
|
+
const output = [
|
|
142
|
+
`--- target/${path}`,
|
|
143
|
+
`+++ expected/${path}`,
|
|
144
|
+
`@@ -1,${before.length} +1,${after.length} @@`
|
|
145
|
+
];
|
|
146
|
+
let beforeIndex = 0;
|
|
147
|
+
let afterIndex = 0;
|
|
148
|
+
while (beforeIndex < before.length || afterIndex < after.length) {
|
|
149
|
+
const beforeLine = beforeIndex < before.length ? getLine(before, beforeIndex) : void 0;
|
|
150
|
+
const afterLine = afterIndex < after.length ? getLine(after, afterIndex) : void 0;
|
|
151
|
+
if (beforeLine !== void 0 && afterLine !== void 0 && beforeLine === afterLine) {
|
|
152
|
+
output.push(` ${beforeLine}`);
|
|
153
|
+
beforeIndex += 1;
|
|
154
|
+
afterIndex += 1;
|
|
155
|
+
} else if (afterLine !== void 0 && (beforeLine === void 0 || getCell(table, beforeIndex, afterIndex + 1) >= getCell(table, beforeIndex + 1, afterIndex))) {
|
|
156
|
+
output.push(`+${afterLine}`);
|
|
157
|
+
afterIndex += 1;
|
|
158
|
+
} else {
|
|
159
|
+
if (beforeLine === void 0) throw new Error("Diff generation reached an invalid state.");
|
|
160
|
+
output.push(`-${beforeLine}`);
|
|
161
|
+
beforeIndex += 1;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
return `${output.join("\n")}\n`;
|
|
165
|
+
}
|
|
166
|
+
function getCell(table, rowIndex, columnIndex) {
|
|
167
|
+
return getRow(table, rowIndex)[columnIndex] ?? 0;
|
|
168
|
+
}
|
|
169
|
+
function getLine(lines, index) {
|
|
170
|
+
const line = lines[index];
|
|
171
|
+
if (line === void 0) throw new Error("Diff generation reached an invalid line index.");
|
|
172
|
+
return line;
|
|
173
|
+
}
|
|
174
|
+
function getRow(table, rowIndex) {
|
|
175
|
+
const row = table[rowIndex];
|
|
176
|
+
if (!row) throw new Error("Diff generation reached an invalid table row.");
|
|
177
|
+
return row;
|
|
178
|
+
}
|
|
179
|
+
const utf8Decoder = new TextDecoder("utf-8", { fatal: true });
|
|
180
|
+
function renderDesiredContent(source, strategy, path) {
|
|
181
|
+
if (!strategy || strategy.type === "copy") return source;
|
|
182
|
+
let text;
|
|
183
|
+
try {
|
|
184
|
+
text = utf8Decoder.decode(source);
|
|
185
|
+
} catch {
|
|
186
|
+
throw new SyncatError(`Text strategy cannot process non-UTF-8 file: ${path}`);
|
|
187
|
+
}
|
|
188
|
+
for (const replacement of strategy.replacements) {
|
|
189
|
+
if (text.split(replacement.from).length - 1 === 0) throw new SyncatError(`Replacement source was not found in ${path}: ${JSON.stringify(replacement.from)}`);
|
|
190
|
+
text = replacement.all === false ? text.replace(replacement.from, replacement.to) : text.split(replacement.from).join(replacement.to);
|
|
191
|
+
}
|
|
192
|
+
return Buffer.from(text, "utf8");
|
|
193
|
+
}
|
|
194
|
+
const globSyntax = /[*?[\]{}]/;
|
|
195
|
+
async function buildSyncPlan(config) {
|
|
196
|
+
await assertDirectory(config.sourceDir, "source");
|
|
197
|
+
await assertDirectory(config.targetDir, "target");
|
|
198
|
+
const [realSourceDir, realTargetDir] = await Promise.all([realpath(config.sourceDir), realpath(config.targetDir)]);
|
|
199
|
+
if (realSourceDir === realTargetDir) throw new SyncatError("Configured source and target directories must be different.");
|
|
200
|
+
const entries = [];
|
|
201
|
+
const seenPaths = /* @__PURE__ */ new Set();
|
|
202
|
+
for (const rule of config.config.files) {
|
|
203
|
+
const paths = await resolveRulePaths(config.sourceDir, rule.path);
|
|
204
|
+
if (paths.length === 0) throw new SyncatError(`File rule matched no source files: ${rule.path}`);
|
|
205
|
+
for (const path of paths) {
|
|
206
|
+
if (seenPaths.has(path)) throw new SyncatError(`A source file is managed by more than one rule: ${path}`);
|
|
207
|
+
seenPaths.add(path);
|
|
208
|
+
const sourcePath = resolve(config.sourceDir, path);
|
|
209
|
+
const targetPath = resolve(config.targetDir, path);
|
|
210
|
+
assertPathInside(config.sourceDir, sourcePath);
|
|
211
|
+
assertPathInside(config.targetDir, targetPath);
|
|
212
|
+
await assertRealPathInside(config.sourceDir, sourcePath);
|
|
213
|
+
await assertRegularFile(sourcePath, "source");
|
|
214
|
+
const sourceContent = await readFile(sourcePath);
|
|
215
|
+
const desiredContent = renderDesiredContent(sourceContent, rule.strategy, path);
|
|
216
|
+
const targetContent = await readOptionalRegularFile(config.targetDir, targetPath);
|
|
217
|
+
const status = !targetContent ? "missing" : targetContent.equals(desiredContent) ? "synced" : "changed";
|
|
218
|
+
entries.push({
|
|
219
|
+
path,
|
|
220
|
+
sourcePath,
|
|
221
|
+
targetPath,
|
|
222
|
+
sourceContent,
|
|
223
|
+
targetContent,
|
|
224
|
+
desiredContent,
|
|
225
|
+
status
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
entries.sort((left, right) => left.path.localeCompare(right.path));
|
|
230
|
+
return {
|
|
231
|
+
config,
|
|
232
|
+
entries,
|
|
233
|
+
drift: entries.filter((entry) => entry.status !== "synced")
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
async function resolveRulePaths(sourceDir, rulePath) {
|
|
237
|
+
if (!globSyntax.test(rulePath)) return [rulePath];
|
|
238
|
+
return fastGlob(rulePath, {
|
|
239
|
+
cwd: sourceDir,
|
|
240
|
+
onlyFiles: true,
|
|
241
|
+
dot: true,
|
|
242
|
+
followSymbolicLinks: false,
|
|
243
|
+
unique: true
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
async function assertDirectory(path, label) {
|
|
247
|
+
let fileStat;
|
|
248
|
+
try {
|
|
249
|
+
fileStat = await stat(path);
|
|
250
|
+
} catch {
|
|
251
|
+
throw new SyncatError(`Configured ${label} directory does not exist: ${path}`);
|
|
252
|
+
}
|
|
253
|
+
if (!fileStat.isDirectory()) throw new SyncatError(`Configured ${label} path is not a directory: ${path}`);
|
|
254
|
+
}
|
|
255
|
+
async function assertRegularFile(path, label) {
|
|
256
|
+
if (!(await lstat(path)).isFile()) throw new SyncatError(`Managed ${label} path is not a regular file: ${path}`);
|
|
257
|
+
}
|
|
258
|
+
async function readOptionalRegularFile(targetRoot, path) {
|
|
259
|
+
try {
|
|
260
|
+
await assertExistingAncestorInside(targetRoot, path);
|
|
261
|
+
await assertRegularFile(path, "target");
|
|
262
|
+
await assertRealPathInside(targetRoot, path);
|
|
263
|
+
return await readFile(path);
|
|
264
|
+
} catch (error) {
|
|
265
|
+
if (isNotFound(error)) return;
|
|
266
|
+
throw error;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
function isNotFound(error) {
|
|
270
|
+
return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
|
|
271
|
+
}
|
|
272
|
+
async function loadSyncatConfig(options = {}) {
|
|
273
|
+
const cwd = options.cwd ?? process.cwd();
|
|
274
|
+
const configPath = resolve(cwd, options.configPath ?? "syncat.config.ts");
|
|
275
|
+
const loaded = await loadConfig({
|
|
276
|
+
name: "syncat",
|
|
277
|
+
cwd,
|
|
278
|
+
configFile: configPath,
|
|
279
|
+
configFileRequired: true,
|
|
280
|
+
dotenv: false,
|
|
281
|
+
envName: false,
|
|
282
|
+
extend: false,
|
|
283
|
+
giget: false,
|
|
284
|
+
globalRc: false,
|
|
285
|
+
packageJson: false,
|
|
286
|
+
rcFile: false
|
|
287
|
+
});
|
|
288
|
+
if (!loaded.configFile) throw new Error("Could not determine the loaded syncat config file.");
|
|
289
|
+
return resolveSyncatConfig(loaded.config, loaded.configFile);
|
|
290
|
+
}
|
|
291
|
+
//#endregion
|
|
292
|
+
export { loadSyncatConfig as a, defineConfig as i, buildSyncPlan as n, createUnifiedDiff as r, applySyncPlan as t };
|
|
293
|
+
|
|
294
|
+
//# sourceMappingURL=dist-Bn-gPTzR.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dist-Bn-gPTzR.js","names":[],"sources":["../../core/dist/index.js"],"sourcesContent":["import { basename, dirname, isAbsolute, join, relative, resolve, sep } from \"node:path\";\nimport { loadConfig } from \"c12\";\nimport { chmod, lstat, mkdir, readFile, realpath, rename, rm, stat, writeFile } from \"node:fs/promises\";\nimport { z } from \"zod\";\nimport { randomUUID } from \"node:crypto\";\nimport { TextDecoder } from \"node:util\";\nimport fastGlob from \"fast-glob\";\n//#region src/errors.ts\nvar SyncatError = class extends Error {\n\tconstructor(message) {\n\t\tsuper(message);\n\t\tthis.name = \"SyncatError\";\n\t}\n};\n//#endregion\n//#region src/config.ts\nconst replaceRuleSchema = z.object({\n\tfrom: z.string().min(1, \"Replacement \\\"from\\\" must not be empty.\"),\n\tto: z.string(),\n\tall: z.boolean().optional()\n}).strict();\nconst strategySchema = z.discriminatedUnion(\"type\", [z.object({ type: z.literal(\"copy\") }).strict(), z.object({\n\ttype: z.literal(\"text-replace\"),\n\treplacements: z.array(replaceRuleSchema).min(1, \"A text-replace strategy needs at least one replacement.\")\n}).strict()]);\nconst syncatConfigSchema = z.object({\n\tsource: z.string().min(1),\n\ttarget: z.string().min(1),\n\tfiles: z.array(z.object({\n\t\tpath: z.string().min(1),\n\t\tstrategy: strategySchema.optional()\n\t}).strict()).min(1, \"At least one file rule is required.\")\n}).strict();\nfunction defineConfig(config) {\n\treturn config;\n}\nfunction resolveSyncatConfig(rawConfig, configFile) {\n\tconst parsed = syncatConfigSchema.safeParse(rawConfig);\n\tif (!parsed.success) throw new SyncatError(`Invalid syncat config:\\n${z.prettifyError(parsed.error)}`);\n\tconst configDir = dirname(configFile);\n\tconst sourceDir = resolveConfigPath(parsed.data.source, configDir);\n\tconst targetDir = resolveConfigPath(parsed.data.target, configDir);\n\tfor (const rule of parsed.data.files) assertSafeRulePath(rule.path);\n\treturn {\n\t\tconfig: parsed.data,\n\t\tconfigFile,\n\t\tsourceDir,\n\t\ttargetDir\n\t};\n}\nfunction resolveConfigPath(value, cwd) {\n\tconst home = process.env[\"HOME\"] ?? process.env[\"USERPROFILE\"];\n\tconst expanded = value === \"~\" && home ? home : value.startsWith(\"~/\") && home ? resolve(home, value.slice(2)) : value;\n\treturn resolve(cwd, expanded);\n}\nfunction assertSafeRulePath(value) {\n\tif (isAbsolute(value) || value.includes(\"\\\\\")) throw new SyncatError(`File rule path must be a relative POSIX path: ${value}`);\n\tif (value.split(\"/\").some((segment) => segment === \"..\") || value === \".\" || value.startsWith(\"../\")) throw new SyncatError(`File rule path must not escape its project root: ${value}`);\n}\nfunction assertPathInside(root, candidate) {\n\tconst pathRelative = relative(root, candidate);\n\tif (pathRelative === \"\" || !pathRelative.startsWith(`..${sep}`) && pathRelative !== \"..\" && !isAbsolute(pathRelative)) return;\n\tthrow new SyncatError(`Resolved path escapes its project root: ${candidate}`);\n}\nasync function assertRealPathInside(root, candidate) {\n\tconst [realRoot, realCandidate] = await Promise.all([realpath(root), realpath(candidate)]);\n\tassertPathInside(realRoot, realCandidate);\n}\nasync function assertExistingAncestorInside(root, candidate) {\n\tconst realRoot = await realpath(root);\n\tlet ancestor = candidate;\n\twhile (true) try {\n\t\tassertPathInside(realRoot, await realpath(ancestor));\n\t\treturn;\n\t} catch (error) {\n\t\tif (!isNotFound$2(error)) throw error;\n\t\tconst parent = dirname(ancestor);\n\t\tif (parent === ancestor) throw new SyncatError(`Could not find an existing ancestor for path: ${candidate}`);\n\t\tancestor = parent;\n\t}\n}\nfunction isNotFound$2(error) {\n\treturn typeof error === \"object\" && error !== null && \"code\" in error && error.code === \"ENOENT\";\n}\n//#endregion\n//#region src/apply.ts\nasync function applySyncPlan(plan, options) {\n\tconst written = plan.drift;\n\tif (options.dryRun) return {\n\t\tdryRun: true,\n\t\twritten\n\t};\n\tfor (const entry of written) await writeEntry(plan.config.targetDir, entry.sourcePath, entry.targetPath, entry.desiredContent);\n\treturn {\n\t\tdryRun: false,\n\t\twritten\n\t};\n}\nasync function writeEntry(targetRoot, sourcePath, targetPath, contents) {\n\tawait mkdir(dirname(targetPath), { recursive: true });\n\tawait assertRealPathInside(targetRoot, dirname(targetPath));\n\tawait rejectTargetSymlink(targetPath);\n\tconst sourceStat = await stat(sourcePath);\n\tconst temporaryPath = join(dirname(targetPath), `.${basename(targetPath)}.syncat-${randomUUID()}.tmp`);\n\ttry {\n\t\tawait writeFile(temporaryPath, contents, { mode: sourceStat.mode });\n\t\tawait chmod(temporaryPath, sourceStat.mode);\n\t\tawait rename(temporaryPath, targetPath);\n\t} catch (error) {\n\t\tawait rm(temporaryPath, { force: true });\n\t\tthrow new SyncatError(`Failed to write ${targetPath}: ${error instanceof Error ? error.message : String(error)}`);\n\t}\n}\nasync function rejectTargetSymlink(path) {\n\ttry {\n\t\tif ((await lstat(path)).isSymbolicLink()) throw new SyncatError(`Refusing to overwrite symbolic link target: ${path}`);\n\t} catch (error) {\n\t\tif (isNotFound$1(error)) return;\n\t\tthrow error;\n\t}\n}\nfunction isNotFound$1(error) {\n\treturn typeof error === \"object\" && error !== null && \"code\" in error && error.code === \"ENOENT\";\n}\n//#endregion\n//#region src/diff.ts\nconst decoder = new TextDecoder(\"utf-8\", { fatal: true });\nconst maximumComparisonCells = 1e6;\nfunction createUnifiedDiff(path, actual, expected) {\n\tlet actualText;\n\tlet expectedText;\n\ttry {\n\t\tactualText = decoder.decode(actual);\n\t\texpectedText = decoder.decode(expected);\n\t} catch {\n\t\treturn `Binary files differ: ${path}\\n`;\n\t}\n\tconst before = actualText.split(\"\\n\");\n\tconst after = expectedText.split(\"\\n\");\n\tif (before.length * after.length > maximumComparisonCells) return `Diff omitted for ${path}: files are too large for a line-by-line comparison.\\n`;\n\tconst table = Array.from({ length: before.length + 1 }, () => new Uint32Array(after.length + 1));\n\tfor (let beforeIndex = before.length - 1; beforeIndex >= 0; beforeIndex -= 1) for (let afterIndex = after.length - 1; afterIndex >= 0; afterIndex -= 1) {\n\t\tconst beforeLine = getLine(before, beforeIndex);\n\t\tconst afterLine = getLine(after, afterIndex);\n\t\tgetRow(table, beforeIndex)[afterIndex] = beforeLine === afterLine ? getCell(table, beforeIndex + 1, afterIndex + 1) + 1 : Math.max(getCell(table, beforeIndex + 1, afterIndex), getCell(table, beforeIndex, afterIndex + 1));\n\t}\n\tconst output = [\n\t\t`--- target/${path}`,\n\t\t`+++ expected/${path}`,\n\t\t`@@ -1,${before.length} +1,${after.length} @@`\n\t];\n\tlet beforeIndex = 0;\n\tlet afterIndex = 0;\n\twhile (beforeIndex < before.length || afterIndex < after.length) {\n\t\tconst beforeLine = beforeIndex < before.length ? getLine(before, beforeIndex) : void 0;\n\t\tconst afterLine = afterIndex < after.length ? getLine(after, afterIndex) : void 0;\n\t\tif (beforeLine !== void 0 && afterLine !== void 0 && beforeLine === afterLine) {\n\t\t\toutput.push(` ${beforeLine}`);\n\t\t\tbeforeIndex += 1;\n\t\t\tafterIndex += 1;\n\t\t} else if (afterLine !== void 0 && (beforeLine === void 0 || getCell(table, beforeIndex, afterIndex + 1) >= getCell(table, beforeIndex + 1, afterIndex))) {\n\t\t\toutput.push(`+${afterLine}`);\n\t\t\tafterIndex += 1;\n\t\t} else {\n\t\t\tif (beforeLine === void 0) throw new Error(\"Diff generation reached an invalid state.\");\n\t\t\toutput.push(`-${beforeLine}`);\n\t\t\tbeforeIndex += 1;\n\t\t}\n\t}\n\treturn `${output.join(\"\\n\")}\\n`;\n}\nfunction getCell(table, rowIndex, columnIndex) {\n\treturn getRow(table, rowIndex)[columnIndex] ?? 0;\n}\nfunction getLine(lines, index) {\n\tconst line = lines[index];\n\tif (line === void 0) throw new Error(\"Diff generation reached an invalid line index.\");\n\treturn line;\n}\nfunction getRow(table, rowIndex) {\n\tconst row = table[rowIndex];\n\tif (!row) throw new Error(\"Diff generation reached an invalid table row.\");\n\treturn row;\n}\n//#endregion\n//#region src/strategy.ts\nconst utf8Decoder = new TextDecoder(\"utf-8\", { fatal: true });\nfunction renderDesiredContent(source, strategy, path) {\n\tif (!strategy || strategy.type === \"copy\") return source;\n\tlet text;\n\ttry {\n\t\ttext = utf8Decoder.decode(source);\n\t} catch {\n\t\tthrow new SyncatError(`Text strategy cannot process non-UTF-8 file: ${path}`);\n\t}\n\tfor (const replacement of strategy.replacements) {\n\t\tif (text.split(replacement.from).length - 1 === 0) throw new SyncatError(`Replacement source was not found in ${path}: ${JSON.stringify(replacement.from)}`);\n\t\ttext = replacement.all === false ? text.replace(replacement.from, replacement.to) : text.split(replacement.from).join(replacement.to);\n\t}\n\treturn Buffer.from(text, \"utf8\");\n}\n//#endregion\n//#region src/plan.ts\nconst globSyntax = /[*?[\\]{}]/;\nasync function buildSyncPlan(config) {\n\tawait assertDirectory(config.sourceDir, \"source\");\n\tawait assertDirectory(config.targetDir, \"target\");\n\tconst [realSourceDir, realTargetDir] = await Promise.all([realpath(config.sourceDir), realpath(config.targetDir)]);\n\tif (realSourceDir === realTargetDir) throw new SyncatError(\"Configured source and target directories must be different.\");\n\tconst entries = [];\n\tconst seenPaths = /* @__PURE__ */ new Set();\n\tfor (const rule of config.config.files) {\n\t\tconst paths = await resolveRulePaths(config.sourceDir, rule.path);\n\t\tif (paths.length === 0) throw new SyncatError(`File rule matched no source files: ${rule.path}`);\n\t\tfor (const path of paths) {\n\t\t\tif (seenPaths.has(path)) throw new SyncatError(`A source file is managed by more than one rule: ${path}`);\n\t\t\tseenPaths.add(path);\n\t\t\tconst sourcePath = resolve(config.sourceDir, path);\n\t\t\tconst targetPath = resolve(config.targetDir, path);\n\t\t\tassertPathInside(config.sourceDir, sourcePath);\n\t\t\tassertPathInside(config.targetDir, targetPath);\n\t\t\tawait assertRealPathInside(config.sourceDir, sourcePath);\n\t\t\tawait assertRegularFile(sourcePath, \"source\");\n\t\t\tconst sourceContent = await readFile(sourcePath);\n\t\t\tconst desiredContent = renderDesiredContent(sourceContent, rule.strategy, path);\n\t\t\tconst targetContent = await readOptionalRegularFile(config.targetDir, targetPath);\n\t\t\tconst status = !targetContent ? \"missing\" : targetContent.equals(desiredContent) ? \"synced\" : \"changed\";\n\t\t\tentries.push({\n\t\t\t\tpath,\n\t\t\t\tsourcePath,\n\t\t\t\ttargetPath,\n\t\t\t\tsourceContent,\n\t\t\t\ttargetContent,\n\t\t\t\tdesiredContent,\n\t\t\t\tstatus\n\t\t\t});\n\t\t}\n\t}\n\tentries.sort((left, right) => left.path.localeCompare(right.path));\n\treturn {\n\t\tconfig,\n\t\tentries,\n\t\tdrift: entries.filter((entry) => entry.status !== \"synced\")\n\t};\n}\nasync function resolveRulePaths(sourceDir, rulePath) {\n\tif (!globSyntax.test(rulePath)) return [rulePath];\n\treturn fastGlob(rulePath, {\n\t\tcwd: sourceDir,\n\t\tonlyFiles: true,\n\t\tdot: true,\n\t\tfollowSymbolicLinks: false,\n\t\tunique: true\n\t});\n}\nasync function assertDirectory(path, label) {\n\tlet fileStat;\n\ttry {\n\t\tfileStat = await stat(path);\n\t} catch {\n\t\tthrow new SyncatError(`Configured ${label} directory does not exist: ${path}`);\n\t}\n\tif (!fileStat.isDirectory()) throw new SyncatError(`Configured ${label} path is not a directory: ${path}`);\n}\nasync function assertRegularFile(path, label) {\n\tif (!(await lstat(path)).isFile()) throw new SyncatError(`Managed ${label} path is not a regular file: ${path}`);\n}\nasync function readOptionalRegularFile(targetRoot, path) {\n\ttry {\n\t\tawait assertExistingAncestorInside(targetRoot, path);\n\t\tawait assertRegularFile(path, \"target\");\n\t\tawait assertRealPathInside(targetRoot, path);\n\t\treturn await readFile(path);\n\t} catch (error) {\n\t\tif (isNotFound(error)) return;\n\t\tthrow error;\n\t}\n}\nfunction isNotFound(error) {\n\treturn typeof error === \"object\" && error !== null && \"code\" in error && error.code === \"ENOENT\";\n}\n//#endregion\n//#region src/index.ts\nasync function loadSyncatConfig(options = {}) {\n\tconst cwd = options.cwd ?? process.cwd();\n\tconst configPath = resolve(cwd, options.configPath ?? \"syncat.config.ts\");\n\tconst loaded = await loadConfig({\n\t\tname: \"syncat\",\n\t\tcwd,\n\t\tconfigFile: configPath,\n\t\tconfigFileRequired: true,\n\t\tdotenv: false,\n\t\tenvName: false,\n\t\textend: false,\n\t\tgiget: false,\n\t\tglobalRc: false,\n\t\tpackageJson: false,\n\t\trcFile: false\n\t});\n\tif (!loaded.configFile) throw new Error(\"Could not determine the loaded syncat config file.\");\n\treturn resolveSyncatConfig(loaded.config, loaded.configFile);\n}\n//#endregion\nexport { SyncatError, applySyncPlan, buildSyncPlan, createUnifiedDiff, defineConfig, loadSyncatConfig, resolveSyncatConfig };\n\n//# sourceMappingURL=index.js.map"],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
//#region ../core/dist/index.d.ts
|
|
3
|
+
//#region src/types.d.ts
|
|
4
|
+
interface ReplaceRule {
|
|
5
|
+
from: string;
|
|
6
|
+
to: string;
|
|
7
|
+
all?: boolean;
|
|
8
|
+
}
|
|
9
|
+
interface CopyStrategy {
|
|
10
|
+
type: 'copy';
|
|
11
|
+
}
|
|
12
|
+
interface TextReplaceStrategy {
|
|
13
|
+
type: 'text-replace';
|
|
14
|
+
replacements: ReplaceRule[];
|
|
15
|
+
}
|
|
16
|
+
type StrategyConfig = CopyStrategy | TextReplaceStrategy;
|
|
17
|
+
interface FileRule {
|
|
18
|
+
path: string;
|
|
19
|
+
strategy?: StrategyConfig;
|
|
20
|
+
}
|
|
21
|
+
interface SyncatConfig {
|
|
22
|
+
source: string;
|
|
23
|
+
target: string;
|
|
24
|
+
files: FileRule[];
|
|
25
|
+
}
|
|
26
|
+
//#endregion
|
|
27
|
+
//#region src/config.d.ts
|
|
28
|
+
export declare function defineConfig<T extends SyncatConfig>(config: T): T;
|
|
29
|
+
//#endregion
|
|
30
|
+
//#region ../strategy/dist/index.d.ts
|
|
31
|
+
//#region src/index.d.ts
|
|
32
|
+
export declare const text: {
|
|
33
|
+
replace(replacements: ReplaceRule[]): TextReplaceStrategy;
|
|
34
|
+
};
|
|
35
|
+
//#endregion
|
|
36
|
+
//#endregion
|
|
37
|
+
export type { FileRule, ReplaceRule, StrategyConfig, SyncatConfig };
|
|
38
|
+
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { i as defineConfig } from "./dist-Bn-gPTzR.js";
|
|
2
|
+
//#region ../strategy/dist/index.js
|
|
3
|
+
const text = { replace(replacements) {
|
|
4
|
+
return {
|
|
5
|
+
type: "text-replace",
|
|
6
|
+
replacements
|
|
7
|
+
};
|
|
8
|
+
} };
|
|
9
|
+
//#endregion
|
|
10
|
+
export { defineConfig, text };
|
|
11
|
+
|
|
12
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../strategy/dist/index.js"],"sourcesContent":["//#region src/index.ts\nconst text = { replace(replacements) {\n\treturn {\n\t\ttype: \"text-replace\",\n\t\treplacements\n\t};\n} };\n//#endregion\nexport { text };\n\n//# sourceMappingURL=index.js.map"],"mappings":";;AACA,MAAM,OAAO,EAAE,QAAQ,cAAc;CACpC,OAAO;EACN,MAAM;EACN;CACD;AACD,EAAE"}
|
package/package.json
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@syncat-dev/cli",
|
|
3
|
+
"version": "0.0.0",
|
|
4
|
+
"description": "Synchronize selected files from a source directory to a target directory with configurable strategies.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"file-copy",
|
|
7
|
+
"file-sync",
|
|
8
|
+
"strategy",
|
|
9
|
+
"synchronization"
|
|
10
|
+
],
|
|
11
|
+
"license": "MIT",
|
|
12
|
+
"bin": {
|
|
13
|
+
"syncat": "./dist/cli.js"
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist"
|
|
17
|
+
],
|
|
18
|
+
"type": "module",
|
|
19
|
+
"exports": {
|
|
20
|
+
".": {
|
|
21
|
+
"types": "./dist/index.d.ts",
|
|
22
|
+
"import": "./dist/index.js"
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"publishConfig": {
|
|
26
|
+
"access": "public"
|
|
27
|
+
},
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"c12": "3.3.4",
|
|
30
|
+
"cac": "7.0.0",
|
|
31
|
+
"fast-glob": "3.3.3",
|
|
32
|
+
"zod": "^4.6.2"
|
|
33
|
+
},
|
|
34
|
+
"devDependencies": {
|
|
35
|
+
"@syncat-dev/core": "0.0.0",
|
|
36
|
+
"@syncat-dev/strategy": "0.0.0"
|
|
37
|
+
},
|
|
38
|
+
"engines": {
|
|
39
|
+
"node": ">=24.14.0"
|
|
40
|
+
},
|
|
41
|
+
"scripts": {
|
|
42
|
+
"build": "vp pack",
|
|
43
|
+
"dev": "vp pack --watch",
|
|
44
|
+
"pretest": "pnpm --filter @syncat-dev/core run build && pnpm --filter @syncat-dev/strategy run build && pnpm run build",
|
|
45
|
+
"test": "vp test"
|
|
46
|
+
}
|
|
47
|
+
}
|