permachine 0.8.0 → 0.9.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/dist/cli.js +130 -89
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -6025,6 +6025,104 @@ function stripJsonComments(jsonString, { whitespace = true, trailingCommas = fal
|
|
|
6025
6025
|
return result + buffer + remaining;
|
|
6026
6026
|
}
|
|
6027
6027
|
|
|
6028
|
+
// src/core/errors.ts
|
|
6029
|
+
class PermachineError extends Error {
|
|
6030
|
+
constructor(message) {
|
|
6031
|
+
super(message);
|
|
6032
|
+
this.name = "PermachineError";
|
|
6033
|
+
}
|
|
6034
|
+
}
|
|
6035
|
+
|
|
6036
|
+
class NestedFilteredDirectoryError extends PermachineError {
|
|
6037
|
+
outerDir;
|
|
6038
|
+
innerDir;
|
|
6039
|
+
constructor(outerDir, innerDir) {
|
|
6040
|
+
super(`Nested machine-specific directories are not supported.
|
|
6041
|
+
` + `Found: ${innerDir} inside ${outerDir}
|
|
6042
|
+
` + `Only one level of directory filtering is allowed.`);
|
|
6043
|
+
this.name = "NestedFilteredDirectoryError";
|
|
6044
|
+
this.outerDir = outerDir;
|
|
6045
|
+
this.innerDir = innerDir;
|
|
6046
|
+
}
|
|
6047
|
+
}
|
|
6048
|
+
|
|
6049
|
+
class DirectoryConflictError extends PermachineError {
|
|
6050
|
+
outputPath;
|
|
6051
|
+
sources;
|
|
6052
|
+
constructor(outputPath, sources) {
|
|
6053
|
+
super(`Multiple directories would output to the same path: ${outputPath}
|
|
6054
|
+
` + `Sources: ${sources.join(", ")}
|
|
6055
|
+
` + `Only one machine-specific directory can match per output path.`);
|
|
6056
|
+
this.name = "DirectoryConflictError";
|
|
6057
|
+
this.outputPath = outputPath;
|
|
6058
|
+
this.sources = sources;
|
|
6059
|
+
}
|
|
6060
|
+
}
|
|
6061
|
+
|
|
6062
|
+
class FileDirectoryConflictError extends PermachineError {
|
|
6063
|
+
outputPath;
|
|
6064
|
+
fileSource;
|
|
6065
|
+
dirSource;
|
|
6066
|
+
constructor(outputPath, fileSource, dirSource) {
|
|
6067
|
+
super(`Both a file merge and directory copy would output to: ${outputPath}
|
|
6068
|
+
` + `File source: ${fileSource}
|
|
6069
|
+
` + `Directory source: ${dirSource}
|
|
6070
|
+
` + `Remove one of these to resolve the conflict.`);
|
|
6071
|
+
this.name = "FileDirectoryConflictError";
|
|
6072
|
+
this.outputPath = outputPath;
|
|
6073
|
+
this.fileSource = fileSource;
|
|
6074
|
+
this.dirSource = dirSource;
|
|
6075
|
+
}
|
|
6076
|
+
}
|
|
6077
|
+
|
|
6078
|
+
class BaseDirectoryNotSupportedError extends PermachineError {
|
|
6079
|
+
dirPath;
|
|
6080
|
+
constructor(dirPath) {
|
|
6081
|
+
super(`Base directories are not supported: ${dirPath}
|
|
6082
|
+
` + `Unlike files, directories do not support .base fallback.
|
|
6083
|
+
` + `Use machine-specific directories only (e.g., mydir.{machine=X}/).`);
|
|
6084
|
+
this.name = "BaseDirectoryNotSupportedError";
|
|
6085
|
+
this.dirPath = dirPath;
|
|
6086
|
+
}
|
|
6087
|
+
}
|
|
6088
|
+
|
|
6089
|
+
class DirectoryCopyError extends PermachineError {
|
|
6090
|
+
sourcePath;
|
|
6091
|
+
outputPath;
|
|
6092
|
+
cause;
|
|
6093
|
+
constructor(sourcePath, outputPath, cause) {
|
|
6094
|
+
super(`Failed to copy directory: ${sourcePath} → ${outputPath}
|
|
6095
|
+
` + (cause ? `Cause: ${cause.message}` : ""));
|
|
6096
|
+
this.name = "DirectoryCopyError";
|
|
6097
|
+
this.sourcePath = sourcePath;
|
|
6098
|
+
this.outputPath = outputPath;
|
|
6099
|
+
this.cause = cause;
|
|
6100
|
+
}
|
|
6101
|
+
}
|
|
6102
|
+
|
|
6103
|
+
class CleanupError extends PermachineError {
|
|
6104
|
+
path;
|
|
6105
|
+
cause;
|
|
6106
|
+
constructor(path2, cause) {
|
|
6107
|
+
super(`Failed to cleanup stale output: ${path2}
|
|
6108
|
+
` + (cause ? `Cause: ${cause.message}` : ""));
|
|
6109
|
+
this.name = "CleanupError";
|
|
6110
|
+
this.path = path2;
|
|
6111
|
+
this.cause = cause;
|
|
6112
|
+
}
|
|
6113
|
+
}
|
|
6114
|
+
|
|
6115
|
+
class ArrayMergeError extends PermachineError {
|
|
6116
|
+
key;
|
|
6117
|
+
constructor(key) {
|
|
6118
|
+
super(`Cannot merge arrays containing non-primitive values at key "${key}".
|
|
6119
|
+
` + `Array merging only supports primitive values (string, number, boolean, null).
|
|
6120
|
+
` + `Arrays containing objects or nested arrays cannot be merged.`);
|
|
6121
|
+
this.name = "ArrayMergeError";
|
|
6122
|
+
this.key = key;
|
|
6123
|
+
}
|
|
6124
|
+
}
|
|
6125
|
+
|
|
6028
6126
|
// src/adapters/json-adapter.ts
|
|
6029
6127
|
class JsonAdapter {
|
|
6030
6128
|
canHandle(extension) {
|
|
@@ -6046,7 +6144,10 @@ class JsonAdapter {
|
|
|
6046
6144
|
return JSON.stringify(data, null, 2) + `
|
|
6047
6145
|
`;
|
|
6048
6146
|
}
|
|
6049
|
-
deepMerge(base, machine) {
|
|
6147
|
+
deepMerge(base, machine, path2 = "") {
|
|
6148
|
+
if (Array.isArray(base) && Array.isArray(machine)) {
|
|
6149
|
+
return this.mergeArrays(base, machine, path2);
|
|
6150
|
+
}
|
|
6050
6151
|
if (machine === null || typeof machine !== "object" || Array.isArray(machine)) {
|
|
6051
6152
|
return machine;
|
|
6052
6153
|
}
|
|
@@ -6056,8 +6157,9 @@ class JsonAdapter {
|
|
|
6056
6157
|
const result = { ...base };
|
|
6057
6158
|
for (const key in machine) {
|
|
6058
6159
|
if (Object.prototype.hasOwnProperty.call(machine, key)) {
|
|
6160
|
+
const newPath = path2 ? `${path2}.${key}` : key;
|
|
6059
6161
|
if (key in result) {
|
|
6060
|
-
result[key] = this.deepMerge(result[key], machine[key]);
|
|
6162
|
+
result[key] = this.deepMerge(result[key], machine[key], newPath);
|
|
6061
6163
|
} else {
|
|
6062
6164
|
result[key] = machine[key];
|
|
6063
6165
|
}
|
|
@@ -6065,6 +6167,32 @@ class JsonAdapter {
|
|
|
6065
6167
|
}
|
|
6066
6168
|
return result;
|
|
6067
6169
|
}
|
|
6170
|
+
isPrimitive(value) {
|
|
6171
|
+
return value === null || typeof value === "string" || typeof value === "number" || typeof value === "boolean";
|
|
6172
|
+
}
|
|
6173
|
+
isArrayOfPrimitives(arr) {
|
|
6174
|
+
return arr.every((item) => this.isPrimitive(item));
|
|
6175
|
+
}
|
|
6176
|
+
mergeArrays(base, machine, path2) {
|
|
6177
|
+
if (!this.isArrayOfPrimitives(base) || !this.isArrayOfPrimitives(machine)) {
|
|
6178
|
+
throw new ArrayMergeError(path2 || "root");
|
|
6179
|
+
}
|
|
6180
|
+
const seen = new Set;
|
|
6181
|
+
const result = [];
|
|
6182
|
+
for (const item of base) {
|
|
6183
|
+
if (!seen.has(item)) {
|
|
6184
|
+
seen.add(item);
|
|
6185
|
+
result.push(item);
|
|
6186
|
+
}
|
|
6187
|
+
}
|
|
6188
|
+
for (const item of machine) {
|
|
6189
|
+
if (!seen.has(item)) {
|
|
6190
|
+
seen.add(item);
|
|
6191
|
+
result.push(item);
|
|
6192
|
+
}
|
|
6193
|
+
}
|
|
6194
|
+
return result;
|
|
6195
|
+
}
|
|
6068
6196
|
}
|
|
6069
6197
|
|
|
6070
6198
|
// src/adapters/env-adapter.ts
|
|
@@ -6144,93 +6272,6 @@ function getFileType(filename) {
|
|
|
6144
6272
|
return "unknown";
|
|
6145
6273
|
}
|
|
6146
6274
|
|
|
6147
|
-
// src/core/errors.ts
|
|
6148
|
-
class PermachineError extends Error {
|
|
6149
|
-
constructor(message) {
|
|
6150
|
-
super(message);
|
|
6151
|
-
this.name = "PermachineError";
|
|
6152
|
-
}
|
|
6153
|
-
}
|
|
6154
|
-
|
|
6155
|
-
class NestedFilteredDirectoryError extends PermachineError {
|
|
6156
|
-
outerDir;
|
|
6157
|
-
innerDir;
|
|
6158
|
-
constructor(outerDir, innerDir) {
|
|
6159
|
-
super(`Nested machine-specific directories are not supported.
|
|
6160
|
-
` + `Found: ${innerDir} inside ${outerDir}
|
|
6161
|
-
` + `Only one level of directory filtering is allowed.`);
|
|
6162
|
-
this.name = "NestedFilteredDirectoryError";
|
|
6163
|
-
this.outerDir = outerDir;
|
|
6164
|
-
this.innerDir = innerDir;
|
|
6165
|
-
}
|
|
6166
|
-
}
|
|
6167
|
-
|
|
6168
|
-
class DirectoryConflictError extends PermachineError {
|
|
6169
|
-
outputPath;
|
|
6170
|
-
sources;
|
|
6171
|
-
constructor(outputPath, sources) {
|
|
6172
|
-
super(`Multiple directories would output to the same path: ${outputPath}
|
|
6173
|
-
` + `Sources: ${sources.join(", ")}
|
|
6174
|
-
` + `Only one machine-specific directory can match per output path.`);
|
|
6175
|
-
this.name = "DirectoryConflictError";
|
|
6176
|
-
this.outputPath = outputPath;
|
|
6177
|
-
this.sources = sources;
|
|
6178
|
-
}
|
|
6179
|
-
}
|
|
6180
|
-
|
|
6181
|
-
class FileDirectoryConflictError extends PermachineError {
|
|
6182
|
-
outputPath;
|
|
6183
|
-
fileSource;
|
|
6184
|
-
dirSource;
|
|
6185
|
-
constructor(outputPath, fileSource, dirSource) {
|
|
6186
|
-
super(`Both a file merge and directory copy would output to: ${outputPath}
|
|
6187
|
-
` + `File source: ${fileSource}
|
|
6188
|
-
` + `Directory source: ${dirSource}
|
|
6189
|
-
` + `Remove one of these to resolve the conflict.`);
|
|
6190
|
-
this.name = "FileDirectoryConflictError";
|
|
6191
|
-
this.outputPath = outputPath;
|
|
6192
|
-
this.fileSource = fileSource;
|
|
6193
|
-
this.dirSource = dirSource;
|
|
6194
|
-
}
|
|
6195
|
-
}
|
|
6196
|
-
|
|
6197
|
-
class BaseDirectoryNotSupportedError extends PermachineError {
|
|
6198
|
-
dirPath;
|
|
6199
|
-
constructor(dirPath) {
|
|
6200
|
-
super(`Base directories are not supported: ${dirPath}
|
|
6201
|
-
` + `Unlike files, directories do not support .base fallback.
|
|
6202
|
-
` + `Use machine-specific directories only (e.g., mydir.{machine=X}/).`);
|
|
6203
|
-
this.name = "BaseDirectoryNotSupportedError";
|
|
6204
|
-
this.dirPath = dirPath;
|
|
6205
|
-
}
|
|
6206
|
-
}
|
|
6207
|
-
|
|
6208
|
-
class DirectoryCopyError extends PermachineError {
|
|
6209
|
-
sourcePath;
|
|
6210
|
-
outputPath;
|
|
6211
|
-
cause;
|
|
6212
|
-
constructor(sourcePath, outputPath, cause) {
|
|
6213
|
-
super(`Failed to copy directory: ${sourcePath} → ${outputPath}
|
|
6214
|
-
` + (cause ? `Cause: ${cause.message}` : ""));
|
|
6215
|
-
this.name = "DirectoryCopyError";
|
|
6216
|
-
this.sourcePath = sourcePath;
|
|
6217
|
-
this.outputPath = outputPath;
|
|
6218
|
-
this.cause = cause;
|
|
6219
|
-
}
|
|
6220
|
-
}
|
|
6221
|
-
|
|
6222
|
-
class CleanupError extends PermachineError {
|
|
6223
|
-
path;
|
|
6224
|
-
cause;
|
|
6225
|
-
constructor(path3, cause) {
|
|
6226
|
-
super(`Failed to cleanup stale output: ${path3}
|
|
6227
|
-
` + (cause ? `Cause: ${cause.message}` : ""));
|
|
6228
|
-
this.name = "CleanupError";
|
|
6229
|
-
this.path = path3;
|
|
6230
|
-
this.cause = cause;
|
|
6231
|
-
}
|
|
6232
|
-
}
|
|
6233
|
-
|
|
6234
6275
|
// src/core/file-scanner.ts
|
|
6235
6276
|
async function scanForMergeOperations(machineName, cwd = process.cwd()) {
|
|
6236
6277
|
const operations = [];
|