saasaloy 0.5.0 → 0.5.2
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/README.md +1 -0
- package/dist/index.js +439 -135
- package/dist/index.js.map +1 -1
- package/package.json +3 -1
- package/schemas/manifest.schema.json +2 -1
- package/schemas/registry-item.schema.json +63 -2
- package/templates/base/apps/web/astro.config.mjs +21 -0
- package/templates/base/apps/web/package.json +11 -10
- package/templates/base/apps/web/wrangler.jsonc +7 -0
- package/templates/base/package.json +9 -9
- package/templates/base/packages/ui/package.json +4 -4
- package/templates/base/packages/ui/src/lib/theme.ts +55 -43
package/dist/index.js
CHANGED
|
@@ -367,6 +367,34 @@ var MANIFEST_FILE = join3(".saasaloy", "manifest.json");
|
|
|
367
367
|
function samePatchEntry(a, b) {
|
|
368
368
|
return a.module === b.module && a.file === b.file && JSON.stringify(a.patch) === JSON.stringify(b.patch);
|
|
369
369
|
}
|
|
370
|
+
function recordManagedFile(manifest, record) {
|
|
371
|
+
manifest.managed[record.target] = {
|
|
372
|
+
hash: record.hash,
|
|
373
|
+
module: record.module,
|
|
374
|
+
...record.adopted ? { adopted: true } : {},
|
|
375
|
+
...record.from === void 0 ? {} : { from: record.from }
|
|
376
|
+
};
|
|
377
|
+
}
|
|
378
|
+
function untrackManagedFile(manifest, target) {
|
|
379
|
+
delete manifest.managed[target];
|
|
380
|
+
}
|
|
381
|
+
function recordPatch(manifest, entry) {
|
|
382
|
+
if (manifest.patches.some((existing) => samePatchEntry(existing, entry))) {
|
|
383
|
+
return;
|
|
384
|
+
}
|
|
385
|
+
manifest.patches.push(entry);
|
|
386
|
+
}
|
|
387
|
+
function untrackPatch(manifest, entry) {
|
|
388
|
+
manifest.patches = manifest.patches.filter(
|
|
389
|
+
(patch) => !samePatchEntry(patch, entry)
|
|
390
|
+
);
|
|
391
|
+
}
|
|
392
|
+
function recordLink(manifest, target, path) {
|
|
393
|
+
manifest.links[target] = path;
|
|
394
|
+
}
|
|
395
|
+
function untrackLink(manifest, target) {
|
|
396
|
+
delete manifest.links[target];
|
|
397
|
+
}
|
|
370
398
|
function emptyManifest() {
|
|
371
399
|
return { links: {}, managed: {}, patches: [], removeWarnings: {} };
|
|
372
400
|
}
|
|
@@ -707,40 +735,45 @@ function buildRouteLink(receiver, path, call) {
|
|
|
707
735
|
|
|
708
736
|
// src/lib/patch/const-array.ts
|
|
709
737
|
import { parseModule as parseModule2 } from "magicast";
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
const
|
|
714
|
-
|
|
715
|
-
|
|
738
|
+
|
|
739
|
+
// src/lib/patch/ts-array.ts
|
|
740
|
+
function findConstArray(program, constName) {
|
|
741
|
+
for (const statement of program.body) {
|
|
742
|
+
const declaration = statement.type === "ExportNamedDeclaration" ? statement.declaration : statement;
|
|
743
|
+
if (declaration?.type !== "VariableDeclaration" || declaration.kind !== "const") {
|
|
744
|
+
continue;
|
|
745
|
+
}
|
|
746
|
+
for (const declarator of declaration.declarations) {
|
|
747
|
+
if (declarator.id.type !== "Identifier" || declarator.id.name !== constName || !declarator.init) {
|
|
748
|
+
continue;
|
|
749
|
+
}
|
|
750
|
+
return asArray(declarator.init);
|
|
751
|
+
}
|
|
716
752
|
}
|
|
717
|
-
return
|
|
753
|
+
return void 0;
|
|
718
754
|
}
|
|
719
|
-
function
|
|
720
|
-
const
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
755
|
+
function asArray(node) {
|
|
756
|
+
const value = unwrapTypeExpression(node);
|
|
757
|
+
return value.type === "ArrayExpression" ? value : void 0;
|
|
758
|
+
}
|
|
759
|
+
function findElement(array, key, wanted) {
|
|
760
|
+
for (const element of array.elements) {
|
|
761
|
+
if (element?.type === "ObjectExpression" && entryValue(element, key) === wanted) {
|
|
762
|
+
return element;
|
|
763
|
+
}
|
|
727
764
|
}
|
|
728
765
|
return void 0;
|
|
729
766
|
}
|
|
730
|
-
function
|
|
731
|
-
const
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
(element) => entryValue(element, patch.key) === wanted
|
|
739
|
-
);
|
|
740
|
-
if (index === -1) {
|
|
741
|
-
return source;
|
|
767
|
+
function propertyValue(object, key) {
|
|
768
|
+
for (const property of object.properties) {
|
|
769
|
+
if (property.type !== "ObjectProperty" || property.computed) {
|
|
770
|
+
continue;
|
|
771
|
+
}
|
|
772
|
+
if (propertyName(property.key) === key) {
|
|
773
|
+
return property.value;
|
|
774
|
+
}
|
|
742
775
|
}
|
|
743
|
-
return
|
|
776
|
+
return void 0;
|
|
744
777
|
}
|
|
745
778
|
function appendEntry(source, array, entry) {
|
|
746
779
|
const close = array.end - 1;
|
|
@@ -797,6 +830,32 @@ function entryValue(element, key) {
|
|
|
797
830
|
}
|
|
798
831
|
return void 0;
|
|
799
832
|
}
|
|
833
|
+
function renderObject(entry) {
|
|
834
|
+
return `{ ${renderProperties(entry).join(", ")} }`;
|
|
835
|
+
}
|
|
836
|
+
function renderProperties(entry) {
|
|
837
|
+
return Object.entries(entry).map(
|
|
838
|
+
([key, value]) => `${renderKey(key)}: ${renderValue(value)}`
|
|
839
|
+
);
|
|
840
|
+
}
|
|
841
|
+
function renderKey(key) {
|
|
842
|
+
return /^[A-Za-z_$][\w$]*$/.test(key) ? key : JSON.stringify(key);
|
|
843
|
+
}
|
|
844
|
+
function renderValue(value) {
|
|
845
|
+
if (value === null || typeof value === "string" || typeof value === "boolean") {
|
|
846
|
+
return JSON.stringify(value);
|
|
847
|
+
}
|
|
848
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
849
|
+
return String(value);
|
|
850
|
+
}
|
|
851
|
+
if (Array.isArray(value)) {
|
|
852
|
+
return `[${value.map(renderValue).join(", ")}]`;
|
|
853
|
+
}
|
|
854
|
+
if (typeof value === "object") {
|
|
855
|
+
return renderObject(value);
|
|
856
|
+
}
|
|
857
|
+
throw new TypeError("patch entries must contain JSON values");
|
|
858
|
+
}
|
|
800
859
|
function propertyName(node) {
|
|
801
860
|
if (node.type === "Identifier") {
|
|
802
861
|
return node.name;
|
|
@@ -809,22 +868,6 @@ function literalValue(node) {
|
|
|
809
868
|
}
|
|
810
869
|
return node.type === "NullLiteral" ? null : node.value;
|
|
811
870
|
}
|
|
812
|
-
function findConstArray(program, constName) {
|
|
813
|
-
for (const statement of program.body) {
|
|
814
|
-
const declaration = statement.type === "ExportNamedDeclaration" ? statement.declaration : statement;
|
|
815
|
-
if (declaration?.type !== "VariableDeclaration" || declaration.kind !== "const") {
|
|
816
|
-
continue;
|
|
817
|
-
}
|
|
818
|
-
for (const declarator of declaration.declarations) {
|
|
819
|
-
if (declarator.id.type !== "Identifier" || declarator.id.name !== constName || !declarator.init) {
|
|
820
|
-
continue;
|
|
821
|
-
}
|
|
822
|
-
const value = unwrapTypeExpression(declarator.init);
|
|
823
|
-
return value.type === "ArrayExpression" ? value : void 0;
|
|
824
|
-
}
|
|
825
|
-
}
|
|
826
|
-
return void 0;
|
|
827
|
-
}
|
|
828
871
|
function unwrapTypeExpression(node) {
|
|
829
872
|
let value = node;
|
|
830
873
|
while (value.type === "TSAsExpression" || value.type === "TSSatisfiesExpression" || value.type === "TypeCastExpression") {
|
|
@@ -832,26 +875,42 @@ function unwrapTypeExpression(node) {
|
|
|
832
875
|
}
|
|
833
876
|
return value;
|
|
834
877
|
}
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
878
|
+
|
|
879
|
+
// src/lib/patch/const-array.ts
|
|
880
|
+
function insertIntoConstArray(source, patch) {
|
|
881
|
+
const mod = parseModule2(source);
|
|
882
|
+
const array = findConstArray(mod.$ast, patch.constName);
|
|
883
|
+
const wanted = patch.entry[patch.key];
|
|
884
|
+
if (!array || wanted === void 0 || hasEntry(array, patch.key, wanted)) {
|
|
885
|
+
return source;
|
|
886
|
+
}
|
|
887
|
+
return appendEntry(source, array, renderObject(patch.entry));
|
|
840
888
|
}
|
|
841
|
-
function
|
|
842
|
-
|
|
843
|
-
|
|
889
|
+
function constArrayInsertRefusal(source, patch) {
|
|
890
|
+
const mod = parseModule2(source);
|
|
891
|
+
const array = findConstArray(mod.$ast, patch.constName);
|
|
892
|
+
if (!array) {
|
|
893
|
+
return `const-array could not find a module-scope const array named ${patch.constName}`;
|
|
844
894
|
}
|
|
845
|
-
if (
|
|
846
|
-
return
|
|
895
|
+
if (patch.entry[patch.key] === void 0) {
|
|
896
|
+
return `const-array entry has no identity property named ${patch.key}`;
|
|
847
897
|
}
|
|
848
|
-
|
|
849
|
-
|
|
898
|
+
return void 0;
|
|
899
|
+
}
|
|
900
|
+
function removeFromConstArray(source, patch) {
|
|
901
|
+
const mod = parseModule2(source);
|
|
902
|
+
const array = findConstArray(mod.$ast, patch.constName);
|
|
903
|
+
const wanted = patch.entry[patch.key];
|
|
904
|
+
if (!array || wanted === void 0) {
|
|
905
|
+
return source;
|
|
850
906
|
}
|
|
851
|
-
|
|
852
|
-
|
|
907
|
+
const index = array.elements.findIndex(
|
|
908
|
+
(element) => entryValue(element, patch.key) === wanted
|
|
909
|
+
);
|
|
910
|
+
if (index === -1) {
|
|
911
|
+
return source;
|
|
853
912
|
}
|
|
854
|
-
|
|
913
|
+
return removeEntry(source, array, index);
|
|
855
914
|
}
|
|
856
915
|
|
|
857
916
|
// src/lib/patch/diff.ts
|
|
@@ -1211,6 +1270,233 @@ function inferFormatting(source) {
|
|
|
1211
1270
|
};
|
|
1212
1271
|
}
|
|
1213
1272
|
|
|
1273
|
+
// src/lib/patch/nav-entry.ts
|
|
1274
|
+
import { parseModule as parseModule5 } from "magicast";
|
|
1275
|
+
|
|
1276
|
+
// src/lib/patch/named-import.ts
|
|
1277
|
+
import { parseModule as parseModule4 } from "magicast";
|
|
1278
|
+
var PRINT_WIDTH = 80;
|
|
1279
|
+
function ensureNamedImport(source, want) {
|
|
1280
|
+
const program = parseModule4(source).$ast;
|
|
1281
|
+
const declaration = findImport(program, want.from);
|
|
1282
|
+
if (!declaration) {
|
|
1283
|
+
return insertDeclaration(source, program, want);
|
|
1284
|
+
}
|
|
1285
|
+
const named = namedSpecifiers(declaration).map(
|
|
1286
|
+
(specifier) => source.slice(specifier.start, specifier.end)
|
|
1287
|
+
);
|
|
1288
|
+
if (named.includes(want.name)) {
|
|
1289
|
+
return source;
|
|
1290
|
+
}
|
|
1291
|
+
const at = named.findIndex((text4) => localOf(text4) > want.name);
|
|
1292
|
+
const next = at === -1 ? [...named, want.name] : named.toSpliced(at, 0, want.name);
|
|
1293
|
+
return replaceDeclaration(source, declaration, next, want.from);
|
|
1294
|
+
}
|
|
1295
|
+
function removeNamedImport(source, want) {
|
|
1296
|
+
const program = parseModule4(source).$ast;
|
|
1297
|
+
const declaration = findImport(program, want.from);
|
|
1298
|
+
if (!declaration || isReferenced(program, want.name)) {
|
|
1299
|
+
return source;
|
|
1300
|
+
}
|
|
1301
|
+
const specifiers = namedSpecifiers(declaration);
|
|
1302
|
+
const texts = specifiers.map(
|
|
1303
|
+
(specifier) => source.slice(specifier.start, specifier.end)
|
|
1304
|
+
);
|
|
1305
|
+
const at = texts.indexOf(want.name);
|
|
1306
|
+
if (at === -1) {
|
|
1307
|
+
return source;
|
|
1308
|
+
}
|
|
1309
|
+
if (declaration.specifiers.length === 1) {
|
|
1310
|
+
const lineEnd = source.indexOf("\n", declaration.end);
|
|
1311
|
+
return lineEnd === -1 ? source.slice(0, declaration.start) : `${source.slice(0, declaration.start)}${source.slice(lineEnd + 1)}`;
|
|
1312
|
+
}
|
|
1313
|
+
return replaceDeclaration(
|
|
1314
|
+
source,
|
|
1315
|
+
declaration,
|
|
1316
|
+
texts.toSpliced(at, 1),
|
|
1317
|
+
want.from
|
|
1318
|
+
);
|
|
1319
|
+
}
|
|
1320
|
+
function importedElsewhere(source, want) {
|
|
1321
|
+
const program = parseModule4(source).$ast;
|
|
1322
|
+
for (const statement of program.body) {
|
|
1323
|
+
if (statement.type !== "ImportDeclaration") {
|
|
1324
|
+
continue;
|
|
1325
|
+
}
|
|
1326
|
+
const declaration = statement;
|
|
1327
|
+
for (const specifier of declaration.specifiers) {
|
|
1328
|
+
if (localName(specifier) !== want.name) {
|
|
1329
|
+
continue;
|
|
1330
|
+
}
|
|
1331
|
+
const ours = specifier.type === "ImportSpecifier" && importedName(specifier) === want.name && sourceValue(declaration) === want.from;
|
|
1332
|
+
if (!ours) {
|
|
1333
|
+
return true;
|
|
1334
|
+
}
|
|
1335
|
+
}
|
|
1336
|
+
}
|
|
1337
|
+
return false;
|
|
1338
|
+
}
|
|
1339
|
+
function replaceDeclaration(source, declaration, named, from) {
|
|
1340
|
+
const leading = declaration.specifiers.filter((specifier) => specifier.type !== "ImportSpecifier").map((specifier) => source.slice(specifier.start, specifier.end));
|
|
1341
|
+
const text4 = source.slice(declaration.start, declaration.end);
|
|
1342
|
+
const tail = `from ${JSON.stringify(from)}${text4.trimEnd().endsWith(";") ? ";" : ""}`;
|
|
1343
|
+
const keyword = declaration.importKind === "type" ? "import type" : "import";
|
|
1344
|
+
const inline = [...leading, `{ ${named.join(", ")} }`].join(", ");
|
|
1345
|
+
const oneLine = `${keyword} ${inline} ${tail}`;
|
|
1346
|
+
const printed2 = oneLine.length <= PRINT_WIDTH ? oneLine : `${[keyword, ...leading.map((part) => `${part},`)].join(" ")} {
|
|
1347
|
+
${named.map((specifier) => ` ${specifier},
|
|
1348
|
+
`).join("")}} ${tail}`;
|
|
1349
|
+
return `${source.slice(0, declaration.start)}${printed2}${source.slice(declaration.end)}`;
|
|
1350
|
+
}
|
|
1351
|
+
function insertDeclaration(source, program, want) {
|
|
1352
|
+
const statement = `import { ${want.name} } from ${JSON.stringify(want.from)};`;
|
|
1353
|
+
const last = program.body.findLast(
|
|
1354
|
+
(node) => node.type === "ImportDeclaration"
|
|
1355
|
+
);
|
|
1356
|
+
if (!last) {
|
|
1357
|
+
return `${statement}
|
|
1358
|
+
|
|
1359
|
+
${source}`;
|
|
1360
|
+
}
|
|
1361
|
+
return `${source.slice(0, last.end)}
|
|
1362
|
+
${statement}${source.slice(last.end)}`;
|
|
1363
|
+
}
|
|
1364
|
+
function findImport(program, from) {
|
|
1365
|
+
for (const statement of program.body) {
|
|
1366
|
+
if (statement.type !== "ImportDeclaration") {
|
|
1367
|
+
continue;
|
|
1368
|
+
}
|
|
1369
|
+
const declaration = statement;
|
|
1370
|
+
if (declaration.importKind === "type") {
|
|
1371
|
+
continue;
|
|
1372
|
+
}
|
|
1373
|
+
if (sourceValue(declaration) === from) {
|
|
1374
|
+
return declaration;
|
|
1375
|
+
}
|
|
1376
|
+
}
|
|
1377
|
+
return void 0;
|
|
1378
|
+
}
|
|
1379
|
+
function namedSpecifiers(declaration) {
|
|
1380
|
+
return declaration.specifiers.filter(
|
|
1381
|
+
(specifier) => specifier.type === "ImportSpecifier" && specifier.importKind !== "type"
|
|
1382
|
+
);
|
|
1383
|
+
}
|
|
1384
|
+
function localOf(text4) {
|
|
1385
|
+
const [, local] = text4.split(/\s+as\s+/);
|
|
1386
|
+
return (local ?? text4).trim();
|
|
1387
|
+
}
|
|
1388
|
+
function sourceValue(declaration) {
|
|
1389
|
+
const value = declaration.source.value;
|
|
1390
|
+
return typeof value === "string" ? value : void 0;
|
|
1391
|
+
}
|
|
1392
|
+
function importedName(specifier) {
|
|
1393
|
+
return identifierName(specifier.imported);
|
|
1394
|
+
}
|
|
1395
|
+
function localName(specifier) {
|
|
1396
|
+
return identifierName(specifier.local);
|
|
1397
|
+
}
|
|
1398
|
+
function identifierName(node) {
|
|
1399
|
+
if (node?.type === "Identifier") {
|
|
1400
|
+
return node.name;
|
|
1401
|
+
}
|
|
1402
|
+
if (node?.type === "StringLiteral" || node?.type === "Literal") {
|
|
1403
|
+
const value = node.value;
|
|
1404
|
+
return typeof value === "string" ? value : void 0;
|
|
1405
|
+
}
|
|
1406
|
+
return void 0;
|
|
1407
|
+
}
|
|
1408
|
+
|
|
1409
|
+
// src/lib/patch/nav-entry.ts
|
|
1410
|
+
var IDENTITY = "to";
|
|
1411
|
+
function insertNavEntry(source, patch) {
|
|
1412
|
+
const items = findItems(source, patch);
|
|
1413
|
+
const wanted = patch.entry[IDENTITY];
|
|
1414
|
+
if (!items || wanted === void 0 || hasEntry(items, IDENTITY, wanted)) {
|
|
1415
|
+
return source;
|
|
1416
|
+
}
|
|
1417
|
+
if (importedElsewhere(source, patch.icon)) {
|
|
1418
|
+
return source;
|
|
1419
|
+
}
|
|
1420
|
+
const withEntry = appendEntry(source, items, renderItem(patch));
|
|
1421
|
+
return ensureNamedImport(withEntry, patch.icon);
|
|
1422
|
+
}
|
|
1423
|
+
function navEntryInsertRefusal(source, patch) {
|
|
1424
|
+
const program = parseModule5(source).$ast;
|
|
1425
|
+
const areas = findConstArray(program, patch.constName);
|
|
1426
|
+
if (!areas) {
|
|
1427
|
+
return `nav-entry could not find a module-scope const array named ${patch.constName}`;
|
|
1428
|
+
}
|
|
1429
|
+
const area = findElement(areas, IDENTITY, patch.area);
|
|
1430
|
+
if (!area) {
|
|
1431
|
+
return `nav-entry could not find an area with ${IDENTITY} ${JSON.stringify(patch.area)} in ${patch.constName}`;
|
|
1432
|
+
}
|
|
1433
|
+
const groups = arrayProperty(area, "groups");
|
|
1434
|
+
if (!groups) {
|
|
1435
|
+
return `nav-entry found the ${JSON.stringify(patch.area)} area but it has no groups array`;
|
|
1436
|
+
}
|
|
1437
|
+
const group = findElement(groups, "label", patch.group);
|
|
1438
|
+
if (!group) {
|
|
1439
|
+
return `nav-entry could not find a group labelled ${JSON.stringify(patch.group)} in the ${JSON.stringify(patch.area)} area`;
|
|
1440
|
+
}
|
|
1441
|
+
if (!arrayProperty(group, "items")) {
|
|
1442
|
+
return `nav-entry found the ${JSON.stringify(patch.group)} group but it has no items array`;
|
|
1443
|
+
}
|
|
1444
|
+
if (patch.entry[IDENTITY] === void 0) {
|
|
1445
|
+
return `nav-entry entry has no identity property named ${IDENTITY}`;
|
|
1446
|
+
}
|
|
1447
|
+
if (importedElsewhere(source, patch.icon)) {
|
|
1448
|
+
return `${JSON.stringify(patch.icon.name)} is already bound to something other than the named import from ${JSON.stringify(patch.icon.from)}, so the icon would render the wrong component`;
|
|
1449
|
+
}
|
|
1450
|
+
return void 0;
|
|
1451
|
+
}
|
|
1452
|
+
function removeNavEntry(source, patch) {
|
|
1453
|
+
const items = findItems(source, patch);
|
|
1454
|
+
const wanted = patch.entry[IDENTITY];
|
|
1455
|
+
if (!items || wanted === void 0) {
|
|
1456
|
+
return source;
|
|
1457
|
+
}
|
|
1458
|
+
const index = items.elements.findIndex(
|
|
1459
|
+
(element) => entryValue(element, IDENTITY) === wanted
|
|
1460
|
+
);
|
|
1461
|
+
if (index === -1) {
|
|
1462
|
+
return source;
|
|
1463
|
+
}
|
|
1464
|
+
const withoutEntry = removeEntry(source, items, index);
|
|
1465
|
+
return removeNamedImport(withoutEntry, patch.icon);
|
|
1466
|
+
}
|
|
1467
|
+
function navEntryRemoveRefusal(source, patch) {
|
|
1468
|
+
const items = findItems(source, patch);
|
|
1469
|
+
if (!items) {
|
|
1470
|
+
return `nav-entry could not find the ${JSON.stringify(patch.group)} group of the ${JSON.stringify(patch.area)} area in ${patch.constName}`;
|
|
1471
|
+
}
|
|
1472
|
+
return void 0;
|
|
1473
|
+
}
|
|
1474
|
+
function findItems(source, patch) {
|
|
1475
|
+
const program = parseModule5(source).$ast;
|
|
1476
|
+
const areas = findConstArray(program, patch.constName);
|
|
1477
|
+
if (!areas) {
|
|
1478
|
+
return void 0;
|
|
1479
|
+
}
|
|
1480
|
+
const area = findElement(areas, IDENTITY, patch.area);
|
|
1481
|
+
const groups = area && arrayProperty(area, "groups");
|
|
1482
|
+
if (!groups) {
|
|
1483
|
+
return void 0;
|
|
1484
|
+
}
|
|
1485
|
+
const group = findElement(groups, "label", patch.group);
|
|
1486
|
+
return group ? arrayProperty(group, "items") : void 0;
|
|
1487
|
+
}
|
|
1488
|
+
function arrayProperty(object, key) {
|
|
1489
|
+
const value = propertyValue(object, key);
|
|
1490
|
+
return value ? asArray(value) : void 0;
|
|
1491
|
+
}
|
|
1492
|
+
function renderItem(patch) {
|
|
1493
|
+
const properties = [
|
|
1494
|
+
...renderProperties(patch.entry),
|
|
1495
|
+
`${renderKey("icon")}: ${patch.icon.name}`
|
|
1496
|
+
];
|
|
1497
|
+
return `{ ${properties.join(", ")} }`;
|
|
1498
|
+
}
|
|
1499
|
+
|
|
1214
1500
|
// src/lib/patch/pkg-json.ts
|
|
1215
1501
|
import {
|
|
1216
1502
|
applyEdits as applyEdits2,
|
|
@@ -1325,9 +1611,9 @@ function upsertPackageJsonScript(source, patch) {
|
|
|
1325
1611
|
}
|
|
1326
1612
|
|
|
1327
1613
|
// src/lib/patch/ts-module.ts
|
|
1328
|
-
import { builders as builders2, generateCode as generateCode3, parseModule as
|
|
1614
|
+
import { builders as builders2, generateCode as generateCode3, parseModule as parseModule6 } from "magicast";
|
|
1329
1615
|
function insertIntoPluginArray(source, patch) {
|
|
1330
|
-
const mod =
|
|
1616
|
+
const mod = parseModule6(source);
|
|
1331
1617
|
const exported = mod.exports[patch.exportName];
|
|
1332
1618
|
const callArg = exported?.$args?.[0];
|
|
1333
1619
|
if (!callArg) {
|
|
@@ -1353,7 +1639,7 @@ function insertIntoPluginArray(source, patch) {
|
|
|
1353
1639
|
return print2(source, mod);
|
|
1354
1640
|
}
|
|
1355
1641
|
function removeFromPluginArray(source, patch) {
|
|
1356
|
-
const mod =
|
|
1642
|
+
const mod = parseModule6(source);
|
|
1357
1643
|
const exported = mod.exports[patch.exportName];
|
|
1358
1644
|
const callArg = exported?.$args?.[0];
|
|
1359
1645
|
if (!callArg) {
|
|
@@ -1381,7 +1667,7 @@ function removeFromPluginArray(source, patch) {
|
|
|
1381
1667
|
return print2(source, mod);
|
|
1382
1668
|
}
|
|
1383
1669
|
function pluginArrayRemoveRefusal(source, patch) {
|
|
1384
|
-
const mod =
|
|
1670
|
+
const mod = parseModule6(source);
|
|
1385
1671
|
const callArg = mod.exports[patch.exportName]?.$args?.[0];
|
|
1386
1672
|
if (!callArg) {
|
|
1387
1673
|
return void 0;
|
|
@@ -1433,6 +1719,7 @@ var PATCH_KIND_KEYS = {
|
|
|
1433
1719
|
"chained-route": true,
|
|
1434
1720
|
"const-array": true,
|
|
1435
1721
|
"drizzle-column": true,
|
|
1722
|
+
"nav-entry": true,
|
|
1436
1723
|
"package-json-dependency": true,
|
|
1437
1724
|
"package-json-script": true,
|
|
1438
1725
|
"plugin-array": true,
|
|
@@ -1469,6 +1756,9 @@ function applyCodemod(source, patch) {
|
|
|
1469
1756
|
case "const-array": {
|
|
1470
1757
|
return insertIntoConstArray(source, patch);
|
|
1471
1758
|
}
|
|
1759
|
+
case "nav-entry": {
|
|
1760
|
+
return insertNavEntry(source, patch);
|
|
1761
|
+
}
|
|
1472
1762
|
case "drizzle-column": {
|
|
1473
1763
|
return insertDrizzleColumn(source, patch);
|
|
1474
1764
|
}
|
|
@@ -1497,6 +1787,7 @@ var INVERSES = {
|
|
|
1497
1787
|
"chained-route": removeChainedRoute,
|
|
1498
1788
|
"const-array": removeFromConstArray,
|
|
1499
1789
|
"drizzle-column": removeDrizzleColumn,
|
|
1790
|
+
"nav-entry": removeNavEntry,
|
|
1500
1791
|
"plugin-array": removeFromPluginArray,
|
|
1501
1792
|
"wrangler-binding": removeWranglerBinding
|
|
1502
1793
|
};
|
|
@@ -1521,11 +1812,13 @@ var REFUSALS = {
|
|
|
1521
1812
|
"chained-route": chainedRouteInsertRefusal,
|
|
1522
1813
|
"const-array": constArrayInsertRefusal,
|
|
1523
1814
|
"drizzle-column": drizzleColumnInsertRefusal,
|
|
1815
|
+
"nav-entry": navEntryInsertRefusal,
|
|
1524
1816
|
"package-json-script": packageJsonScriptRefusal
|
|
1525
1817
|
};
|
|
1526
1818
|
var REVERSAL_REFUSALS = {
|
|
1527
1819
|
"chained-route": chainedRouteRemoveRefusal,
|
|
1528
1820
|
"drizzle-column": drizzleColumnRemoveRefusal,
|
|
1821
|
+
"nav-entry": navEntryRemoveRefusal,
|
|
1529
1822
|
"plugin-array": pluginArrayRemoveRefusal,
|
|
1530
1823
|
"wrangler-binding": wranglerBindingRemoveRefusal
|
|
1531
1824
|
};
|
|
@@ -1957,11 +2250,12 @@ async function executePlan(plan, root, config, manifest) {
|
|
|
1957
2250
|
} else {
|
|
1958
2251
|
refreshed.push(file);
|
|
1959
2252
|
}
|
|
1960
|
-
manifest
|
|
1961
|
-
|
|
2253
|
+
recordManagedFile(manifest, {
|
|
2254
|
+
from: file.from,
|
|
1962
2255
|
hash: file.newHash,
|
|
1963
|
-
|
|
1964
|
-
|
|
2256
|
+
module: file.module,
|
|
2257
|
+
target: file.target
|
|
2258
|
+
});
|
|
1965
2259
|
}
|
|
1966
2260
|
for (const p of plan.patches) {
|
|
1967
2261
|
if (!await pathExists(p.fileAbs)) {
|
|
@@ -1974,14 +2268,11 @@ async function executePlan(plan, root, config, manifest) {
|
|
|
1974
2268
|
if (changed) {
|
|
1975
2269
|
await writeFile3(p.fileAbs, content, "utf-8");
|
|
1976
2270
|
patched.push(p);
|
|
1977
|
-
|
|
1978
|
-
module: p.module,
|
|
2271
|
+
recordPatch(manifest, {
|
|
1979
2272
|
file: p.file,
|
|
2273
|
+
module: p.module,
|
|
1980
2274
|
patch: p.patch
|
|
1981
|
-
};
|
|
1982
|
-
if (!manifest.patches.some((existing) => samePatchEntry(existing, entry))) {
|
|
1983
|
-
manifest.patches.push(entry);
|
|
1984
|
-
}
|
|
2275
|
+
});
|
|
1985
2276
|
} else if (reason !== void 0) {
|
|
1986
2277
|
patchRefusals.push({ patch: p, reason });
|
|
1987
2278
|
}
|
|
@@ -1995,7 +2286,7 @@ async function executePlan(plan, root, config, manifest) {
|
|
|
1995
2286
|
await assertNoSymlinkPath(root, link.pathAbs);
|
|
1996
2287
|
await createDirLink(link.pathAbs, link.targetAbs);
|
|
1997
2288
|
}
|
|
1998
|
-
manifest
|
|
2289
|
+
recordLink(manifest, link.target, link.path);
|
|
1999
2290
|
links.push(link);
|
|
2000
2291
|
}
|
|
2001
2292
|
for (const [alias, prefix] of Object.entries(plan.aliases)) {
|
|
@@ -2633,6 +2924,24 @@ function upsertLock(lock, provenance, installed, graph) {
|
|
|
2633
2924
|
}
|
|
2634
2925
|
}
|
|
2635
2926
|
|
|
2927
|
+
// src/lib/ledger.ts
|
|
2928
|
+
async function persistLedger(input) {
|
|
2929
|
+
const saves = [
|
|
2930
|
+
[MANIFEST_FILE, () => saveManifest(input.root, input.manifest)],
|
|
2931
|
+
[CONFIG_FILE, () => saveConfig(input.root, input.config)],
|
|
2932
|
+
[LOCK_FILE, () => saveLock(input.root, input.lock)]
|
|
2933
|
+
];
|
|
2934
|
+
const failures = [];
|
|
2935
|
+
for (const [file, save] of saves) {
|
|
2936
|
+
try {
|
|
2937
|
+
await save();
|
|
2938
|
+
} catch (error) {
|
|
2939
|
+
failures.push({ error, file });
|
|
2940
|
+
}
|
|
2941
|
+
}
|
|
2942
|
+
return failures;
|
|
2943
|
+
}
|
|
2944
|
+
|
|
2636
2945
|
// src/lib/pkg-json.ts
|
|
2637
2946
|
import { readFile as readFile8, writeFile as writeFile6 } from "fs/promises";
|
|
2638
2947
|
import { join as join6 } from "path";
|
|
@@ -3478,18 +3787,11 @@ async function persistState(input) {
|
|
|
3478
3787
|
failures.push(error);
|
|
3479
3788
|
log.warn(`Couldn't pin ${LOCK_FILE} \u2014 ${formatFailure(error)}.`);
|
|
3480
3789
|
}
|
|
3481
|
-
const
|
|
3482
|
-
|
|
3483
|
-
|
|
3484
|
-
|
|
3485
|
-
|
|
3486
|
-
for (const [file, save] of saves) {
|
|
3487
|
-
try {
|
|
3488
|
-
await save();
|
|
3489
|
-
} catch (error) {
|
|
3490
|
-
failures.push(error);
|
|
3491
|
-
log.warn(`Couldn't write ${file} \u2014 ${formatFailure(error)}.`);
|
|
3492
|
-
}
|
|
3790
|
+
for (const failure of await persistLedger(input)) {
|
|
3791
|
+
failures.push(failure.error);
|
|
3792
|
+
log.warn(
|
|
3793
|
+
`Couldn't write ${failure.file} \u2014 ${formatFailure(failure.error)}.`
|
|
3794
|
+
);
|
|
3493
3795
|
}
|
|
3494
3796
|
return failures;
|
|
3495
3797
|
}
|
|
@@ -3881,7 +4183,7 @@ import { fileURLToPath as fileURLToPath3 } from "url";
|
|
|
3881
4183
|
// package.json
|
|
3882
4184
|
var package_default = {
|
|
3883
4185
|
name: "saasaloy",
|
|
3884
|
-
version: "0.5.
|
|
4186
|
+
version: "0.5.2",
|
|
3885
4187
|
description: "Composable SaaS accelerator kit.",
|
|
3886
4188
|
keywords: [
|
|
3887
4189
|
"saas",
|
|
@@ -3920,6 +4222,7 @@ var package_default = {
|
|
|
3920
4222
|
},
|
|
3921
4223
|
scripts: {
|
|
3922
4224
|
build: "tsup",
|
|
4225
|
+
clean: "rimraf dist coverage",
|
|
3923
4226
|
prepack: "pnpm -w run readme:cli && pnpm run build",
|
|
3924
4227
|
dev: "tsup --watch",
|
|
3925
4228
|
start: "node ./dist/index.js",
|
|
@@ -3935,6 +4238,7 @@ var package_default = {
|
|
|
3935
4238
|
"@types/node": "26.4.1",
|
|
3936
4239
|
"@vitest/coverage-v8": "5.0.0",
|
|
3937
4240
|
"release-it": "21.0.2",
|
|
4241
|
+
rimraf: "6.1.3",
|
|
3938
4242
|
tsup: "8.5.1",
|
|
3939
4243
|
vitest: "5.0.0"
|
|
3940
4244
|
},
|
|
@@ -4107,16 +4411,17 @@ function baseEntries(manifest) {
|
|
|
4107
4411
|
function recordBaseFiles(manifest, files) {
|
|
4108
4412
|
for (const [target, entry] of Object.entries(manifest.managed)) {
|
|
4109
4413
|
if (entry.module === BASE_MODULE) {
|
|
4110
|
-
|
|
4414
|
+
untrackManagedFile(manifest, target);
|
|
4111
4415
|
}
|
|
4112
4416
|
}
|
|
4113
4417
|
for (const file of files) {
|
|
4114
|
-
manifest
|
|
4115
|
-
|
|
4418
|
+
recordManagedFile(manifest, {
|
|
4419
|
+
adopted: file.adopted,
|
|
4420
|
+
from: file.from,
|
|
4116
4421
|
hash: file.hash,
|
|
4117
|
-
|
|
4118
|
-
|
|
4119
|
-
};
|
|
4422
|
+
module: BASE_MODULE,
|
|
4423
|
+
target: file.target
|
|
4424
|
+
});
|
|
4120
4425
|
}
|
|
4121
4426
|
}
|
|
4122
4427
|
function isBaseTracked(lock, manifest) {
|
|
@@ -4250,7 +4555,7 @@ async function baseUpdateInput(args) {
|
|
|
4250
4555
|
|
|
4251
4556
|
// src/lib/doctor.ts
|
|
4252
4557
|
import { findNodeAtLocation as findNodeAtLocation4, getNodeValue as getNodeValue4, parseTree as parseTree4 } from "jsonc-parser";
|
|
4253
|
-
import { parseModule as
|
|
4558
|
+
import { parseModule as parseModule7 } from "magicast";
|
|
4254
4559
|
import { readFile as readFile13 } from "fs/promises";
|
|
4255
4560
|
import { basename, dirname as dirname6, join as join11, posix as posix7, resolve as resolve3 } from "path";
|
|
4256
4561
|
var SKILL_PREFIX = "saasaloy-";
|
|
@@ -4258,7 +4563,7 @@ var PINNED_DEP = /^(@[^/@]+\/)?[^/@]+@\d+\.\d+\.\d+(-[\w.-]+)?(\+[\w.-]+)?$/;
|
|
|
4258
4563
|
function finding(module, where, message) {
|
|
4259
4564
|
return { message, module, where };
|
|
4260
4565
|
}
|
|
4261
|
-
function
|
|
4566
|
+
function asArray2(value) {
|
|
4262
4567
|
return Array.isArray(value) ? value : [];
|
|
4263
4568
|
}
|
|
4264
4569
|
function asRecord(value) {
|
|
@@ -4282,7 +4587,7 @@ async function collectAliases(registryDir, names) {
|
|
|
4282
4587
|
const fromScaffolds = {};
|
|
4283
4588
|
for (const name of names) {
|
|
4284
4589
|
const item = asRecord(await readDescriptor(join11(registryDir, name)));
|
|
4285
|
-
for (const scaffold of
|
|
4590
|
+
for (const scaffold of asArray2(item.scaffolds)) {
|
|
4286
4591
|
const aliases = asRecord(asRecord(scaffold).aliases);
|
|
4287
4592
|
for (const [alias, prefix] of Object.entries(aliases)) {
|
|
4288
4593
|
if (typeof prefix === "string") {
|
|
@@ -4346,7 +4651,7 @@ async function checkModule(input) {
|
|
|
4346
4651
|
}
|
|
4347
4652
|
const known = { ...aliases.base, ...aliases.fromScaffolds };
|
|
4348
4653
|
const knownList = Object.keys(known).toSorted().join(", ") || "(none)";
|
|
4349
|
-
for (const [index, entry] of
|
|
4654
|
+
for (const [index, entry] of asArray2(item.files).entries()) {
|
|
4350
4655
|
await checkSourcePath(findings, module, dir, `/files/${index}/path`, entry);
|
|
4351
4656
|
const target = asRecord(entry).target;
|
|
4352
4657
|
const alias = typeof target === "string" ? target.split("/")[0] : void 0;
|
|
@@ -4360,8 +4665,8 @@ async function checkModule(input) {
|
|
|
4360
4665
|
);
|
|
4361
4666
|
}
|
|
4362
4667
|
}
|
|
4363
|
-
for (const [index, scaffold] of
|
|
4364
|
-
for (const [fileIndex, entry] of
|
|
4668
|
+
for (const [index, scaffold] of asArray2(item.scaffolds).entries()) {
|
|
4669
|
+
for (const [fileIndex, entry] of asArray2(
|
|
4365
4670
|
asRecord(scaffold).files
|
|
4366
4671
|
).entries()) {
|
|
4367
4672
|
await checkSourcePath(
|
|
@@ -4379,7 +4684,7 @@ async function checkModule(input) {
|
|
|
4379
4684
|
["requiresOneOf", item.requiresOneOf],
|
|
4380
4685
|
["conflictsWith", item.conflictsWith]
|
|
4381
4686
|
]) {
|
|
4382
|
-
for (const [index, name] of
|
|
4687
|
+
for (const [index, name] of asArray2(names).entries()) {
|
|
4383
4688
|
if (typeof name === "string" && !siblingSet.has(name)) {
|
|
4384
4689
|
findings.push(
|
|
4385
4690
|
finding(
|
|
@@ -4395,7 +4700,7 @@ async function checkModule(input) {
|
|
|
4395
4700
|
["dependencies", item.dependencies],
|
|
4396
4701
|
["devDependencies", item.devDependencies]
|
|
4397
4702
|
]) {
|
|
4398
|
-
for (const [index, dep] of
|
|
4703
|
+
for (const [index, dep] of asArray2(deps).entries()) {
|
|
4399
4704
|
if (typeof dep === "string" && !PINNED_DEP.test(dep)) {
|
|
4400
4705
|
findings.push(
|
|
4401
4706
|
finding(
|
|
@@ -4438,7 +4743,7 @@ async function checkModule(input) {
|
|
|
4438
4743
|
);
|
|
4439
4744
|
}
|
|
4440
4745
|
}
|
|
4441
|
-
for (const [index, skill] of
|
|
4746
|
+
for (const [index, skill] of asArray2(asRecord(item.agent).skills).entries()) {
|
|
4442
4747
|
if (typeof skill !== "string") {
|
|
4443
4748
|
continue;
|
|
4444
4749
|
}
|
|
@@ -4561,7 +4866,7 @@ function rateLimitBindingNames(wrangler) {
|
|
|
4561
4866
|
function registeredPolicies(args) {
|
|
4562
4867
|
let mod;
|
|
4563
4868
|
try {
|
|
4564
|
-
mod =
|
|
4869
|
+
mod = parseModule7(args.index);
|
|
4565
4870
|
} catch {
|
|
4566
4871
|
return [];
|
|
4567
4872
|
}
|
|
@@ -4609,7 +4914,7 @@ function calleeName(element) {
|
|
|
4609
4914
|
function policyNameIn(source, callee) {
|
|
4610
4915
|
let program;
|
|
4611
4916
|
try {
|
|
4612
|
-
program =
|
|
4917
|
+
program = parseModule7(source).$ast;
|
|
4613
4918
|
} catch {
|
|
4614
4919
|
return void 0;
|
|
4615
4920
|
}
|
|
@@ -4624,8 +4929,8 @@ function policyNameIn(source, callee) {
|
|
|
4624
4929
|
factory,
|
|
4625
4930
|
(node) => node.type === "CallExpression" && asRecord(node.callee).name === "definePolicy"
|
|
4626
4931
|
);
|
|
4627
|
-
const argument =
|
|
4628
|
-
const property =
|
|
4932
|
+
const argument = asArray2(call?.arguments)[0];
|
|
4933
|
+
const property = asArray2(asRecord(argument).properties).find(
|
|
4629
4934
|
(entry) => asRecord(asRecord(entry).key).name === "name"
|
|
4630
4935
|
);
|
|
4631
4936
|
const value = asRecord(asRecord(property).value).value;
|
|
@@ -4688,7 +4993,7 @@ async function readPolicyState(root, installed) {
|
|
|
4688
4993
|
function relativeImportsIn(index) {
|
|
4689
4994
|
let mod;
|
|
4690
4995
|
try {
|
|
4691
|
-
mod =
|
|
4996
|
+
mod = parseModule7(index);
|
|
4692
4997
|
} catch {
|
|
4693
4998
|
return [];
|
|
4694
4999
|
}
|
|
@@ -6552,7 +6857,7 @@ async function executeRemovePlan(plan, args) {
|
|
|
6552
6857
|
} else {
|
|
6553
6858
|
missingUntracked.push(file);
|
|
6554
6859
|
}
|
|
6555
|
-
|
|
6860
|
+
untrackManagedFile(manifest, file.target);
|
|
6556
6861
|
}
|
|
6557
6862
|
const linksRemoved = [];
|
|
6558
6863
|
const linkConflicts = [];
|
|
@@ -6568,7 +6873,7 @@ async function executeRemovePlan(plan, args) {
|
|
|
6568
6873
|
} else if (link.action === "conflict") {
|
|
6569
6874
|
linkConflicts.push(link);
|
|
6570
6875
|
}
|
|
6571
|
-
|
|
6876
|
+
untrackLink(manifest, link.target);
|
|
6572
6877
|
}
|
|
6573
6878
|
const patchesReversed = [];
|
|
6574
6879
|
const patchesDropped = [];
|
|
@@ -6591,9 +6896,7 @@ async function executeRemovePlan(plan, args) {
|
|
|
6591
6896
|
}
|
|
6592
6897
|
}
|
|
6593
6898
|
(reversed ? patchesReversed : patchesDropped).push(entry);
|
|
6594
|
-
manifest
|
|
6595
|
-
(p) => !samePatchEntry(p, entry)
|
|
6596
|
-
);
|
|
6899
|
+
untrackPatch(manifest, entry);
|
|
6597
6900
|
}
|
|
6598
6901
|
const prunedDirs = await pruneEmptyDirs(root, [
|
|
6599
6902
|
...deleted.map((f) => f.targetAbs),
|
|
@@ -7173,11 +7476,12 @@ async function executeUpdatePlan(plan, args) {
|
|
|
7173
7476
|
await writeFile11(file.targetAbs, file.theirs, "utf-8");
|
|
7174
7477
|
result.written.push(file);
|
|
7175
7478
|
}
|
|
7176
|
-
manifest
|
|
7177
|
-
|
|
7479
|
+
recordManagedFile(manifest, {
|
|
7480
|
+
from: file.from,
|
|
7178
7481
|
hash: file.newHash ?? hashContent(file.theirs),
|
|
7179
|
-
|
|
7180
|
-
|
|
7482
|
+
module: file.module,
|
|
7483
|
+
target: file.target
|
|
7484
|
+
});
|
|
7181
7485
|
}
|
|
7182
7486
|
for (const file of mod.removals) {
|
|
7183
7487
|
if (file.action === "delete") {
|
|
@@ -7193,7 +7497,7 @@ async function executeUpdatePlan(plan, args) {
|
|
|
7193
7497
|
} else {
|
|
7194
7498
|
result.untracked.push(file);
|
|
7195
7499
|
}
|
|
7196
|
-
|
|
7500
|
+
untrackManagedFile(manifest, file.target);
|
|
7197
7501
|
}
|
|
7198
7502
|
for (const p of mod.patches) {
|
|
7199
7503
|
if (!await pathExists(p.fileAbs)) {
|
|
@@ -7208,14 +7512,11 @@ async function executeUpdatePlan(plan, args) {
|
|
|
7208
7512
|
}
|
|
7209
7513
|
await writeFile11(p.fileAbs, content, "utf-8");
|
|
7210
7514
|
result.patched.push(p);
|
|
7211
|
-
|
|
7212
|
-
module: p.module,
|
|
7515
|
+
recordPatch(manifest, {
|
|
7213
7516
|
file: p.file,
|
|
7517
|
+
module: p.module,
|
|
7214
7518
|
patch: p.patch
|
|
7215
|
-
};
|
|
7216
|
-
if (!manifest.patches.some((existing) => samePatchEntry(existing, entry))) {
|
|
7217
|
-
manifest.patches.push(entry);
|
|
7218
|
-
}
|
|
7519
|
+
});
|
|
7219
7520
|
}
|
|
7220
7521
|
for (const link of mod.links) {
|
|
7221
7522
|
if (link.action === "conflict") {
|
|
@@ -7225,7 +7526,7 @@ async function executeUpdatePlan(plan, args) {
|
|
|
7225
7526
|
if (link.action === "create") {
|
|
7226
7527
|
await createDirLink(link.pathAbs, link.targetAbs);
|
|
7227
7528
|
}
|
|
7228
|
-
manifest
|
|
7529
|
+
recordLink(manifest, link.target, link.path);
|
|
7229
7530
|
result.links.push(link);
|
|
7230
7531
|
}
|
|
7231
7532
|
for (const bump of mod.depBumps) {
|
|
@@ -7737,6 +8038,7 @@ async function runRemove(argv) {
|
|
|
7737
8038
|
}
|
|
7738
8039
|
}
|
|
7739
8040
|
let result;
|
|
8041
|
+
let ledgerFailures = [];
|
|
7740
8042
|
try {
|
|
7741
8043
|
result = await executeRemovePlan(plan, {
|
|
7742
8044
|
root,
|
|
@@ -7746,9 +8048,11 @@ async function runRemove(argv) {
|
|
|
7746
8048
|
deleteDrifted
|
|
7747
8049
|
});
|
|
7748
8050
|
} finally {
|
|
7749
|
-
await
|
|
7750
|
-
|
|
7751
|
-
|
|
8051
|
+
ledgerFailures = await persistLedger({ config, lock, manifest, root });
|
|
8052
|
+
}
|
|
8053
|
+
const failedSave = ledgerFailures[0];
|
|
8054
|
+
if (failedSave) {
|
|
8055
|
+
throw failedSave.error;
|
|
7752
8056
|
}
|
|
7753
8057
|
for (const file of result.deleted) {
|
|
7754
8058
|
log6.step(`${pc10.red("delete")} ${file.target}`);
|
|
@@ -8705,7 +9009,11 @@ async function runUpdate(argv) {
|
|
|
8705
9009
|
const skipped = comparisons.filter((c) => c.status !== "outdated");
|
|
8706
9010
|
if (outdated.length === 0) {
|
|
8707
9011
|
if (!preview && recordRefRewrites(lock, comparisons).length > 0) {
|
|
8708
|
-
await
|
|
9012
|
+
const failures2 = await persistLedger({ config, lock, manifest, root });
|
|
9013
|
+
const failedSave2 = failures2[0];
|
|
9014
|
+
if (failedSave2) {
|
|
9015
|
+
throw failedSave2.error;
|
|
9016
|
+
}
|
|
8709
9017
|
}
|
|
8710
9018
|
for (const comparison of skipped) {
|
|
8711
9019
|
log7.info(
|
|
@@ -8932,17 +9240,13 @@ async function runUpdate(argv) {
|
|
|
8932
9240
|
(value) => ({ ok: true, value }),
|
|
8933
9241
|
(error) => ({ ok: false, error })
|
|
8934
9242
|
);
|
|
8935
|
-
const
|
|
8936
|
-
saveManifest(root, manifest),
|
|
8937
|
-
saveConfig(root, config),
|
|
8938
|
-
saveLock(root, lock)
|
|
8939
|
-
]);
|
|
9243
|
+
const failures = await persistLedger({ config, lock, manifest, root });
|
|
8940
9244
|
if (!outcome.ok) {
|
|
8941
9245
|
throw outcome.error;
|
|
8942
9246
|
}
|
|
8943
|
-
const failedSave =
|
|
9247
|
+
const failedSave = failures[0];
|
|
8944
9248
|
if (failedSave) {
|
|
8945
|
-
throw failedSave.
|
|
9249
|
+
throw failedSave.error;
|
|
8946
9250
|
}
|
|
8947
9251
|
const result = outcome.value;
|
|
8948
9252
|
reportResult(result);
|