kintone-migrator 0.24.9 → 0.26.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/index.mjs +1257 -1010
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
package/dist/index.mjs
CHANGED
|
@@ -497,6 +497,236 @@ async function applyAction({ container }) {
|
|
|
497
497
|
});
|
|
498
498
|
}
|
|
499
499
|
//#endregion
|
|
500
|
+
//#region src/lib/deepEqual.ts
|
|
501
|
+
function isArrayEqual(a, b, stack) {
|
|
502
|
+
if (!Array.isArray(b)) return false;
|
|
503
|
+
if (a.length !== b.length) return false;
|
|
504
|
+
for (let i = 0; i < a.length; i++) if (!deepEqualInner(a[i], b[i], stack)) return false;
|
|
505
|
+
return true;
|
|
506
|
+
}
|
|
507
|
+
/**
|
|
508
|
+
* Compare two objects by own enumerable string keys (via `Object.keys`).
|
|
509
|
+
*
|
|
510
|
+
* Note: Custom class instances (e.g. `new Error()`) that pass `isRecord` are
|
|
511
|
+
* accepted. Because `Error.prototype.message` is non-enumerable, two Error
|
|
512
|
+
* objects with different messages will compare as equal. Callers should not
|
|
513
|
+
* rely on this function for types with non-enumerable significant state.
|
|
514
|
+
*/
|
|
515
|
+
function isRecordEqual(a, b, stack) {
|
|
516
|
+
if (!isRecord(b)) return false;
|
|
517
|
+
const keysA = Object.keys(a);
|
|
518
|
+
const keysB = Object.keys(b);
|
|
519
|
+
if (keysA.length !== keysB.length) return false;
|
|
520
|
+
for (const key of keysA) {
|
|
521
|
+
if (!Object.hasOwn(b, key)) return false;
|
|
522
|
+
if (!deepEqualInner(a[key], b[key], stack)) return false;
|
|
523
|
+
}
|
|
524
|
+
return true;
|
|
525
|
+
}
|
|
526
|
+
/**
|
|
527
|
+
* Map keys are compared by reference (SameValueZero), not by deep equality.
|
|
528
|
+
* Two Maps with structurally equal but referentially different object keys
|
|
529
|
+
* will be considered unequal.
|
|
530
|
+
*/
|
|
531
|
+
function isMapEqual$1(a, b, stack) {
|
|
532
|
+
if (!(b instanceof Map)) return false;
|
|
533
|
+
if (a.size !== b.size) return false;
|
|
534
|
+
for (const [key, valA] of a) {
|
|
535
|
+
if (!b.has(key)) return false;
|
|
536
|
+
if (!deepEqualInner(valA, b.get(key), stack)) return false;
|
|
537
|
+
}
|
|
538
|
+
return true;
|
|
539
|
+
}
|
|
540
|
+
function isPrimitiveSet(s) {
|
|
541
|
+
for (const val of s) {
|
|
542
|
+
if (val !== null && typeof val === "object") return false;
|
|
543
|
+
if (typeof val === "function") return false;
|
|
544
|
+
}
|
|
545
|
+
return true;
|
|
546
|
+
}
|
|
547
|
+
function isSetEqual(a, b, stack) {
|
|
548
|
+
if (!(b instanceof Set)) return false;
|
|
549
|
+
if (a.size !== b.size) return false;
|
|
550
|
+
if (isPrimitiveSet(a) && isPrimitiveSet(b)) {
|
|
551
|
+
for (const val of a) if (!b.has(val)) return false;
|
|
552
|
+
return true;
|
|
553
|
+
}
|
|
554
|
+
const remaining = [...b];
|
|
555
|
+
for (const valA of a) {
|
|
556
|
+
const stackLen = stack.length;
|
|
557
|
+
const idx = remaining.findIndex((valB) => {
|
|
558
|
+
const result = deepEqualInner(valA, valB, stack);
|
|
559
|
+
if (!result) stack.length = stackLen;
|
|
560
|
+
return result;
|
|
561
|
+
});
|
|
562
|
+
if (idx === -1) return false;
|
|
563
|
+
remaining.splice(idx, 1);
|
|
564
|
+
}
|
|
565
|
+
return true;
|
|
566
|
+
}
|
|
567
|
+
function compareDateOrRegExp(objA, objB) {
|
|
568
|
+
if (objA instanceof Date && objB instanceof Date) {
|
|
569
|
+
const ta = objA.getTime();
|
|
570
|
+
const tb = objB.getTime();
|
|
571
|
+
return ta === tb || Number.isNaN(ta) && Number.isNaN(tb);
|
|
572
|
+
}
|
|
573
|
+
if (objA instanceof Date || objB instanceof Date) return false;
|
|
574
|
+
if (objA instanceof RegExp && objB instanceof RegExp) return String(objA) === String(objB);
|
|
575
|
+
if (objA instanceof RegExp || objB instanceof RegExp) return false;
|
|
576
|
+
}
|
|
577
|
+
function compareCollectionOrRecord(objA, objB, stack) {
|
|
578
|
+
if (Array.isArray(objA)) return isArrayEqual(objA, objB, stack);
|
|
579
|
+
if (objA instanceof Map) return isMapEqual$1(objA, objB, stack);
|
|
580
|
+
if (objB instanceof Map) return false;
|
|
581
|
+
if (objA instanceof Set) return isSetEqual(objA, objB, stack);
|
|
582
|
+
if (objB instanceof Set) return false;
|
|
583
|
+
if (isRecord(objA)) return isRecordEqual(objA, objB, stack);
|
|
584
|
+
return false;
|
|
585
|
+
}
|
|
586
|
+
function deepEqualInner(a, b, stack) {
|
|
587
|
+
if (a === b) return true;
|
|
588
|
+
if (typeof a === "number" && typeof b === "number" && Number.isNaN(a) && Number.isNaN(b)) return true;
|
|
589
|
+
if (a === null || b === null) return a === b;
|
|
590
|
+
if (typeof a !== typeof b) return false;
|
|
591
|
+
if (typeof a !== "object") return false;
|
|
592
|
+
const objA = a;
|
|
593
|
+
const objB = b;
|
|
594
|
+
const dateRegExpResult = compareDateOrRegExp(objA, objB);
|
|
595
|
+
if (dateRegExpResult !== void 0) return dateRegExpResult;
|
|
596
|
+
for (const [sa, sb] of stack) if (sa === objA && sb === objB) return true;
|
|
597
|
+
stack.push([objA, objB]);
|
|
598
|
+
try {
|
|
599
|
+
return compareCollectionOrRecord(objA, objB, stack);
|
|
600
|
+
} finally {
|
|
601
|
+
stack.pop();
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
/**
|
|
605
|
+
* Deep equality comparison for structured data.
|
|
606
|
+
* Supports primitives, plain objects, arrays, Date, RegExp, Map, and Set.
|
|
607
|
+
*
|
|
608
|
+
* Note: `{ a: undefined }` and `{}` are NOT considered equal. This differs from
|
|
609
|
+
* JSON.stringify behavior but is intentional — explicit undefined properties are
|
|
610
|
+
* semantically distinct from absent properties.
|
|
611
|
+
*
|
|
612
|
+
* Set comparison is order-independent: each element in one set is matched to an
|
|
613
|
+
* element in the other using deep equality (O(n²) for non-primitive elements,
|
|
614
|
+
* O(n) fast path for primitive-only sets).
|
|
615
|
+
*
|
|
616
|
+
* Map keys are compared by reference (SameValueZero), not by deep equality.
|
|
617
|
+
* Two Maps with structurally equal but referentially different object keys
|
|
618
|
+
* will be considered unequal.
|
|
619
|
+
*
|
|
620
|
+
* NaN handling: `deepEqual(NaN, NaN)` returns `true`. Invalid Date objects
|
|
621
|
+
* (whose `getTime()` returns NaN) are also considered equal to each other.
|
|
622
|
+
*
|
|
623
|
+
* Signed zero: `-0` and `0` are considered equal (`-0 === 0` is `true`).
|
|
624
|
+
*
|
|
625
|
+
* Circular reference handling: uses a stack-based pair tracker. When the same
|
|
626
|
+
* `(objA, objB)` pair is encountered again on the current comparison path,
|
|
627
|
+
* structural equality is assumed to break the cycle. Shared references (DAG
|
|
628
|
+
* structures) are correctly handled — the same sub-object appearing in multiple
|
|
629
|
+
* properties does not cause false negatives.
|
|
630
|
+
*/
|
|
631
|
+
function deepEqual(a, b) {
|
|
632
|
+
return deepEqualInner(a, b, []);
|
|
633
|
+
}
|
|
634
|
+
//#endregion
|
|
635
|
+
//#region src/core/domain/diff.ts
|
|
636
|
+
const typeOrder = {
|
|
637
|
+
added: 0,
|
|
638
|
+
modified: 1,
|
|
639
|
+
deleted: 2
|
|
640
|
+
};
|
|
641
|
+
function buildDiffResult(entries, warnings = []) {
|
|
642
|
+
const sorted = [...entries].sort((a, b) => typeOrder[a.type] - typeOrder[b.type]);
|
|
643
|
+
let added = 0;
|
|
644
|
+
let modified = 0;
|
|
645
|
+
let deleted = 0;
|
|
646
|
+
for (const e of sorted) if (e.type === "added") added++;
|
|
647
|
+
else if (e.type === "modified") modified++;
|
|
648
|
+
else deleted++;
|
|
649
|
+
return {
|
|
650
|
+
entries: sorted,
|
|
651
|
+
summary: {
|
|
652
|
+
added,
|
|
653
|
+
modified,
|
|
654
|
+
deleted,
|
|
655
|
+
total: sorted.length
|
|
656
|
+
},
|
|
657
|
+
isEmpty: sorted.length === 0,
|
|
658
|
+
warnings
|
|
659
|
+
};
|
|
660
|
+
}
|
|
661
|
+
//#endregion
|
|
662
|
+
//#region src/core/domain/services/recordDiffDetector.ts
|
|
663
|
+
function detectRecordDiff(localRecord, remoteRecord, callbacks) {
|
|
664
|
+
const entries = [];
|
|
665
|
+
for (const [key, localValue] of Object.entries(localRecord)) if (!Object.hasOwn(remoteRecord, key)) entries.push(callbacks.onAdded(key, localValue));
|
|
666
|
+
else {
|
|
667
|
+
const entry = callbacks.onModified(key, localValue, remoteRecord[key]);
|
|
668
|
+
if (entry !== void 0) entries.push(entry);
|
|
669
|
+
}
|
|
670
|
+
for (const [key, remoteValue] of Object.entries(remoteRecord)) if (!Object.hasOwn(localRecord, key)) entries.push(callbacks.onDeleted(key, remoteValue));
|
|
671
|
+
return entries;
|
|
672
|
+
}
|
|
673
|
+
//#endregion
|
|
674
|
+
//#region src/core/domain/action/services/diffDetector.ts
|
|
675
|
+
function compareActions$1(local, remote) {
|
|
676
|
+
const diffs = [];
|
|
677
|
+
if (local.index !== remote.index) diffs.push(`index: ${remote.index} -> ${local.index}`);
|
|
678
|
+
if (!deepEqual(local.destApp, remote.destApp)) diffs.push("destApp changed");
|
|
679
|
+
if (local.filterCond !== remote.filterCond) diffs.push("filterCond changed");
|
|
680
|
+
if (!deepEqual(local.mappings, remote.mappings)) if (local.mappings.length !== remote.mappings.length) diffs.push(`mappings: ${remote.mappings.length} -> ${local.mappings.length}`);
|
|
681
|
+
else diffs.push("mappings changed");
|
|
682
|
+
if (!deepEqual(local.entities, remote.entities)) diffs.push("entities changed");
|
|
683
|
+
return diffs;
|
|
684
|
+
}
|
|
685
|
+
function destAppLabel(action) {
|
|
686
|
+
return `dest: ${action.destApp.app ?? action.destApp.code ?? "(unspecified)"}`;
|
|
687
|
+
}
|
|
688
|
+
const ActionDiffDetector = { detect: (local, remote) => {
|
|
689
|
+
return buildDiffResult(detectRecordDiff(local.actions, remote.actions, {
|
|
690
|
+
onAdded: (name, localAction) => ({
|
|
691
|
+
type: "added",
|
|
692
|
+
actionName: name,
|
|
693
|
+
details: destAppLabel(localAction)
|
|
694
|
+
}),
|
|
695
|
+
onModified: (name, localAction, remoteAction) => {
|
|
696
|
+
const diffs = compareActions$1(localAction, remoteAction);
|
|
697
|
+
if (diffs.length > 0) return {
|
|
698
|
+
type: "modified",
|
|
699
|
+
actionName: name,
|
|
700
|
+
details: diffs.join(", ")
|
|
701
|
+
};
|
|
702
|
+
},
|
|
703
|
+
onDeleted: (name, remoteAction) => ({
|
|
704
|
+
type: "deleted",
|
|
705
|
+
actionName: name,
|
|
706
|
+
details: destAppLabel(remoteAction)
|
|
707
|
+
})
|
|
708
|
+
}));
|
|
709
|
+
} };
|
|
710
|
+
//#endregion
|
|
711
|
+
//#region src/core/application/detectDiffBase.ts
|
|
712
|
+
async function detectDiffFromConfig(config) {
|
|
713
|
+
const [storageResult, remote] = await Promise.all([config.getStorage(), config.fetchRemote()]);
|
|
714
|
+
if (!storageResult.exists) throw new ValidationError(ValidationErrorCode.InvalidInput, config.notFoundMessage);
|
|
715
|
+
const local = config.parseConfig(storageResult.content);
|
|
716
|
+
return config.detect(local, remote);
|
|
717
|
+
}
|
|
718
|
+
//#endregion
|
|
719
|
+
//#region src/core/application/action/detectActionDiff.ts
|
|
720
|
+
async function detectActionDiff({ container }) {
|
|
721
|
+
return detectDiffFromConfig({
|
|
722
|
+
getStorage: () => container.actionStorage.get(),
|
|
723
|
+
fetchRemote: () => container.actionConfigurator.getActions(),
|
|
724
|
+
parseConfig: (content) => parseActionConfigText(container.configCodec, content),
|
|
725
|
+
detect: (local, remote) => ActionDiffDetector.detect(local, { actions: remote.actions }),
|
|
726
|
+
notFoundMessage: "Action config file not found"
|
|
727
|
+
});
|
|
728
|
+
}
|
|
729
|
+
//#endregion
|
|
500
730
|
//#region src/core/adapters/kintone/wrapKintoneError.ts
|
|
501
731
|
const KINTONE_REVISION_CONFLICT_CODE = "GAIA_CO02";
|
|
502
732
|
const KINTONE_MAINTENANCE_MODE_CODE = "GAIA_NO02";
|
|
@@ -2508,9 +2738,27 @@ const { resolveFilePath: resolveActionFilePath, resolveContainerConfig: resolveA
|
|
|
2508
2738
|
//#endregion
|
|
2509
2739
|
//#region src/cli/commands/applyCommandFactory.ts
|
|
2510
2740
|
function createApplyCommand(config) {
|
|
2511
|
-
async function
|
|
2741
|
+
async function runDiffPreview(containerConfig, diffPreview) {
|
|
2512
2742
|
const container = config.createContainer(containerConfig);
|
|
2513
2743
|
const s = p.spinner();
|
|
2744
|
+
s.start("Detecting changes...");
|
|
2745
|
+
let result;
|
|
2746
|
+
try {
|
|
2747
|
+
result = await diffPreview.detectDiff({ container });
|
|
2748
|
+
} catch (error) {
|
|
2749
|
+
s.stop("Comparison failed.");
|
|
2750
|
+
throw error;
|
|
2751
|
+
}
|
|
2752
|
+
s.stop("Comparison complete.");
|
|
2753
|
+
diffPreview.printResult(result);
|
|
2754
|
+
return {
|
|
2755
|
+
container,
|
|
2756
|
+
hasChanges: !result.isEmpty
|
|
2757
|
+
};
|
|
2758
|
+
}
|
|
2759
|
+
async function runApply(containerConfig, existingContainer) {
|
|
2760
|
+
const container = existingContainer ?? config.createContainer(containerConfig);
|
|
2761
|
+
const s = p.spinner();
|
|
2514
2762
|
s.start(config.spinnerMessage);
|
|
2515
2763
|
let result;
|
|
2516
2764
|
try {
|
|
@@ -2537,21 +2785,91 @@ function createApplyCommand(config) {
|
|
|
2537
2785
|
const skipConfirm = ctx.values.yes === true;
|
|
2538
2786
|
await routeMultiApp(values, {
|
|
2539
2787
|
singleLegacy: async () => {
|
|
2540
|
-
|
|
2788
|
+
const containerConfig = config.resolveContainerConfig(values);
|
|
2789
|
+
if (config.diffPreview) {
|
|
2790
|
+
const { container, hasChanges } = await runDiffPreview(containerConfig, config.diffPreview);
|
|
2791
|
+
if (!hasChanges) {
|
|
2792
|
+
p.log.success("No changes detected.");
|
|
2793
|
+
return;
|
|
2794
|
+
}
|
|
2795
|
+
if (!skipConfirm) {
|
|
2796
|
+
const shouldContinue = await p.confirm({ message: "Apply these changes?" });
|
|
2797
|
+
if (p.isCancel(shouldContinue) || !shouldContinue) {
|
|
2798
|
+
p.cancel("Apply cancelled.");
|
|
2799
|
+
return;
|
|
2800
|
+
}
|
|
2801
|
+
}
|
|
2802
|
+
await confirmAndDeploy([await runApply(containerConfig, container)], skipConfirm);
|
|
2803
|
+
} else await confirmAndDeploy([await runApply(containerConfig)], skipConfirm);
|
|
2541
2804
|
},
|
|
2542
2805
|
singleApp: async (app, projectConfig) => {
|
|
2543
|
-
|
|
2806
|
+
const containerConfig = config.resolveAppContainerConfig(app, projectConfig, values);
|
|
2807
|
+
if (config.diffPreview) {
|
|
2808
|
+
const { container, hasChanges } = await runDiffPreview(containerConfig, config.diffPreview);
|
|
2809
|
+
if (!hasChanges) {
|
|
2810
|
+
p.log.success("No changes detected.");
|
|
2811
|
+
return;
|
|
2812
|
+
}
|
|
2813
|
+
if (!skipConfirm) {
|
|
2814
|
+
const shouldContinue = await p.confirm({ message: "Apply these changes?" });
|
|
2815
|
+
if (p.isCancel(shouldContinue) || !shouldContinue) {
|
|
2816
|
+
p.cancel("Apply cancelled.");
|
|
2817
|
+
return;
|
|
2818
|
+
}
|
|
2819
|
+
}
|
|
2820
|
+
await confirmAndDeploy([await runApply(containerConfig, container)], skipConfirm);
|
|
2821
|
+
} else await confirmAndDeploy([await runApply(containerConfig)], skipConfirm);
|
|
2544
2822
|
},
|
|
2545
2823
|
multiApp: async (plan, projectConfig) => {
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
const
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2824
|
+
if (config.diffPreview) {
|
|
2825
|
+
const appDiffResults = [];
|
|
2826
|
+
for (const app of plan.orderedApps) {
|
|
2827
|
+
const containerConfig = config.resolveAppContainerConfig(app, projectConfig, values);
|
|
2828
|
+
printAppHeader(app.name, app.appId);
|
|
2829
|
+
const { container, hasChanges } = await runDiffPreview(containerConfig, config.diffPreview);
|
|
2830
|
+
appDiffResults.push({
|
|
2831
|
+
app,
|
|
2832
|
+
container,
|
|
2833
|
+
hasChanges
|
|
2834
|
+
});
|
|
2835
|
+
}
|
|
2836
|
+
if (!appDiffResults.some((a) => a.hasChanges)) {
|
|
2837
|
+
p.log.success("No changes detected in any app.");
|
|
2838
|
+
return;
|
|
2839
|
+
}
|
|
2840
|
+
if (!skipConfirm) {
|
|
2841
|
+
const shouldContinue = await p.confirm({ message: "Apply these changes to all apps?" });
|
|
2842
|
+
if (p.isCancel(shouldContinue) || !shouldContinue) {
|
|
2843
|
+
p.cancel("Apply cancelled.");
|
|
2844
|
+
return;
|
|
2845
|
+
}
|
|
2846
|
+
}
|
|
2847
|
+
const containers = [];
|
|
2848
|
+
await runMultiAppWithHeaders(plan, async (app) => {
|
|
2849
|
+
const entry = appDiffResults.find((a) => a.app.name === app.name);
|
|
2850
|
+
if (!entry) throw new SystemError(SystemErrorCode.InternalServerError, `App container not found for "${app.name}"`);
|
|
2851
|
+
if (!entry.hasChanges) {
|
|
2852
|
+
p.log.info("No changes. Skipping.");
|
|
2853
|
+
return;
|
|
2854
|
+
}
|
|
2855
|
+
await runApply(config.resolveAppContainerConfig(app, projectConfig, values), entry.container);
|
|
2856
|
+
containers.push({
|
|
2857
|
+
appDeployer: entry.container.appDeployer,
|
|
2858
|
+
appName: app.name
|
|
2859
|
+
});
|
|
2552
2860
|
});
|
|
2553
|
-
|
|
2554
|
-
|
|
2861
|
+
await confirmAndDeploy(containers, skipConfirm);
|
|
2862
|
+
} else {
|
|
2863
|
+
const containers = [];
|
|
2864
|
+
await runMultiAppWithHeaders(plan, async (app) => {
|
|
2865
|
+
const container = await runApply(config.resolveAppContainerConfig(app, projectConfig, values));
|
|
2866
|
+
containers.push({
|
|
2867
|
+
appDeployer: container.appDeployer,
|
|
2868
|
+
appName: app.name
|
|
2869
|
+
});
|
|
2870
|
+
});
|
|
2871
|
+
await confirmAndDeploy(containers, skipConfirm);
|
|
2872
|
+
}
|
|
2555
2873
|
}
|
|
2556
2874
|
});
|
|
2557
2875
|
} catch (error) {
|
|
@@ -2570,6 +2888,10 @@ var apply_default$12 = createApplyCommand({
|
|
|
2570
2888
|
successMessage: "Action settings applied successfully.",
|
|
2571
2889
|
createContainer: createActionCliContainer,
|
|
2572
2890
|
applyFn: applyAction,
|
|
2891
|
+
diffPreview: {
|
|
2892
|
+
detectDiff: detectActionDiff,
|
|
2893
|
+
printResult: printActionDiffResult
|
|
2894
|
+
},
|
|
2573
2895
|
resolveContainerConfig: resolveActionContainerConfig,
|
|
2574
2896
|
resolveAppContainerConfig: resolveActionAppContainerConfig
|
|
2575
2897
|
});
|
|
@@ -2710,236 +3032,6 @@ var capture_default$13 = createCaptureCommand({
|
|
|
2710
3032
|
resolveAppContainerConfig: resolveActionAppContainerConfig
|
|
2711
3033
|
});
|
|
2712
3034
|
//#endregion
|
|
2713
|
-
//#region src/lib/deepEqual.ts
|
|
2714
|
-
function isArrayEqual(a, b, stack) {
|
|
2715
|
-
if (!Array.isArray(b)) return false;
|
|
2716
|
-
if (a.length !== b.length) return false;
|
|
2717
|
-
for (let i = 0; i < a.length; i++) if (!deepEqualInner(a[i], b[i], stack)) return false;
|
|
2718
|
-
return true;
|
|
2719
|
-
}
|
|
2720
|
-
/**
|
|
2721
|
-
* Compare two objects by own enumerable string keys (via `Object.keys`).
|
|
2722
|
-
*
|
|
2723
|
-
* Note: Custom class instances (e.g. `new Error()`) that pass `isRecord` are
|
|
2724
|
-
* accepted. Because `Error.prototype.message` is non-enumerable, two Error
|
|
2725
|
-
* objects with different messages will compare as equal. Callers should not
|
|
2726
|
-
* rely on this function for types with non-enumerable significant state.
|
|
2727
|
-
*/
|
|
2728
|
-
function isRecordEqual(a, b, stack) {
|
|
2729
|
-
if (!isRecord(b)) return false;
|
|
2730
|
-
const keysA = Object.keys(a);
|
|
2731
|
-
const keysB = Object.keys(b);
|
|
2732
|
-
if (keysA.length !== keysB.length) return false;
|
|
2733
|
-
for (const key of keysA) {
|
|
2734
|
-
if (!Object.hasOwn(b, key)) return false;
|
|
2735
|
-
if (!deepEqualInner(a[key], b[key], stack)) return false;
|
|
2736
|
-
}
|
|
2737
|
-
return true;
|
|
2738
|
-
}
|
|
2739
|
-
/**
|
|
2740
|
-
* Map keys are compared by reference (SameValueZero), not by deep equality.
|
|
2741
|
-
* Two Maps with structurally equal but referentially different object keys
|
|
2742
|
-
* will be considered unequal.
|
|
2743
|
-
*/
|
|
2744
|
-
function isMapEqual$1(a, b, stack) {
|
|
2745
|
-
if (!(b instanceof Map)) return false;
|
|
2746
|
-
if (a.size !== b.size) return false;
|
|
2747
|
-
for (const [key, valA] of a) {
|
|
2748
|
-
if (!b.has(key)) return false;
|
|
2749
|
-
if (!deepEqualInner(valA, b.get(key), stack)) return false;
|
|
2750
|
-
}
|
|
2751
|
-
return true;
|
|
2752
|
-
}
|
|
2753
|
-
function isPrimitiveSet(s) {
|
|
2754
|
-
for (const val of s) {
|
|
2755
|
-
if (val !== null && typeof val === "object") return false;
|
|
2756
|
-
if (typeof val === "function") return false;
|
|
2757
|
-
}
|
|
2758
|
-
return true;
|
|
2759
|
-
}
|
|
2760
|
-
function isSetEqual(a, b, stack) {
|
|
2761
|
-
if (!(b instanceof Set)) return false;
|
|
2762
|
-
if (a.size !== b.size) return false;
|
|
2763
|
-
if (isPrimitiveSet(a) && isPrimitiveSet(b)) {
|
|
2764
|
-
for (const val of a) if (!b.has(val)) return false;
|
|
2765
|
-
return true;
|
|
2766
|
-
}
|
|
2767
|
-
const remaining = [...b];
|
|
2768
|
-
for (const valA of a) {
|
|
2769
|
-
const stackLen = stack.length;
|
|
2770
|
-
const idx = remaining.findIndex((valB) => {
|
|
2771
|
-
const result = deepEqualInner(valA, valB, stack);
|
|
2772
|
-
if (!result) stack.length = stackLen;
|
|
2773
|
-
return result;
|
|
2774
|
-
});
|
|
2775
|
-
if (idx === -1) return false;
|
|
2776
|
-
remaining.splice(idx, 1);
|
|
2777
|
-
}
|
|
2778
|
-
return true;
|
|
2779
|
-
}
|
|
2780
|
-
function compareDateOrRegExp(objA, objB) {
|
|
2781
|
-
if (objA instanceof Date && objB instanceof Date) {
|
|
2782
|
-
const ta = objA.getTime();
|
|
2783
|
-
const tb = objB.getTime();
|
|
2784
|
-
return ta === tb || Number.isNaN(ta) && Number.isNaN(tb);
|
|
2785
|
-
}
|
|
2786
|
-
if (objA instanceof Date || objB instanceof Date) return false;
|
|
2787
|
-
if (objA instanceof RegExp && objB instanceof RegExp) return String(objA) === String(objB);
|
|
2788
|
-
if (objA instanceof RegExp || objB instanceof RegExp) return false;
|
|
2789
|
-
}
|
|
2790
|
-
function compareCollectionOrRecord(objA, objB, stack) {
|
|
2791
|
-
if (Array.isArray(objA)) return isArrayEqual(objA, objB, stack);
|
|
2792
|
-
if (objA instanceof Map) return isMapEqual$1(objA, objB, stack);
|
|
2793
|
-
if (objB instanceof Map) return false;
|
|
2794
|
-
if (objA instanceof Set) return isSetEqual(objA, objB, stack);
|
|
2795
|
-
if (objB instanceof Set) return false;
|
|
2796
|
-
if (isRecord(objA)) return isRecordEqual(objA, objB, stack);
|
|
2797
|
-
return false;
|
|
2798
|
-
}
|
|
2799
|
-
function deepEqualInner(a, b, stack) {
|
|
2800
|
-
if (a === b) return true;
|
|
2801
|
-
if (typeof a === "number" && typeof b === "number" && Number.isNaN(a) && Number.isNaN(b)) return true;
|
|
2802
|
-
if (a === null || b === null) return a === b;
|
|
2803
|
-
if (typeof a !== typeof b) return false;
|
|
2804
|
-
if (typeof a !== "object") return false;
|
|
2805
|
-
const objA = a;
|
|
2806
|
-
const objB = b;
|
|
2807
|
-
const dateRegExpResult = compareDateOrRegExp(objA, objB);
|
|
2808
|
-
if (dateRegExpResult !== void 0) return dateRegExpResult;
|
|
2809
|
-
for (const [sa, sb] of stack) if (sa === objA && sb === objB) return true;
|
|
2810
|
-
stack.push([objA, objB]);
|
|
2811
|
-
try {
|
|
2812
|
-
return compareCollectionOrRecord(objA, objB, stack);
|
|
2813
|
-
} finally {
|
|
2814
|
-
stack.pop();
|
|
2815
|
-
}
|
|
2816
|
-
}
|
|
2817
|
-
/**
|
|
2818
|
-
* Deep equality comparison for structured data.
|
|
2819
|
-
* Supports primitives, plain objects, arrays, Date, RegExp, Map, and Set.
|
|
2820
|
-
*
|
|
2821
|
-
* Note: `{ a: undefined }` and `{}` are NOT considered equal. This differs from
|
|
2822
|
-
* JSON.stringify behavior but is intentional — explicit undefined properties are
|
|
2823
|
-
* semantically distinct from absent properties.
|
|
2824
|
-
*
|
|
2825
|
-
* Set comparison is order-independent: each element in one set is matched to an
|
|
2826
|
-
* element in the other using deep equality (O(n²) for non-primitive elements,
|
|
2827
|
-
* O(n) fast path for primitive-only sets).
|
|
2828
|
-
*
|
|
2829
|
-
* Map keys are compared by reference (SameValueZero), not by deep equality.
|
|
2830
|
-
* Two Maps with structurally equal but referentially different object keys
|
|
2831
|
-
* will be considered unequal.
|
|
2832
|
-
*
|
|
2833
|
-
* NaN handling: `deepEqual(NaN, NaN)` returns `true`. Invalid Date objects
|
|
2834
|
-
* (whose `getTime()` returns NaN) are also considered equal to each other.
|
|
2835
|
-
*
|
|
2836
|
-
* Signed zero: `-0` and `0` are considered equal (`-0 === 0` is `true`).
|
|
2837
|
-
*
|
|
2838
|
-
* Circular reference handling: uses a stack-based pair tracker. When the same
|
|
2839
|
-
* `(objA, objB)` pair is encountered again on the current comparison path,
|
|
2840
|
-
* structural equality is assumed to break the cycle. Shared references (DAG
|
|
2841
|
-
* structures) are correctly handled — the same sub-object appearing in multiple
|
|
2842
|
-
* properties does not cause false negatives.
|
|
2843
|
-
*/
|
|
2844
|
-
function deepEqual(a, b) {
|
|
2845
|
-
return deepEqualInner(a, b, []);
|
|
2846
|
-
}
|
|
2847
|
-
//#endregion
|
|
2848
|
-
//#region src/core/domain/diff.ts
|
|
2849
|
-
const typeOrder = {
|
|
2850
|
-
added: 0,
|
|
2851
|
-
modified: 1,
|
|
2852
|
-
deleted: 2
|
|
2853
|
-
};
|
|
2854
|
-
function buildDiffResult(entries, warnings = []) {
|
|
2855
|
-
const sorted = [...entries].sort((a, b) => typeOrder[a.type] - typeOrder[b.type]);
|
|
2856
|
-
let added = 0;
|
|
2857
|
-
let modified = 0;
|
|
2858
|
-
let deleted = 0;
|
|
2859
|
-
for (const e of sorted) if (e.type === "added") added++;
|
|
2860
|
-
else if (e.type === "modified") modified++;
|
|
2861
|
-
else deleted++;
|
|
2862
|
-
return {
|
|
2863
|
-
entries: sorted,
|
|
2864
|
-
summary: {
|
|
2865
|
-
added,
|
|
2866
|
-
modified,
|
|
2867
|
-
deleted,
|
|
2868
|
-
total: sorted.length
|
|
2869
|
-
},
|
|
2870
|
-
isEmpty: sorted.length === 0,
|
|
2871
|
-
warnings
|
|
2872
|
-
};
|
|
2873
|
-
}
|
|
2874
|
-
//#endregion
|
|
2875
|
-
//#region src/core/domain/services/recordDiffDetector.ts
|
|
2876
|
-
function detectRecordDiff(localRecord, remoteRecord, callbacks) {
|
|
2877
|
-
const entries = [];
|
|
2878
|
-
for (const [key, localValue] of Object.entries(localRecord)) if (!Object.hasOwn(remoteRecord, key)) entries.push(callbacks.onAdded(key, localValue));
|
|
2879
|
-
else {
|
|
2880
|
-
const entry = callbacks.onModified(key, localValue, remoteRecord[key]);
|
|
2881
|
-
if (entry !== void 0) entries.push(entry);
|
|
2882
|
-
}
|
|
2883
|
-
for (const [key, remoteValue] of Object.entries(remoteRecord)) if (!Object.hasOwn(localRecord, key)) entries.push(callbacks.onDeleted(key, remoteValue));
|
|
2884
|
-
return entries;
|
|
2885
|
-
}
|
|
2886
|
-
//#endregion
|
|
2887
|
-
//#region src/core/domain/action/services/diffDetector.ts
|
|
2888
|
-
function compareActions$1(local, remote) {
|
|
2889
|
-
const diffs = [];
|
|
2890
|
-
if (local.index !== remote.index) diffs.push(`index: ${remote.index} -> ${local.index}`);
|
|
2891
|
-
if (!deepEqual(local.destApp, remote.destApp)) diffs.push("destApp changed");
|
|
2892
|
-
if (local.filterCond !== remote.filterCond) diffs.push("filterCond changed");
|
|
2893
|
-
if (!deepEqual(local.mappings, remote.mappings)) if (local.mappings.length !== remote.mappings.length) diffs.push(`mappings: ${remote.mappings.length} -> ${local.mappings.length}`);
|
|
2894
|
-
else diffs.push("mappings changed");
|
|
2895
|
-
if (!deepEqual(local.entities, remote.entities)) diffs.push("entities changed");
|
|
2896
|
-
return diffs;
|
|
2897
|
-
}
|
|
2898
|
-
function destAppLabel(action) {
|
|
2899
|
-
return `dest: ${action.destApp.app ?? action.destApp.code ?? "(unspecified)"}`;
|
|
2900
|
-
}
|
|
2901
|
-
const ActionDiffDetector = { detect: (local, remote) => {
|
|
2902
|
-
return buildDiffResult(detectRecordDiff(local.actions, remote.actions, {
|
|
2903
|
-
onAdded: (name, localAction) => ({
|
|
2904
|
-
type: "added",
|
|
2905
|
-
actionName: name,
|
|
2906
|
-
details: destAppLabel(localAction)
|
|
2907
|
-
}),
|
|
2908
|
-
onModified: (name, localAction, remoteAction) => {
|
|
2909
|
-
const diffs = compareActions$1(localAction, remoteAction);
|
|
2910
|
-
if (diffs.length > 0) return {
|
|
2911
|
-
type: "modified",
|
|
2912
|
-
actionName: name,
|
|
2913
|
-
details: diffs.join(", ")
|
|
2914
|
-
};
|
|
2915
|
-
},
|
|
2916
|
-
onDeleted: (name, remoteAction) => ({
|
|
2917
|
-
type: "deleted",
|
|
2918
|
-
actionName: name,
|
|
2919
|
-
details: destAppLabel(remoteAction)
|
|
2920
|
-
})
|
|
2921
|
-
}));
|
|
2922
|
-
} };
|
|
2923
|
-
//#endregion
|
|
2924
|
-
//#region src/core/application/detectDiffBase.ts
|
|
2925
|
-
async function detectDiffFromConfig(config) {
|
|
2926
|
-
const [storageResult, remote] = await Promise.all([config.getStorage(), config.fetchRemote()]);
|
|
2927
|
-
if (!storageResult.exists) throw new ValidationError(ValidationErrorCode.InvalidInput, config.notFoundMessage);
|
|
2928
|
-
const local = config.parseConfig(storageResult.content);
|
|
2929
|
-
return config.detect(local, remote);
|
|
2930
|
-
}
|
|
2931
|
-
//#endregion
|
|
2932
|
-
//#region src/core/application/action/detectActionDiff.ts
|
|
2933
|
-
async function detectActionDiff({ container }) {
|
|
2934
|
-
return detectDiffFromConfig({
|
|
2935
|
-
getStorage: () => container.actionStorage.get(),
|
|
2936
|
-
fetchRemote: () => container.actionConfigurator.getActions(),
|
|
2937
|
-
parseConfig: (content) => parseActionConfigText(container.configCodec, content),
|
|
2938
|
-
detect: (local, remote) => ActionDiffDetector.detect(local, { actions: remote.actions }),
|
|
2939
|
-
notFoundMessage: "Action config file not found"
|
|
2940
|
-
});
|
|
2941
|
-
}
|
|
2942
|
-
//#endregion
|
|
2943
3035
|
//#region src/cli/commands/diffCommandFactory.ts
|
|
2944
3036
|
function createDiffCommand(config) {
|
|
2945
3037
|
async function runDiff(containerConfig) {
|
|
@@ -3040,6 +3132,41 @@ async function applyAdminNotes({ container }) {
|
|
|
3040
3132
|
});
|
|
3041
3133
|
}
|
|
3042
3134
|
//#endregion
|
|
3135
|
+
//#region src/core/domain/adminNotes/services/diffDetector.ts
|
|
3136
|
+
const CONTENT_TRUNCATE_LENGTH = 30;
|
|
3137
|
+
function truncate(text, maxLength) {
|
|
3138
|
+
if (text.length <= maxLength) return text;
|
|
3139
|
+
return `${text.slice(0, maxLength)}...`;
|
|
3140
|
+
}
|
|
3141
|
+
function compareConfigs$2(local, remote) {
|
|
3142
|
+
const entries = [];
|
|
3143
|
+
if (local.content !== remote.content) entries.push({
|
|
3144
|
+
type: "modified",
|
|
3145
|
+
field: "content",
|
|
3146
|
+
details: `"${truncate(remote.content, CONTENT_TRUNCATE_LENGTH)}" -> "${truncate(local.content, CONTENT_TRUNCATE_LENGTH)}"`
|
|
3147
|
+
});
|
|
3148
|
+
if (local.includeInTemplateAndDuplicates !== remote.includeInTemplateAndDuplicates) entries.push({
|
|
3149
|
+
type: "modified",
|
|
3150
|
+
field: "includeInTemplateAndDuplicates",
|
|
3151
|
+
details: `${String(remote.includeInTemplateAndDuplicates)} -> ${String(local.includeInTemplateAndDuplicates)}`
|
|
3152
|
+
});
|
|
3153
|
+
return entries;
|
|
3154
|
+
}
|
|
3155
|
+
const AdminNotesDiffDetector = { detect: (local, remote) => {
|
|
3156
|
+
return buildDiffResult(compareConfigs$2(local, remote));
|
|
3157
|
+
} };
|
|
3158
|
+
//#endregion
|
|
3159
|
+
//#region src/core/application/adminNotes/detectAdminNotesDiff.ts
|
|
3160
|
+
async function detectAdminNotesDiff({ container }) {
|
|
3161
|
+
return detectDiffFromConfig({
|
|
3162
|
+
getStorage: () => container.adminNotesStorage.get(),
|
|
3163
|
+
fetchRemote: () => container.adminNotesConfigurator.getAdminNotes(),
|
|
3164
|
+
parseConfig: (content) => parseAdminNotesConfigText(container.configCodec, content),
|
|
3165
|
+
detect: (local, remote) => AdminNotesDiffDetector.detect(local, remote.config),
|
|
3166
|
+
notFoundMessage: "Admin notes config file not found"
|
|
3167
|
+
});
|
|
3168
|
+
}
|
|
3169
|
+
//#endregion
|
|
3043
3170
|
//#region src/core/adapters/kintone/adminNotesConfigurator.ts
|
|
3044
3171
|
var KintoneAdminNotesConfigurator = class {
|
|
3045
3172
|
constructor(client, appId) {
|
|
@@ -3124,6 +3251,10 @@ var apply_default$11 = createApplyCommand({
|
|
|
3124
3251
|
successMessage: "Admin notes applied successfully.",
|
|
3125
3252
|
createContainer: createAdminNotesCliContainer,
|
|
3126
3253
|
applyFn: applyAdminNotes,
|
|
3254
|
+
diffPreview: {
|
|
3255
|
+
detectDiff: detectAdminNotesDiff,
|
|
3256
|
+
printResult: printAdminNotesDiffResult
|
|
3257
|
+
},
|
|
3127
3258
|
resolveContainerConfig: resolveAdminNotesContainerConfig,
|
|
3128
3259
|
resolveAppContainerConfig: resolveAdminNotesAppContainerConfig
|
|
3129
3260
|
});
|
|
@@ -3150,59 +3281,26 @@ async function saveAdminNotes({ container, input }) {
|
|
|
3150
3281
|
await container.adminNotesStorage.update(input.configText);
|
|
3151
3282
|
}
|
|
3152
3283
|
//#endregion
|
|
3153
|
-
//#region src/cli/commands/admin-notes/capture.ts
|
|
3154
|
-
var capture_default$12 = createCaptureCommand({
|
|
3155
|
-
description: "Capture current admin notes from kintone app to file",
|
|
3156
|
-
args: adminNotesArgs,
|
|
3157
|
-
spinnerMessage: "Capturing admin notes...",
|
|
3158
|
-
spinnerStopMessage: "Admin notes captured.",
|
|
3159
|
-
domainLabel: "Admin notes",
|
|
3160
|
-
multiAppSuccessMessage: "All admin notes captures completed successfully.",
|
|
3161
|
-
createContainer: createAdminNotesCliContainer,
|
|
3162
|
-
captureFn: captureAdminNotes,
|
|
3163
|
-
saveFn: saveAdminNotes,
|
|
3164
|
-
getConfigFilePath: (config) => config.adminNotesFilePath,
|
|
3165
|
-
resolveContainerConfig: resolveAdminNotesContainerConfig,
|
|
3166
|
-
resolveAppContainerConfig: resolveAdminNotesAppContainerConfig
|
|
3167
|
-
});
|
|
3168
|
-
//#endregion
|
|
3169
|
-
//#region src/core/domain/adminNotes/services/diffDetector.ts
|
|
3170
|
-
function compareConfigs$2(local, remote) {
|
|
3171
|
-
const entries = [];
|
|
3172
|
-
if (local.content !== remote.content) entries.push({
|
|
3173
|
-
type: "modified",
|
|
3174
|
-
field: "content",
|
|
3175
|
-
details: "content changed"
|
|
3176
|
-
});
|
|
3177
|
-
if (local.includeInTemplateAndDuplicates !== remote.includeInTemplateAndDuplicates) entries.push({
|
|
3178
|
-
type: "modified",
|
|
3179
|
-
field: "includeInTemplateAndDuplicates",
|
|
3180
|
-
details: `${String(remote.includeInTemplateAndDuplicates)} -> ${String(local.includeInTemplateAndDuplicates)}`
|
|
3181
|
-
});
|
|
3182
|
-
return entries;
|
|
3183
|
-
}
|
|
3184
|
-
const AdminNotesDiffDetector = { detect: (local, remote) => {
|
|
3185
|
-
return buildDiffResult(compareConfigs$2(local, remote));
|
|
3186
|
-
} };
|
|
3187
|
-
//#endregion
|
|
3188
|
-
//#region src/core/application/adminNotes/detectAdminNotesDiff.ts
|
|
3189
|
-
async function detectAdminNotesDiff({ container }) {
|
|
3190
|
-
return detectDiffFromConfig({
|
|
3191
|
-
getStorage: () => container.adminNotesStorage.get(),
|
|
3192
|
-
fetchRemote: () => container.adminNotesConfigurator.getAdminNotes(),
|
|
3193
|
-
parseConfig: (content) => parseAdminNotesConfigText(container.configCodec, content),
|
|
3194
|
-
detect: (local, remote) => AdminNotesDiffDetector.detect(local, remote.config),
|
|
3195
|
-
notFoundMessage: "Admin notes config file not found"
|
|
3196
|
-
});
|
|
3197
|
-
}
|
|
3198
|
-
//#endregion
|
|
3199
3284
|
//#region src/cli/commands/admin-notes/index.ts
|
|
3200
3285
|
var admin_notes_default = define({
|
|
3201
3286
|
name: "admin-notes",
|
|
3202
3287
|
description: "Manage kintone admin notes",
|
|
3203
3288
|
subCommands: {
|
|
3204
3289
|
apply: apply_default$11,
|
|
3205
|
-
capture:
|
|
3290
|
+
capture: createCaptureCommand({
|
|
3291
|
+
description: "Capture current admin notes from kintone app to file",
|
|
3292
|
+
args: adminNotesArgs,
|
|
3293
|
+
spinnerMessage: "Capturing admin notes...",
|
|
3294
|
+
spinnerStopMessage: "Admin notes captured.",
|
|
3295
|
+
domainLabel: "Admin notes",
|
|
3296
|
+
multiAppSuccessMessage: "All admin notes captures completed successfully.",
|
|
3297
|
+
createContainer: createAdminNotesCliContainer,
|
|
3298
|
+
captureFn: captureAdminNotes,
|
|
3299
|
+
saveFn: saveAdminNotes,
|
|
3300
|
+
getConfigFilePath: (config) => config.adminNotesFilePath,
|
|
3301
|
+
resolveContainerConfig: resolveAdminNotesContainerConfig,
|
|
3302
|
+
resolveAppContainerConfig: resolveAdminNotesAppContainerConfig
|
|
3303
|
+
}),
|
|
3206
3304
|
diff: createDiffCommand({
|
|
3207
3305
|
description: "Compare local admin notes config with remote kintone app",
|
|
3208
3306
|
args: adminNotesArgs,
|
|
@@ -3281,6 +3379,68 @@ async function applyAppPermission({ container }) {
|
|
|
3281
3379
|
});
|
|
3282
3380
|
}
|
|
3283
3381
|
//#endregion
|
|
3382
|
+
//#region src/core/domain/appPermission/services/diffDetector.ts
|
|
3383
|
+
const BOOLEAN_FLAGS = [
|
|
3384
|
+
"includeSubs",
|
|
3385
|
+
"appEditable",
|
|
3386
|
+
"recordViewable",
|
|
3387
|
+
"recordAddable",
|
|
3388
|
+
"recordEditable",
|
|
3389
|
+
"recordDeletable",
|
|
3390
|
+
"recordImportable",
|
|
3391
|
+
"recordExportable"
|
|
3392
|
+
];
|
|
3393
|
+
function entityKey(right) {
|
|
3394
|
+
return `${right.entity.type}:${right.entity.code}`;
|
|
3395
|
+
}
|
|
3396
|
+
function describeRight$1(right) {
|
|
3397
|
+
const flags = BOOLEAN_FLAGS.filter((f) => f !== "includeSubs" && right[f]);
|
|
3398
|
+
return flags.length > 0 ? flags.join(", ") : "no permissions";
|
|
3399
|
+
}
|
|
3400
|
+
function compareRights(local, remote) {
|
|
3401
|
+
const diffs = [];
|
|
3402
|
+
for (const flag of BOOLEAN_FLAGS) if (local[flag] !== remote[flag]) diffs.push(`${flag}: ${String(remote[flag])} -> ${String(local[flag])}`);
|
|
3403
|
+
return diffs;
|
|
3404
|
+
}
|
|
3405
|
+
const AppPermissionDiffDetector = { detect: (local, remote) => {
|
|
3406
|
+
const entries = [];
|
|
3407
|
+
const localMap = new Map(local.rights.map((r) => [entityKey(r), r]));
|
|
3408
|
+
const remoteMap = new Map(remote.rights.map((r) => [entityKey(r), r]));
|
|
3409
|
+
for (const [key, localRight] of localMap) {
|
|
3410
|
+
const remoteRight = remoteMap.get(key);
|
|
3411
|
+
if (!remoteRight) entries.push({
|
|
3412
|
+
type: "added",
|
|
3413
|
+
entityKey: key,
|
|
3414
|
+
details: describeRight$1(localRight)
|
|
3415
|
+
});
|
|
3416
|
+
else {
|
|
3417
|
+
const diffs = compareRights(localRight, remoteRight);
|
|
3418
|
+
if (diffs.length > 0) entries.push({
|
|
3419
|
+
type: "modified",
|
|
3420
|
+
entityKey: key,
|
|
3421
|
+
details: diffs.join(", ")
|
|
3422
|
+
});
|
|
3423
|
+
}
|
|
3424
|
+
}
|
|
3425
|
+
for (const [key, remoteRight] of remoteMap) if (!localMap.has(key)) entries.push({
|
|
3426
|
+
type: "deleted",
|
|
3427
|
+
entityKey: key,
|
|
3428
|
+
details: describeRight$1(remoteRight)
|
|
3429
|
+
});
|
|
3430
|
+
return buildDiffResult(entries);
|
|
3431
|
+
} };
|
|
3432
|
+
//#endregion
|
|
3433
|
+
//#region src/core/application/appPermission/detectAppPermissionDiff.ts
|
|
3434
|
+
async function detectAppPermissionDiff({ container }) {
|
|
3435
|
+
return detectDiffFromConfig({
|
|
3436
|
+
getStorage: () => container.appPermissionStorage.get(),
|
|
3437
|
+
fetchRemote: () => container.appPermissionConfigurator.getAppPermissions(),
|
|
3438
|
+
parseConfig: (content) => parseAppPermissionConfigText(container.configCodec, content),
|
|
3439
|
+
detect: (local, remote) => AppPermissionDiffDetector.detect(local, { rights: remote.rights }),
|
|
3440
|
+
notFoundMessage: "App permission config file not found"
|
|
3441
|
+
});
|
|
3442
|
+
}
|
|
3443
|
+
//#endregion
|
|
3284
3444
|
//#region src/core/adapters/kintone/appPermissionConfigurator.ts
|
|
3285
3445
|
const VALID_ENTITY_TYPES$7 = new Set([
|
|
3286
3446
|
"USER",
|
|
@@ -3407,6 +3567,10 @@ var apply_default$10 = createApplyCommand({
|
|
|
3407
3567
|
successMessage: "App access permissions applied successfully.",
|
|
3408
3568
|
createContainer: createAppPermissionCliContainer,
|
|
3409
3569
|
applyFn: applyAppPermission,
|
|
3570
|
+
diffPreview: {
|
|
3571
|
+
detectDiff: detectAppPermissionDiff,
|
|
3572
|
+
printResult: printAppPermissionDiffResult
|
|
3573
|
+
},
|
|
3410
3574
|
resolveContainerConfig: resolveAppAclContainerConfig,
|
|
3411
3575
|
resolveAppContainerConfig: resolveAppAclAppContainerConfig
|
|
3412
3576
|
});
|
|
@@ -3446,91 +3610,26 @@ async function saveAppPermission({ container, input }) {
|
|
|
3446
3610
|
await container.appPermissionStorage.update(input.configText);
|
|
3447
3611
|
}
|
|
3448
3612
|
//#endregion
|
|
3449
|
-
//#region src/cli/commands/app-acl/capture.ts
|
|
3450
|
-
var capture_default$11 = createCaptureCommand({
|
|
3451
|
-
description: "Capture current app access permissions from kintone app to file",
|
|
3452
|
-
args: appAclArgs,
|
|
3453
|
-
spinnerMessage: "Capturing app access permissions...",
|
|
3454
|
-
spinnerStopMessage: "App access permissions captured.",
|
|
3455
|
-
domainLabel: "App ACL",
|
|
3456
|
-
multiAppSuccessMessage: "All app ACL captures completed successfully.",
|
|
3457
|
-
createContainer: createAppPermissionCliContainer,
|
|
3458
|
-
captureFn: captureAppPermission,
|
|
3459
|
-
saveFn: saveAppPermission,
|
|
3460
|
-
getConfigFilePath: (config) => config.appAclFilePath,
|
|
3461
|
-
resolveContainerConfig: resolveAppAclContainerConfig,
|
|
3462
|
-
resolveAppContainerConfig: resolveAppAclAppContainerConfig
|
|
3463
|
-
});
|
|
3464
|
-
//#endregion
|
|
3465
|
-
//#region src/core/domain/appPermission/services/diffDetector.ts
|
|
3466
|
-
const BOOLEAN_FLAGS = [
|
|
3467
|
-
"includeSubs",
|
|
3468
|
-
"appEditable",
|
|
3469
|
-
"recordViewable",
|
|
3470
|
-
"recordAddable",
|
|
3471
|
-
"recordEditable",
|
|
3472
|
-
"recordDeletable",
|
|
3473
|
-
"recordImportable",
|
|
3474
|
-
"recordExportable"
|
|
3475
|
-
];
|
|
3476
|
-
function entityKey(right) {
|
|
3477
|
-
return `${right.entity.type}:${right.entity.code}`;
|
|
3478
|
-
}
|
|
3479
|
-
function describeRight$1(right) {
|
|
3480
|
-
const flags = BOOLEAN_FLAGS.filter((f) => f !== "includeSubs" && right[f]);
|
|
3481
|
-
return flags.length > 0 ? flags.join(", ") : "no permissions";
|
|
3482
|
-
}
|
|
3483
|
-
function compareRights(local, remote) {
|
|
3484
|
-
const diffs = [];
|
|
3485
|
-
for (const flag of BOOLEAN_FLAGS) if (local[flag] !== remote[flag]) diffs.push(`${flag}: ${String(remote[flag])} -> ${String(local[flag])}`);
|
|
3486
|
-
return diffs;
|
|
3487
|
-
}
|
|
3488
|
-
const AppPermissionDiffDetector = { detect: (local, remote) => {
|
|
3489
|
-
const entries = [];
|
|
3490
|
-
const localMap = new Map(local.rights.map((r) => [entityKey(r), r]));
|
|
3491
|
-
const remoteMap = new Map(remote.rights.map((r) => [entityKey(r), r]));
|
|
3492
|
-
for (const [key, localRight] of localMap) {
|
|
3493
|
-
const remoteRight = remoteMap.get(key);
|
|
3494
|
-
if (!remoteRight) entries.push({
|
|
3495
|
-
type: "added",
|
|
3496
|
-
entityKey: key,
|
|
3497
|
-
details: describeRight$1(localRight)
|
|
3498
|
-
});
|
|
3499
|
-
else {
|
|
3500
|
-
const diffs = compareRights(localRight, remoteRight);
|
|
3501
|
-
if (diffs.length > 0) entries.push({
|
|
3502
|
-
type: "modified",
|
|
3503
|
-
entityKey: key,
|
|
3504
|
-
details: diffs.join(", ")
|
|
3505
|
-
});
|
|
3506
|
-
}
|
|
3507
|
-
}
|
|
3508
|
-
for (const [key, remoteRight] of remoteMap) if (!localMap.has(key)) entries.push({
|
|
3509
|
-
type: "deleted",
|
|
3510
|
-
entityKey: key,
|
|
3511
|
-
details: describeRight$1(remoteRight)
|
|
3512
|
-
});
|
|
3513
|
-
return buildDiffResult(entries);
|
|
3514
|
-
} };
|
|
3515
|
-
//#endregion
|
|
3516
|
-
//#region src/core/application/appPermission/detectAppPermissionDiff.ts
|
|
3517
|
-
async function detectAppPermissionDiff({ container }) {
|
|
3518
|
-
return detectDiffFromConfig({
|
|
3519
|
-
getStorage: () => container.appPermissionStorage.get(),
|
|
3520
|
-
fetchRemote: () => container.appPermissionConfigurator.getAppPermissions(),
|
|
3521
|
-
parseConfig: (content) => parseAppPermissionConfigText(container.configCodec, content),
|
|
3522
|
-
detect: (local, remote) => AppPermissionDiffDetector.detect(local, { rights: remote.rights }),
|
|
3523
|
-
notFoundMessage: "App permission config file not found"
|
|
3524
|
-
});
|
|
3525
|
-
}
|
|
3526
|
-
//#endregion
|
|
3527
3613
|
//#region src/cli/commands/app-acl/index.ts
|
|
3528
3614
|
var app_acl_default = define({
|
|
3529
3615
|
name: "app-acl",
|
|
3530
3616
|
description: "Manage kintone app access permissions",
|
|
3531
3617
|
subCommands: {
|
|
3532
3618
|
apply: apply_default$10,
|
|
3533
|
-
capture:
|
|
3619
|
+
capture: createCaptureCommand({
|
|
3620
|
+
description: "Capture current app access permissions from kintone app to file",
|
|
3621
|
+
args: appAclArgs,
|
|
3622
|
+
spinnerMessage: "Capturing app access permissions...",
|
|
3623
|
+
spinnerStopMessage: "App access permissions captured.",
|
|
3624
|
+
domainLabel: "App ACL",
|
|
3625
|
+
multiAppSuccessMessage: "All app ACL captures completed successfully.",
|
|
3626
|
+
createContainer: createAppPermissionCliContainer,
|
|
3627
|
+
captureFn: captureAppPermission,
|
|
3628
|
+
saveFn: saveAppPermission,
|
|
3629
|
+
getConfigFilePath: (config) => config.appAclFilePath,
|
|
3630
|
+
resolveContainerConfig: resolveAppAclContainerConfig,
|
|
3631
|
+
resolveAppContainerConfig: resolveAppAclAppContainerConfig
|
|
3632
|
+
}),
|
|
3534
3633
|
diff: createDiffCommand({
|
|
3535
3634
|
description: "Compare local app permission config with remote kintone app",
|
|
3536
3635
|
args: appAclArgs,
|
|
@@ -3650,35 +3749,124 @@ async function resolveResources(platform, basePath, fileUploader) {
|
|
|
3650
3749
|
css
|
|
3651
3750
|
};
|
|
3652
3751
|
}
|
|
3653
|
-
function mergePlatform(current, incoming) {
|
|
3654
|
-
return {
|
|
3655
|
-
js: ResourceMerger.mergeResources(current.js, incoming.js),
|
|
3656
|
-
css: ResourceMerger.mergeResources(current.css, incoming.css)
|
|
3657
|
-
};
|
|
3752
|
+
function mergePlatform(current, incoming) {
|
|
3753
|
+
return {
|
|
3754
|
+
js: ResourceMerger.mergeResources(current.js, incoming.js),
|
|
3755
|
+
css: ResourceMerger.mergeResources(current.css, incoming.css)
|
|
3756
|
+
};
|
|
3757
|
+
}
|
|
3758
|
+
async function applyCustomization({ container, input }) {
|
|
3759
|
+
const result = await container.customizationStorage.get();
|
|
3760
|
+
if (!result.exists) throw new ValidationError(ValidationErrorCode.InvalidInput, "Customization config file not found");
|
|
3761
|
+
const config = parseCustomizationConfigText(container.configCodec, result.content);
|
|
3762
|
+
const currentCustomization = await container.customizationConfigurator.getCustomization();
|
|
3763
|
+
const [resolvedDesktop, resolvedMobile] = await Promise.all([resolveResources(config.desktop, input.basePath, container.fileUploader), resolveResources(config.mobile, input.basePath, container.fileUploader)]);
|
|
3764
|
+
const mergedDesktop = mergePlatform(currentCustomization.desktop, resolvedDesktop);
|
|
3765
|
+
const mergedMobile = mergePlatform(currentCustomization.mobile, resolvedMobile);
|
|
3766
|
+
ResourceMerger.assertResourceCount("desktop.js", mergedDesktop.js);
|
|
3767
|
+
ResourceMerger.assertResourceCount("desktop.css", mergedDesktop.css);
|
|
3768
|
+
ResourceMerger.assertResourceCount("mobile.js", mergedMobile.js);
|
|
3769
|
+
ResourceMerger.assertResourceCount("mobile.css", mergedMobile.css);
|
|
3770
|
+
await container.customizationConfigurator.updateCustomization({
|
|
3771
|
+
scope: config.scope,
|
|
3772
|
+
desktop: {
|
|
3773
|
+
js: mergedDesktop.js,
|
|
3774
|
+
css: mergedDesktop.css
|
|
3775
|
+
},
|
|
3776
|
+
mobile: {
|
|
3777
|
+
js: mergedMobile.js,
|
|
3778
|
+
css: mergedMobile.css
|
|
3779
|
+
},
|
|
3780
|
+
revision: currentCustomization.revision
|
|
3781
|
+
});
|
|
3782
|
+
}
|
|
3783
|
+
//#endregion
|
|
3784
|
+
//#region src/core/domain/customization/services/diffDetector.ts
|
|
3785
|
+
function resourceName(resource) {
|
|
3786
|
+
if (resource.type === "URL") return resource.url;
|
|
3787
|
+
const parts = resource.path.replace(/\\/g, "/").split("/").filter(Boolean);
|
|
3788
|
+
return parts[parts.length - 1];
|
|
3789
|
+
}
|
|
3790
|
+
function remoteResourceName(resource) {
|
|
3791
|
+
if (resource.type === "URL") return resource.url;
|
|
3792
|
+
return resource.file.name;
|
|
3793
|
+
}
|
|
3794
|
+
function compareResourceLists(localResources, remoteResources, platform, resourceType, warnings) {
|
|
3795
|
+
const entries = [];
|
|
3796
|
+
const localNames = localResources.map(resourceName);
|
|
3797
|
+
const remoteNames = remoteResources.map(remoteResourceName);
|
|
3798
|
+
const localNameSet = new Set(localNames);
|
|
3799
|
+
const remoteNameSet = new Set(remoteNames);
|
|
3800
|
+
const hasDuplicates = localNames.length !== localNameSet.size || remoteNames.length !== remoteNameSet.size;
|
|
3801
|
+
if (hasDuplicates) warnings.push(`[${platform}.${resourceType}] duplicate basenames detected; diff results may be inaccurate for FILE resources`);
|
|
3802
|
+
const localTypeMap = /* @__PURE__ */ new Map();
|
|
3803
|
+
for (const r of localResources) localTypeMap.set(resourceName(r), r.type);
|
|
3804
|
+
const remoteTypeMap = /* @__PURE__ */ new Map();
|
|
3805
|
+
for (const r of remoteResources) remoteTypeMap.set(remoteResourceName(r), r.type);
|
|
3806
|
+
for (const name of localNameSet) if (!remoteNameSet.has(name)) {
|
|
3807
|
+
const resType = localTypeMap.get(name) ?? "FILE";
|
|
3808
|
+
entries.push({
|
|
3809
|
+
type: "added",
|
|
3810
|
+
platform,
|
|
3811
|
+
category: resourceType,
|
|
3812
|
+
name,
|
|
3813
|
+
details: `new ${resType} resource`
|
|
3814
|
+
});
|
|
3815
|
+
}
|
|
3816
|
+
for (const name of remoteNameSet) if (!localNameSet.has(name)) {
|
|
3817
|
+
const resType = remoteTypeMap.get(name) ?? "FILE";
|
|
3818
|
+
entries.push({
|
|
3819
|
+
type: "deleted",
|
|
3820
|
+
platform,
|
|
3821
|
+
category: resourceType,
|
|
3822
|
+
name,
|
|
3823
|
+
details: `removed ${resType} resource`
|
|
3824
|
+
});
|
|
3825
|
+
}
|
|
3826
|
+
const matchedFiles = [...localNameSet].filter((n) => remoteNameSet.has(n));
|
|
3827
|
+
const hasLocalFiles = localResources.some((r) => r.type === "FILE");
|
|
3828
|
+
const hasRemoteFiles = remoteResources.some((r) => r.type === "FILE");
|
|
3829
|
+
if (matchedFiles.length > 0 && hasLocalFiles && hasRemoteFiles) warnings.push(`[${platform}.${resourceType}] FILE resources are compared by name only; content changes are not detected`);
|
|
3830
|
+
if (!hasDuplicates) {
|
|
3831
|
+
const localShared = localNames.filter((n) => remoteNameSet.has(n));
|
|
3832
|
+
const remoteShared = remoteNames.filter((n) => localNameSet.has(n));
|
|
3833
|
+
if (localShared.length > 1 && localShared.length === remoteShared.length && localShared.some((n, i) => n !== remoteShared[i])) entries.push({
|
|
3834
|
+
type: "modified",
|
|
3835
|
+
platform,
|
|
3836
|
+
category: resourceType,
|
|
3837
|
+
name: "(order)",
|
|
3838
|
+
details: "resource load order changed"
|
|
3839
|
+
});
|
|
3840
|
+
}
|
|
3841
|
+
return entries;
|
|
3842
|
+
}
|
|
3843
|
+
function comparePlatform(localJs, localCss, remote, platform, warnings) {
|
|
3844
|
+
return [...compareResourceLists(localJs, remote.js, platform, "js", warnings), ...compareResourceLists(localCss, remote.css, platform, "css", warnings)];
|
|
3658
3845
|
}
|
|
3659
|
-
|
|
3660
|
-
const
|
|
3661
|
-
|
|
3662
|
-
const
|
|
3663
|
-
|
|
3664
|
-
|
|
3665
|
-
|
|
3666
|
-
|
|
3667
|
-
|
|
3668
|
-
|
|
3669
|
-
|
|
3670
|
-
|
|
3671
|
-
|
|
3672
|
-
|
|
3673
|
-
|
|
3674
|
-
|
|
3675
|
-
|
|
3676
|
-
|
|
3677
|
-
|
|
3678
|
-
|
|
3679
|
-
|
|
3680
|
-
|
|
3681
|
-
|
|
3846
|
+
const CustomizationDiffDetector = { detect: (local, remote) => {
|
|
3847
|
+
const entries = [];
|
|
3848
|
+
const warnings = [];
|
|
3849
|
+
const localScope = local.scope ?? "ALL";
|
|
3850
|
+
if (localScope !== remote.scope) entries.push({
|
|
3851
|
+
type: "modified",
|
|
3852
|
+
platform: "config",
|
|
3853
|
+
category: "scope",
|
|
3854
|
+
name: "scope",
|
|
3855
|
+
details: `${remote.scope} -> ${localScope}`
|
|
3856
|
+
});
|
|
3857
|
+
entries.push(...comparePlatform(local.desktop.js, local.desktop.css, remote.desktop, "desktop", warnings));
|
|
3858
|
+
entries.push(...comparePlatform(local.mobile.js, local.mobile.css, remote.mobile, "mobile", warnings));
|
|
3859
|
+
return buildDiffResult(entries, warnings);
|
|
3860
|
+
} };
|
|
3861
|
+
//#endregion
|
|
3862
|
+
//#region src/core/application/customization/detectCustomizationDiff.ts
|
|
3863
|
+
async function detectCustomizationDiff({ container }) {
|
|
3864
|
+
return detectDiffFromConfig({
|
|
3865
|
+
getStorage: () => container.customizationStorage.get(),
|
|
3866
|
+
fetchRemote: () => container.customizationConfigurator.getCustomization(),
|
|
3867
|
+
parseConfig: (content) => parseCustomizationConfigText(container.configCodec, content),
|
|
3868
|
+
detect: (local, remote) => CustomizationDiffDetector.detect(local, remote),
|
|
3869
|
+
notFoundMessage: "Customization config file not found"
|
|
3682
3870
|
});
|
|
3683
3871
|
}
|
|
3684
3872
|
//#endregion
|
|
@@ -3945,132 +4133,46 @@ var capture_default$10 = define({
|
|
|
3945
4133
|
});
|
|
3946
4134
|
//#endregion
|
|
3947
4135
|
//#region src/cli/commands/customize/apply.ts
|
|
3948
|
-
|
|
4136
|
+
function createCustomizationContainer(config) {
|
|
3949
4137
|
const container = createCustomizationCliContainer(config);
|
|
3950
4138
|
const filePrefix = deriveFilePrefix(config.customizeFilePath);
|
|
3951
|
-
|
|
3952
|
-
const s = p.spinner();
|
|
3953
|
-
s.start("Applying customization...");
|
|
3954
|
-
await applyCustomization({
|
|
4139
|
+
return {
|
|
3955
4140
|
container,
|
|
3956
|
-
|
|
3957
|
-
}
|
|
3958
|
-
s.stop("Customization applied.");
|
|
3959
|
-
return container;
|
|
3960
|
-
}
|
|
3961
|
-
var apply_default$9 = define({
|
|
3962
|
-
name: "apply",
|
|
3963
|
-
description: "Apply JS/CSS customization to kintone app",
|
|
3964
|
-
args: {
|
|
3965
|
-
...customizeArgs,
|
|
3966
|
-
...confirmArgs
|
|
3967
|
-
},
|
|
3968
|
-
run: async (ctx) => {
|
|
3969
|
-
try {
|
|
3970
|
-
const values = ctx.values;
|
|
3971
|
-
const skipConfirm = values.yes === true;
|
|
3972
|
-
await routeMultiApp(values, {
|
|
3973
|
-
singleLegacy: async () => {
|
|
3974
|
-
await confirmAndDeploy([await applyCustomizationForApp(resolveCustomizeConfig(values))], skipConfirm, "Customization applied and deployed successfully.");
|
|
3975
|
-
},
|
|
3976
|
-
singleApp: async (app, projectConfig) => {
|
|
3977
|
-
await confirmAndDeploy([await applyCustomizationForApp(resolveCustomizeAppConfig(app, projectConfig, values))], skipConfirm, "Customization applied and deployed successfully.");
|
|
3978
|
-
},
|
|
3979
|
-
multiApp: async (plan, projectConfig) => {
|
|
3980
|
-
const containers = [];
|
|
3981
|
-
await runMultiAppWithHeaders(plan, async (app) => {
|
|
3982
|
-
const container = await applyCustomizationForApp(resolveCustomizeAppConfig(app, projectConfig, values));
|
|
3983
|
-
containers.push({
|
|
3984
|
-
appDeployer: container.appDeployer,
|
|
3985
|
-
appName: app.name
|
|
3986
|
-
});
|
|
3987
|
-
});
|
|
3988
|
-
await confirmAndDeploy(containers, skipConfirm, "Customization applied and deployed successfully.");
|
|
3989
|
-
}
|
|
3990
|
-
});
|
|
3991
|
-
} catch (error) {
|
|
3992
|
-
handleCliError(error);
|
|
3993
|
-
}
|
|
3994
|
-
}
|
|
3995
|
-
});
|
|
3996
|
-
//#endregion
|
|
3997
|
-
//#region src/core/domain/customization/services/diffDetector.ts
|
|
3998
|
-
function resourceName(resource) {
|
|
3999
|
-
if (resource.type === "URL") return resource.url;
|
|
4000
|
-
const parts = resource.path.replace(/\\/g, "/").split("/").filter(Boolean);
|
|
4001
|
-
return parts[parts.length - 1];
|
|
4002
|
-
}
|
|
4003
|
-
function remoteResourceName(resource) {
|
|
4004
|
-
if (resource.type === "URL") return resource.url;
|
|
4005
|
-
return resource.file.name;
|
|
4141
|
+
basePath: join(dirname(resolve(config.customizeFilePath)), filePrefix)
|
|
4142
|
+
};
|
|
4006
4143
|
}
|
|
4007
|
-
function
|
|
4008
|
-
const
|
|
4009
|
-
|
|
4010
|
-
|
|
4011
|
-
|
|
4012
|
-
|
|
4013
|
-
|
|
4014
|
-
if (hasDuplicates) warnings.push(`[${platform}.${resourceType}] duplicate basenames detected; diff results may be inaccurate for FILE resources`);
|
|
4015
|
-
for (const name of localNameSet) if (!remoteNameSet.has(name)) entries.push({
|
|
4016
|
-
type: "added",
|
|
4017
|
-
platform,
|
|
4018
|
-
category: resourceType,
|
|
4019
|
-
name,
|
|
4020
|
-
details: "new resource"
|
|
4021
|
-
});
|
|
4022
|
-
for (const name of remoteNameSet) if (!localNameSet.has(name)) entries.push({
|
|
4023
|
-
type: "deleted",
|
|
4024
|
-
platform,
|
|
4025
|
-
category: resourceType,
|
|
4026
|
-
name,
|
|
4027
|
-
details: "removed"
|
|
4028
|
-
});
|
|
4029
|
-
const matchedFiles = [...localNameSet].filter((n) => remoteNameSet.has(n));
|
|
4030
|
-
const hasLocalFiles = localResources.some((r) => r.type === "FILE");
|
|
4031
|
-
const hasRemoteFiles = remoteResources.some((r) => r.type === "FILE");
|
|
4032
|
-
if (matchedFiles.length > 0 && hasLocalFiles && hasRemoteFiles) warnings.push(`[${platform}.${resourceType}] FILE resources are compared by name only; content changes are not detected`);
|
|
4033
|
-
if (!hasDuplicates) {
|
|
4034
|
-
const localShared = localNames.filter((n) => remoteNameSet.has(n));
|
|
4035
|
-
const remoteShared = remoteNames.filter((n) => localNameSet.has(n));
|
|
4036
|
-
if (localShared.length > 1 && localShared.length === remoteShared.length && localShared.some((n, i) => n !== remoteShared[i])) entries.push({
|
|
4037
|
-
type: "modified",
|
|
4038
|
-
platform,
|
|
4039
|
-
category: resourceType,
|
|
4040
|
-
name: "(order)",
|
|
4041
|
-
details: "resource load order changed"
|
|
4144
|
+
async function runCustomizationApply(container, basePath) {
|
|
4145
|
+
const s = p.spinner();
|
|
4146
|
+
s.start("Applying customization...");
|
|
4147
|
+
try {
|
|
4148
|
+
await applyCustomization({
|
|
4149
|
+
container,
|
|
4150
|
+
input: { basePath }
|
|
4042
4151
|
});
|
|
4152
|
+
} catch (error) {
|
|
4153
|
+
s.stop("Apply failed.");
|
|
4154
|
+
throw error;
|
|
4043
4155
|
}
|
|
4044
|
-
|
|
4045
|
-
}
|
|
4046
|
-
function comparePlatform(localJs, localCss, remote, platform, warnings) {
|
|
4047
|
-
return [...compareResourceLists(localJs, remote.js, platform, "js", warnings), ...compareResourceLists(localCss, remote.css, platform, "css", warnings)];
|
|
4156
|
+
s.stop("Customization applied.");
|
|
4048
4157
|
}
|
|
4049
|
-
|
|
4050
|
-
const
|
|
4051
|
-
const
|
|
4052
|
-
|
|
4053
|
-
|
|
4054
|
-
|
|
4055
|
-
|
|
4056
|
-
|
|
4057
|
-
|
|
4058
|
-
|
|
4059
|
-
}
|
|
4060
|
-
|
|
4061
|
-
|
|
4062
|
-
return
|
|
4063
|
-
|
|
4064
|
-
|
|
4065
|
-
|
|
4066
|
-
|
|
4067
|
-
return detectDiffFromConfig({
|
|
4068
|
-
getStorage: () => container.customizationStorage.get(),
|
|
4069
|
-
fetchRemote: () => container.customizationConfigurator.getCustomization(),
|
|
4070
|
-
parseConfig: (content) => parseCustomizationConfigText(container.configCodec, content),
|
|
4071
|
-
detect: (local, remote) => CustomizationDiffDetector.detect(local, remote),
|
|
4072
|
-
notFoundMessage: "Customization config file not found"
|
|
4073
|
-
});
|
|
4158
|
+
async function runDiffPreview(config) {
|
|
4159
|
+
const { container, basePath } = createCustomizationContainer(config);
|
|
4160
|
+
const s = p.spinner();
|
|
4161
|
+
s.start("Detecting changes...");
|
|
4162
|
+
let result;
|
|
4163
|
+
try {
|
|
4164
|
+
result = await detectCustomizationDiff({ container });
|
|
4165
|
+
} catch (error) {
|
|
4166
|
+
s.stop("Comparison failed.");
|
|
4167
|
+
throw error;
|
|
4168
|
+
}
|
|
4169
|
+
s.stop("Comparison complete.");
|
|
4170
|
+
printCustomizationDiffResult(result);
|
|
4171
|
+
return {
|
|
4172
|
+
container,
|
|
4173
|
+
basePath,
|
|
4174
|
+
hasChanges: !result.isEmpty
|
|
4175
|
+
};
|
|
4074
4176
|
}
|
|
4075
4177
|
//#endregion
|
|
4076
4178
|
//#region src/cli/commands/customize/index.ts
|
|
@@ -4078,7 +4180,96 @@ var customize_default = define({
|
|
|
4078
4180
|
name: "customize",
|
|
4079
4181
|
description: "Manage kintone JS/CSS customizations",
|
|
4080
4182
|
subCommands: {
|
|
4081
|
-
apply:
|
|
4183
|
+
apply: define({
|
|
4184
|
+
name: "apply",
|
|
4185
|
+
description: "Apply JS/CSS customization to kintone app",
|
|
4186
|
+
args: {
|
|
4187
|
+
...customizeArgs,
|
|
4188
|
+
...confirmArgs
|
|
4189
|
+
},
|
|
4190
|
+
run: async (ctx) => {
|
|
4191
|
+
try {
|
|
4192
|
+
const values = ctx.values;
|
|
4193
|
+
const skipConfirm = values.yes === true;
|
|
4194
|
+
await routeMultiApp(values, {
|
|
4195
|
+
singleLegacy: async () => {
|
|
4196
|
+
const { container, basePath, hasChanges } = await runDiffPreview(resolveCustomizeConfig(values));
|
|
4197
|
+
if (!hasChanges) {
|
|
4198
|
+
p.log.success("No changes detected.");
|
|
4199
|
+
return;
|
|
4200
|
+
}
|
|
4201
|
+
if (!skipConfirm) {
|
|
4202
|
+
const shouldContinue = await p.confirm({ message: "Apply these changes?" });
|
|
4203
|
+
if (p.isCancel(shouldContinue) || !shouldContinue) {
|
|
4204
|
+
p.cancel("Apply cancelled.");
|
|
4205
|
+
return;
|
|
4206
|
+
}
|
|
4207
|
+
}
|
|
4208
|
+
await runCustomizationApply(container, basePath);
|
|
4209
|
+
await confirmAndDeploy([container], skipConfirm, "Customization applied and deployed successfully.");
|
|
4210
|
+
},
|
|
4211
|
+
singleApp: async (app, projectConfig) => {
|
|
4212
|
+
const { container, basePath, hasChanges } = await runDiffPreview(resolveCustomizeAppConfig(app, projectConfig, values));
|
|
4213
|
+
if (!hasChanges) {
|
|
4214
|
+
p.log.success("No changes detected.");
|
|
4215
|
+
return;
|
|
4216
|
+
}
|
|
4217
|
+
if (!skipConfirm) {
|
|
4218
|
+
const shouldContinue = await p.confirm({ message: "Apply these changes?" });
|
|
4219
|
+
if (p.isCancel(shouldContinue) || !shouldContinue) {
|
|
4220
|
+
p.cancel("Apply cancelled.");
|
|
4221
|
+
return;
|
|
4222
|
+
}
|
|
4223
|
+
}
|
|
4224
|
+
await runCustomizationApply(container, basePath);
|
|
4225
|
+
await confirmAndDeploy([container], skipConfirm, "Customization applied and deployed successfully.");
|
|
4226
|
+
},
|
|
4227
|
+
multiApp: async (plan, projectConfig) => {
|
|
4228
|
+
const appDiffResults = [];
|
|
4229
|
+
for (const app of plan.orderedApps) {
|
|
4230
|
+
const config = resolveCustomizeAppConfig(app, projectConfig, values);
|
|
4231
|
+
printAppHeader(app.name, app.appId);
|
|
4232
|
+
const { container, basePath, hasChanges } = await runDiffPreview(config);
|
|
4233
|
+
appDiffResults.push({
|
|
4234
|
+
app,
|
|
4235
|
+
container,
|
|
4236
|
+
basePath,
|
|
4237
|
+
hasChanges
|
|
4238
|
+
});
|
|
4239
|
+
}
|
|
4240
|
+
if (!appDiffResults.some((a) => a.hasChanges)) {
|
|
4241
|
+
p.log.success("No changes detected in any app.");
|
|
4242
|
+
return;
|
|
4243
|
+
}
|
|
4244
|
+
if (!skipConfirm) {
|
|
4245
|
+
const shouldContinue = await p.confirm({ message: "Apply these changes to all apps?" });
|
|
4246
|
+
if (p.isCancel(shouldContinue) || !shouldContinue) {
|
|
4247
|
+
p.cancel("Apply cancelled.");
|
|
4248
|
+
return;
|
|
4249
|
+
}
|
|
4250
|
+
}
|
|
4251
|
+
const containers = [];
|
|
4252
|
+
await runMultiAppWithHeaders(plan, async (app) => {
|
|
4253
|
+
const entry = appDiffResults.find((a) => a.app.name === app.name);
|
|
4254
|
+
if (!entry) throw new SystemError(SystemErrorCode.InternalServerError, `App container not found for "${app.name}"`);
|
|
4255
|
+
if (!entry.hasChanges) {
|
|
4256
|
+
p.log.info("No changes. Skipping.");
|
|
4257
|
+
return;
|
|
4258
|
+
}
|
|
4259
|
+
await runCustomizationApply(entry.container, entry.basePath);
|
|
4260
|
+
containers.push({
|
|
4261
|
+
appDeployer: entry.container.appDeployer,
|
|
4262
|
+
appName: app.name
|
|
4263
|
+
});
|
|
4264
|
+
});
|
|
4265
|
+
await confirmAndDeploy(containers, skipConfirm, "Customization applied and deployed successfully.");
|
|
4266
|
+
}
|
|
4267
|
+
});
|
|
4268
|
+
} catch (error) {
|
|
4269
|
+
handleCliError(error);
|
|
4270
|
+
}
|
|
4271
|
+
}
|
|
4272
|
+
}),
|
|
4082
4273
|
capture: capture_default$10,
|
|
4083
4274
|
diff: createDiffCommand({
|
|
4084
4275
|
description: "Compare local customization config with remote kintone app",
|
|
@@ -4258,14 +4449,71 @@ function parseFieldPermissionConfigText(codec, rawText) {
|
|
|
4258
4449
|
async function applyFieldPermission({ container }) {
|
|
4259
4450
|
await applyFromConfig({
|
|
4260
4451
|
getStorage: () => container.fieldPermissionStorage.get(),
|
|
4261
|
-
parseConfig: (content) => parseFieldPermissionConfigText(container.configCodec, content),
|
|
4452
|
+
parseConfig: (content) => parseFieldPermissionConfigText(container.configCodec, content),
|
|
4453
|
+
fetchRemote: () => container.fieldPermissionConfigurator.getFieldPermissions(),
|
|
4454
|
+
update: async (config, current) => {
|
|
4455
|
+
await container.fieldPermissionConfigurator.updateFieldPermissions({
|
|
4456
|
+
rights: config.rights,
|
|
4457
|
+
revision: current.revision
|
|
4458
|
+
});
|
|
4459
|
+
},
|
|
4460
|
+
notFoundMessage: "Field permission config file not found"
|
|
4461
|
+
});
|
|
4462
|
+
}
|
|
4463
|
+
//#endregion
|
|
4464
|
+
//#region src/core/domain/fieldPermission/services/diffDetector.ts
|
|
4465
|
+
function isEntitiesEqual$1(a, b) {
|
|
4466
|
+
return deepEqual(a.entities.map((e) => ({
|
|
4467
|
+
accessibility: e.accessibility,
|
|
4468
|
+
type: e.entity.type,
|
|
4469
|
+
code: e.entity.code,
|
|
4470
|
+
includeSubs: e.includeSubs ?? false
|
|
4471
|
+
})), b.entities.map((e) => ({
|
|
4472
|
+
accessibility: e.accessibility,
|
|
4473
|
+
type: e.entity.type,
|
|
4474
|
+
code: e.entity.code,
|
|
4475
|
+
includeSubs: e.includeSubs ?? false
|
|
4476
|
+
})));
|
|
4477
|
+
}
|
|
4478
|
+
function describeEntities(entities) {
|
|
4479
|
+
if (entities.length === 0) return "no entities";
|
|
4480
|
+
return entities.map((e) => {
|
|
4481
|
+
const access = e.accessibility.toLowerCase();
|
|
4482
|
+
return `${e.entity.type}:${e.entity.code}(${access})`;
|
|
4483
|
+
}).join(", ");
|
|
4484
|
+
}
|
|
4485
|
+
const FieldPermissionDiffDetector = { detect: (local, remote) => {
|
|
4486
|
+
const entries = [];
|
|
4487
|
+
const localMap = new Map(local.rights.map((r) => [r.code, r]));
|
|
4488
|
+
const remoteMap = new Map(remote.rights.map((r) => [r.code, r]));
|
|
4489
|
+
for (const [code, localRight] of localMap) {
|
|
4490
|
+
const remoteRight = remoteMap.get(code);
|
|
4491
|
+
if (!remoteRight) entries.push({
|
|
4492
|
+
type: "added",
|
|
4493
|
+
fieldCode: code,
|
|
4494
|
+
details: `entities: ${describeEntities(localRight.entities)}`
|
|
4495
|
+
});
|
|
4496
|
+
else if (!isEntitiesEqual$1(localRight, remoteRight)) entries.push({
|
|
4497
|
+
type: "modified",
|
|
4498
|
+
fieldCode: code,
|
|
4499
|
+
details: `entities: ${describeEntities(localRight.entities)}`
|
|
4500
|
+
});
|
|
4501
|
+
}
|
|
4502
|
+
for (const [code, remoteRight] of remoteMap) if (!localMap.has(code)) entries.push({
|
|
4503
|
+
type: "deleted",
|
|
4504
|
+
fieldCode: code,
|
|
4505
|
+
details: `entities: ${describeEntities(remoteRight.entities)}`
|
|
4506
|
+
});
|
|
4507
|
+
return buildDiffResult(entries);
|
|
4508
|
+
} };
|
|
4509
|
+
//#endregion
|
|
4510
|
+
//#region src/core/application/fieldPermission/detectFieldPermissionDiff.ts
|
|
4511
|
+
async function detectFieldPermissionDiff({ container }) {
|
|
4512
|
+
return detectDiffFromConfig({
|
|
4513
|
+
getStorage: () => container.fieldPermissionStorage.get(),
|
|
4262
4514
|
fetchRemote: () => container.fieldPermissionConfigurator.getFieldPermissions(),
|
|
4263
|
-
|
|
4264
|
-
|
|
4265
|
-
rights: config.rights,
|
|
4266
|
-
revision: current.revision
|
|
4267
|
-
});
|
|
4268
|
-
},
|
|
4515
|
+
parseConfig: (content) => parseFieldPermissionConfigText(container.configCodec, content),
|
|
4516
|
+
detect: (local, remote) => FieldPermissionDiffDetector.detect(local, { rights: remote.rights }),
|
|
4269
4517
|
notFoundMessage: "Field permission config file not found"
|
|
4270
4518
|
});
|
|
4271
4519
|
}
|
|
@@ -4300,6 +4548,10 @@ var apply_default$8 = createApplyCommand({
|
|
|
4300
4548
|
successMessage: "Field access permissions applied successfully.",
|
|
4301
4549
|
createContainer: createFieldPermissionCliContainer,
|
|
4302
4550
|
applyFn: applyFieldPermission,
|
|
4551
|
+
diffPreview: {
|
|
4552
|
+
detectDiff: detectFieldPermissionDiff,
|
|
4553
|
+
printResult: printFieldPermissionDiffResult
|
|
4554
|
+
},
|
|
4303
4555
|
resolveContainerConfig: resolveFieldAclContainerConfig,
|
|
4304
4556
|
resolveAppContainerConfig: resolveFieldAclAppContainerConfig
|
|
4305
4557
|
});
|
|
@@ -4337,79 +4589,26 @@ async function saveFieldPermission({ container, input }) {
|
|
|
4337
4589
|
await container.fieldPermissionStorage.update(input.configText);
|
|
4338
4590
|
}
|
|
4339
4591
|
//#endregion
|
|
4340
|
-
//#region src/cli/commands/field-acl/capture.ts
|
|
4341
|
-
var capture_default$9 = createCaptureCommand({
|
|
4342
|
-
description: "Capture current field access permissions from kintone app to file",
|
|
4343
|
-
args: fieldAclArgs,
|
|
4344
|
-
spinnerMessage: "Capturing field access permissions...",
|
|
4345
|
-
spinnerStopMessage: "Field access permissions captured.",
|
|
4346
|
-
domainLabel: "Field ACL",
|
|
4347
|
-
multiAppSuccessMessage: "All field ACL captures completed successfully.",
|
|
4348
|
-
createContainer: createFieldPermissionCliContainer,
|
|
4349
|
-
captureFn: captureFieldPermission,
|
|
4350
|
-
saveFn: saveFieldPermission,
|
|
4351
|
-
getConfigFilePath: (config) => config.fieldAclFilePath,
|
|
4352
|
-
resolveContainerConfig: resolveFieldAclContainerConfig,
|
|
4353
|
-
resolveAppContainerConfig: resolveFieldAclAppContainerConfig
|
|
4354
|
-
});
|
|
4355
|
-
//#endregion
|
|
4356
|
-
//#region src/core/domain/fieldPermission/services/diffDetector.ts
|
|
4357
|
-
function isEntitiesEqual$1(a, b) {
|
|
4358
|
-
return deepEqual(a.entities.map((e) => ({
|
|
4359
|
-
accessibility: e.accessibility,
|
|
4360
|
-
type: e.entity.type,
|
|
4361
|
-
code: e.entity.code,
|
|
4362
|
-
includeSubs: e.includeSubs ?? false
|
|
4363
|
-
})), b.entities.map((e) => ({
|
|
4364
|
-
accessibility: e.accessibility,
|
|
4365
|
-
type: e.entity.type,
|
|
4366
|
-
code: e.entity.code,
|
|
4367
|
-
includeSubs: e.includeSubs ?? false
|
|
4368
|
-
})));
|
|
4369
|
-
}
|
|
4370
|
-
const FieldPermissionDiffDetector = { detect: (local, remote) => {
|
|
4371
|
-
const entries = [];
|
|
4372
|
-
const localMap = new Map(local.rights.map((r) => [r.code, r]));
|
|
4373
|
-
const remoteMap = new Map(remote.rights.map((r) => [r.code, r]));
|
|
4374
|
-
for (const [code, localRight] of localMap) {
|
|
4375
|
-
const remoteRight = remoteMap.get(code);
|
|
4376
|
-
if (!remoteRight) entries.push({
|
|
4377
|
-
type: "added",
|
|
4378
|
-
fieldCode: code,
|
|
4379
|
-
details: `${localRight.entities.length} entities`
|
|
4380
|
-
});
|
|
4381
|
-
else if (!isEntitiesEqual$1(localRight, remoteRight)) entries.push({
|
|
4382
|
-
type: "modified",
|
|
4383
|
-
fieldCode: code,
|
|
4384
|
-
details: "entities changed"
|
|
4385
|
-
});
|
|
4386
|
-
}
|
|
4387
|
-
for (const code of remoteMap.keys()) if (!localMap.has(code)) entries.push({
|
|
4388
|
-
type: "deleted",
|
|
4389
|
-
fieldCode: code,
|
|
4390
|
-
details: "removed"
|
|
4391
|
-
});
|
|
4392
|
-
return buildDiffResult(entries);
|
|
4393
|
-
} };
|
|
4394
|
-
//#endregion
|
|
4395
|
-
//#region src/core/application/fieldPermission/detectFieldPermissionDiff.ts
|
|
4396
|
-
async function detectFieldPermissionDiff({ container }) {
|
|
4397
|
-
return detectDiffFromConfig({
|
|
4398
|
-
getStorage: () => container.fieldPermissionStorage.get(),
|
|
4399
|
-
fetchRemote: () => container.fieldPermissionConfigurator.getFieldPermissions(),
|
|
4400
|
-
parseConfig: (content) => parseFieldPermissionConfigText(container.configCodec, content),
|
|
4401
|
-
detect: (local, remote) => FieldPermissionDiffDetector.detect(local, { rights: remote.rights }),
|
|
4402
|
-
notFoundMessage: "Field permission config file not found"
|
|
4403
|
-
});
|
|
4404
|
-
}
|
|
4405
|
-
//#endregion
|
|
4406
4592
|
//#region src/cli/commands/field-acl/index.ts
|
|
4407
4593
|
var field_acl_default = define({
|
|
4408
4594
|
name: "field-acl",
|
|
4409
4595
|
description: "Manage kintone field access permissions",
|
|
4410
4596
|
subCommands: {
|
|
4411
4597
|
apply: apply_default$8,
|
|
4412
|
-
capture:
|
|
4598
|
+
capture: createCaptureCommand({
|
|
4599
|
+
description: "Capture current field access permissions from kintone app to file",
|
|
4600
|
+
args: fieldAclArgs,
|
|
4601
|
+
spinnerMessage: "Capturing field access permissions...",
|
|
4602
|
+
spinnerStopMessage: "Field access permissions captured.",
|
|
4603
|
+
domainLabel: "Field ACL",
|
|
4604
|
+
multiAppSuccessMessage: "All field ACL captures completed successfully.",
|
|
4605
|
+
createContainer: createFieldPermissionCliContainer,
|
|
4606
|
+
captureFn: captureFieldPermission,
|
|
4607
|
+
saveFn: saveFieldPermission,
|
|
4608
|
+
getConfigFilePath: (config) => config.fieldAclFilePath,
|
|
4609
|
+
resolveContainerConfig: resolveFieldAclContainerConfig,
|
|
4610
|
+
resolveAppContainerConfig: resolveFieldAclAppContainerConfig
|
|
4611
|
+
}),
|
|
4413
4612
|
diff: createDiffCommand({
|
|
4414
4613
|
description: "Compare local field permission config with remote kintone app",
|
|
4415
4614
|
args: fieldAclArgs,
|
|
@@ -5548,6 +5747,42 @@ function createCliCaptureContainers(input) {
|
|
|
5548
5747
|
};
|
|
5549
5748
|
}
|
|
5550
5749
|
//#endregion
|
|
5750
|
+
//#region src/core/adapters/kintone/appLister.ts
|
|
5751
|
+
const PAGE_LIMIT = 100;
|
|
5752
|
+
const MAX_PAGES = 1e3;
|
|
5753
|
+
var KintoneAppLister = class {
|
|
5754
|
+
constructor(client) {
|
|
5755
|
+
this.client = client;
|
|
5756
|
+
}
|
|
5757
|
+
async getAllApps() {
|
|
5758
|
+
const result = [];
|
|
5759
|
+
try {
|
|
5760
|
+
let offset = 0;
|
|
5761
|
+
let completed = false;
|
|
5762
|
+
for (let page = 0; page < MAX_PAGES; page++) {
|
|
5763
|
+
const { apps } = await this.client.app.getApps({
|
|
5764
|
+
limit: PAGE_LIMIT,
|
|
5765
|
+
offset
|
|
5766
|
+
});
|
|
5767
|
+
for (const app of apps) result.push({
|
|
5768
|
+
appId: app.appId,
|
|
5769
|
+
code: app.code,
|
|
5770
|
+
name: app.name
|
|
5771
|
+
});
|
|
5772
|
+
if (apps.length < PAGE_LIMIT) {
|
|
5773
|
+
completed = true;
|
|
5774
|
+
break;
|
|
5775
|
+
}
|
|
5776
|
+
offset += PAGE_LIMIT;
|
|
5777
|
+
}
|
|
5778
|
+
if (!completed) throw new SystemError(SystemErrorCode.ExternalApiError, `Pagination did not complete within ${MAX_PAGES} pages (fetched ${result.length} apps). This may indicate an API issue.`);
|
|
5779
|
+
return result;
|
|
5780
|
+
} catch (error) {
|
|
5781
|
+
throw wrapKintoneError(error, `Failed to get apps (fetched ${result.length} so far)`);
|
|
5782
|
+
}
|
|
5783
|
+
}
|
|
5784
|
+
};
|
|
5785
|
+
//#endregion
|
|
5551
5786
|
//#region src/core/adapters/kintone/spaceReader.ts
|
|
5552
5787
|
/**
|
|
5553
5788
|
* Reads space information from the kintone REST API.
|
|
@@ -5597,8 +5832,10 @@ function createLocalFileProjectConfigStorage(filePath) {
|
|
|
5597
5832
|
//#endregion
|
|
5598
5833
|
//#region src/core/application/container/initCli.ts
|
|
5599
5834
|
function createInitCliContainer(config) {
|
|
5835
|
+
const client = config.client ?? createKintoneClient(config);
|
|
5600
5836
|
return {
|
|
5601
|
-
spaceReader: new KintoneSpaceReader(
|
|
5837
|
+
spaceReader: new KintoneSpaceReader(client),
|
|
5838
|
+
appLister: new KintoneAppLister(client),
|
|
5602
5839
|
projectConfigStorage: createLocalFileProjectConfigStorage(config.configFilePath)
|
|
5603
5840
|
};
|
|
5604
5841
|
}
|
|
@@ -6300,6 +6537,13 @@ async function captureAllForApp(args) {
|
|
|
6300
6537
|
return results;
|
|
6301
6538
|
}
|
|
6302
6539
|
//#endregion
|
|
6540
|
+
//#region src/core/application/init/fetchAllApps.ts
|
|
6541
|
+
async function fetchAllApps(args) {
|
|
6542
|
+
const apps = await args.container.appLister.getAllApps();
|
|
6543
|
+
if (apps.length === 0) throw new NotFoundError(NotFoundErrorCode.NotFound, "No apps found. Please check your API token permissions.");
|
|
6544
|
+
return apps;
|
|
6545
|
+
}
|
|
6546
|
+
//#endregion
|
|
6303
6547
|
//#region src/core/application/init/fetchSpaceApps.ts
|
|
6304
6548
|
async function fetchSpaceApps(args) {
|
|
6305
6549
|
const apps = await args.container.spaceReader.getSpaceApps(args.input.spaceId);
|
|
@@ -6307,7 +6551,7 @@ async function fetchSpaceApps(args) {
|
|
|
6307
6551
|
return apps;
|
|
6308
6552
|
}
|
|
6309
6553
|
//#endregion
|
|
6310
|
-
//#region src/core/domain/
|
|
6554
|
+
//#region src/core/domain/app/entity.ts
|
|
6311
6555
|
const UNSAFE_PATH_CHARS = /[<>:"/\\|?*\u0000-\u001f]/g;
|
|
6312
6556
|
function sanitizeForFileSystem(name) {
|
|
6313
6557
|
const sanitized = name.replace(UNSAFE_PATH_CHARS, "_").replace(/\.+$/, "");
|
|
@@ -6347,8 +6591,7 @@ const initArgs = {
|
|
|
6347
6591
|
"space-id": {
|
|
6348
6592
|
type: "string",
|
|
6349
6593
|
short: "s",
|
|
6350
|
-
description: "kintone space ID"
|
|
6351
|
-
required: true
|
|
6594
|
+
description: "kintone space ID"
|
|
6352
6595
|
},
|
|
6353
6596
|
domain: kintoneArgs.domain,
|
|
6354
6597
|
username: kintoneArgs.username,
|
|
@@ -6378,15 +6621,52 @@ function printCaptureResults(results) {
|
|
|
6378
6621
|
logError(result.error);
|
|
6379
6622
|
}
|
|
6380
6623
|
}
|
|
6624
|
+
function printDryRunPreview(apps, configPath, configText, output) {
|
|
6625
|
+
p.log.info(pc.dim("(dry-run mode - no files will be written)"));
|
|
6626
|
+
p.log.step(`\nConfig file: ${pc.cyan(configPath)}`);
|
|
6627
|
+
p.log.message(configText);
|
|
6628
|
+
for (const app of apps) {
|
|
6629
|
+
const appName = resolveAppName(app);
|
|
6630
|
+
const paths = buildAppFilePaths(appName, output);
|
|
6631
|
+
p.log.step(`\n=== [${pc.bold(appName)}] (appId: ${app.appId}) ===`);
|
|
6632
|
+
p.log.message(" Files that would be created:");
|
|
6633
|
+
for (const [domain, filePath] of Object.entries(paths)) p.log.message(` ${domain}: ${pc.dim(filePath)}`);
|
|
6634
|
+
}
|
|
6635
|
+
p.log.success("\nDry run complete. No files were written.");
|
|
6636
|
+
}
|
|
6637
|
+
async function captureApps(apps, config) {
|
|
6638
|
+
for (const app of apps) {
|
|
6639
|
+
const appName = resolveAppName(app);
|
|
6640
|
+
p.log.step(`\n=== [${pc.bold(appName)}] (appId: ${app.appId}) ===`);
|
|
6641
|
+
const { containers, paths } = createCliCaptureContainers({
|
|
6642
|
+
baseUrl: config.baseUrl,
|
|
6643
|
+
auth: config.auth,
|
|
6644
|
+
appId: app.appId,
|
|
6645
|
+
guestSpaceId: config.guestSpaceId,
|
|
6646
|
+
appName,
|
|
6647
|
+
baseDir: config.output
|
|
6648
|
+
});
|
|
6649
|
+
const cs = p.spinner();
|
|
6650
|
+
cs.start(`Capturing all domains for ${appName}...`);
|
|
6651
|
+
const results = await captureAllForApp({
|
|
6652
|
+
container: containers,
|
|
6653
|
+
input: { customizeBasePath: dirname(resolve(paths.customize)) }
|
|
6654
|
+
});
|
|
6655
|
+
const successCount = results.filter((r) => r.success).length;
|
|
6656
|
+
const failCount = results.length - successCount;
|
|
6657
|
+
cs.stop(`Captured ${successCount}/${results.length} domains.` + (failCount > 0 ? pc.red(` (${failCount} failed)`) : ""));
|
|
6658
|
+
printCaptureResults(results);
|
|
6659
|
+
}
|
|
6660
|
+
}
|
|
6381
6661
|
var init_default = define({
|
|
6382
6662
|
name: "init",
|
|
6383
|
-
description: "Initialize project from
|
|
6663
|
+
description: "Initialize project from kintone",
|
|
6384
6664
|
args: initArgs,
|
|
6385
6665
|
run: async (ctx) => {
|
|
6386
6666
|
try {
|
|
6387
6667
|
const values = ctx.values;
|
|
6388
6668
|
const spaceId = values["space-id"];
|
|
6389
|
-
if (!/^[1-9]\d*$/.test(spaceId)) throw new ValidationError(ValidationErrorCode.InvalidInput, `Invalid space ID: "${spaceId}" (must be a positive integer, e.g. 1, 42, 100)`);
|
|
6669
|
+
if (spaceId && !/^[1-9]\d*$/.test(spaceId)) throw new ValidationError(ValidationErrorCode.InvalidInput, `Invalid space ID: "${spaceId}" (must be a positive integer, e.g. 1, 42, 100)`);
|
|
6390
6670
|
const kintoneDomain = values.domain ?? process.env.KINTONE_DOMAIN;
|
|
6391
6671
|
if (!kintoneDomain) throw new ValidationError(ValidationErrorCode.InvalidInput, "Missing required configuration:\n KINTONE_DOMAIN is required");
|
|
6392
6672
|
const apiToken = values["api-token"] ?? process.env.KINTONE_API_TOKEN;
|
|
@@ -6399,19 +6679,26 @@ var init_default = define({
|
|
|
6399
6679
|
const configPath = DEFAULT_CONFIG_PATH;
|
|
6400
6680
|
const skipConfirm = values.yes ?? false;
|
|
6401
6681
|
const dryRun = values["dry-run"] ?? false;
|
|
6402
|
-
const { spaceReader, projectConfigStorage } = createInitCliContainer({
|
|
6682
|
+
const { spaceReader, appLister, projectConfigStorage } = createInitCliContainer({
|
|
6403
6683
|
baseUrl,
|
|
6404
6684
|
auth,
|
|
6405
6685
|
guestSpaceId,
|
|
6406
6686
|
configFilePath: configPath
|
|
6407
6687
|
});
|
|
6408
6688
|
const s = p.spinner();
|
|
6409
|
-
|
|
6410
|
-
|
|
6411
|
-
|
|
6412
|
-
|
|
6413
|
-
|
|
6414
|
-
|
|
6689
|
+
let apps;
|
|
6690
|
+
if (spaceId) {
|
|
6691
|
+
s.start("Fetching space info...");
|
|
6692
|
+
apps = await fetchSpaceApps({
|
|
6693
|
+
container: { spaceReader },
|
|
6694
|
+
input: { spaceId }
|
|
6695
|
+
});
|
|
6696
|
+
s.stop(`Found ${apps.length} app(s) in the space.`);
|
|
6697
|
+
} else {
|
|
6698
|
+
s.start("Fetching all apps...");
|
|
6699
|
+
apps = await fetchAllApps({ container: { appLister } });
|
|
6700
|
+
s.stop(`Found ${apps.length} app(s).`);
|
|
6701
|
+
}
|
|
6415
6702
|
p.log.info("Apps:");
|
|
6416
6703
|
for (const app of apps) {
|
|
6417
6704
|
const name = resolveAppName(app);
|
|
@@ -6424,17 +6711,7 @@ var init_default = define({
|
|
|
6424
6711
|
baseDir: output
|
|
6425
6712
|
}, configCodec);
|
|
6426
6713
|
if (dryRun) {
|
|
6427
|
-
|
|
6428
|
-
p.log.step(`\nConfig file: ${pc.cyan(configPath)}`);
|
|
6429
|
-
p.log.message(configText);
|
|
6430
|
-
for (const app of apps) {
|
|
6431
|
-
const appName = resolveAppName(app);
|
|
6432
|
-
const paths = buildAppFilePaths(appName, output);
|
|
6433
|
-
p.log.step(`\n=== [${pc.bold(appName)}] (appId: ${app.appId}) ===`);
|
|
6434
|
-
p.log.message(" Files that would be created:");
|
|
6435
|
-
for (const [domain, filePath] of Object.entries(paths)) p.log.message(` ${domain}: ${pc.dim(filePath)}`);
|
|
6436
|
-
}
|
|
6437
|
-
p.log.success("\nDry run complete. No files were written.");
|
|
6714
|
+
printDryRunPreview(apps, configPath, configText, output);
|
|
6438
6715
|
return;
|
|
6439
6716
|
}
|
|
6440
6717
|
if ((await projectConfigStorage.get()).exists && !skipConfirm) {
|
|
@@ -6446,28 +6723,12 @@ var init_default = define({
|
|
|
6446
6723
|
}
|
|
6447
6724
|
await projectConfigStorage.update(configText);
|
|
6448
6725
|
p.log.success(`Config written to: ${pc.cyan(configPath)}`);
|
|
6449
|
-
|
|
6450
|
-
|
|
6451
|
-
|
|
6452
|
-
|
|
6453
|
-
|
|
6454
|
-
|
|
6455
|
-
appId: app.appId,
|
|
6456
|
-
guestSpaceId,
|
|
6457
|
-
appName,
|
|
6458
|
-
baseDir: output
|
|
6459
|
-
});
|
|
6460
|
-
const cs = p.spinner();
|
|
6461
|
-
cs.start(`Capturing all domains for ${appName}...`);
|
|
6462
|
-
const results = await captureAllForApp({
|
|
6463
|
-
container: containers,
|
|
6464
|
-
input: { customizeBasePath: dirname(resolve(paths.customize)) }
|
|
6465
|
-
});
|
|
6466
|
-
const successCount = results.filter((r) => r.success).length;
|
|
6467
|
-
const failCount = results.length - successCount;
|
|
6468
|
-
cs.stop(`Captured ${successCount}/${results.length} domains.` + (failCount > 0 ? pc.red(` (${failCount} failed)`) : ""));
|
|
6469
|
-
printCaptureResults(results);
|
|
6470
|
-
}
|
|
6726
|
+
await captureApps(apps, {
|
|
6727
|
+
baseUrl,
|
|
6728
|
+
auth,
|
|
6729
|
+
guestSpaceId,
|
|
6730
|
+
output
|
|
6731
|
+
});
|
|
6471
6732
|
p.log.info(`Add authentication settings (auth) to ${pc.cyan(configPath)} or set KINTONE_API_TOKEN / KINTONE_USERNAME + KINTONE_PASSWORD environment variables.`);
|
|
6472
6733
|
p.log.success("\nProject initialization complete.");
|
|
6473
6734
|
} catch (error) {
|
|
@@ -6627,56 +6888,6 @@ async function applyNotification({ container }) {
|
|
|
6627
6888
|
}
|
|
6628
6889
|
}
|
|
6629
6890
|
//#endregion
|
|
6630
|
-
//#region src/cli/notificationConfig.ts
|
|
6631
|
-
const notificationArgs = {
|
|
6632
|
-
...kintoneArgs,
|
|
6633
|
-
...multiAppArgs,
|
|
6634
|
-
"notification-file": {
|
|
6635
|
-
type: "string",
|
|
6636
|
-
description: "Notification file path (default: notification.yaml)"
|
|
6637
|
-
}
|
|
6638
|
-
};
|
|
6639
|
-
const { resolveFilePath: resolveNotificationFilePath, resolveContainerConfig: resolveNotificationContainerConfig, resolveAppContainerConfig: resolveNotificationAppContainerConfig } = createDomainConfigResolver({
|
|
6640
|
-
fileArgKey: "notification-file",
|
|
6641
|
-
envVar: () => process.env.NOTIFICATION_FILE_PATH,
|
|
6642
|
-
appFileField: (a) => a.notificationFile,
|
|
6643
|
-
defaultDir: "notification",
|
|
6644
|
-
defaultFileName: "notification.yaml",
|
|
6645
|
-
buildConfig: (base, filePath) => ({
|
|
6646
|
-
...base,
|
|
6647
|
-
notificationFilePath: filePath
|
|
6648
|
-
})
|
|
6649
|
-
});
|
|
6650
|
-
//#endregion
|
|
6651
|
-
//#region src/cli/commands/notification/apply.ts
|
|
6652
|
-
var apply_default$7 = createApplyCommand({
|
|
6653
|
-
description: "Apply notification settings from YAML to kintone app",
|
|
6654
|
-
args: notificationArgs,
|
|
6655
|
-
spinnerMessage: "Applying notification settings...",
|
|
6656
|
-
spinnerStopMessage: "Notification settings applied.",
|
|
6657
|
-
successMessage: "Notification settings applied successfully.",
|
|
6658
|
-
createContainer: createNotificationCliContainer,
|
|
6659
|
-
applyFn: applyNotification,
|
|
6660
|
-
resolveContainerConfig: resolveNotificationContainerConfig,
|
|
6661
|
-
resolveAppContainerConfig: resolveNotificationAppContainerConfig
|
|
6662
|
-
});
|
|
6663
|
-
//#endregion
|
|
6664
|
-
//#region src/cli/commands/notification/capture.ts
|
|
6665
|
-
var capture_default$8 = createCaptureCommand({
|
|
6666
|
-
description: "Capture current notification settings from kintone app to file",
|
|
6667
|
-
args: notificationArgs,
|
|
6668
|
-
spinnerMessage: "Capturing notification settings...",
|
|
6669
|
-
spinnerStopMessage: "Notification settings captured.",
|
|
6670
|
-
domainLabel: "Notification settings",
|
|
6671
|
-
multiAppSuccessMessage: "All notification captures completed successfully.",
|
|
6672
|
-
createContainer: createNotificationCliContainer,
|
|
6673
|
-
captureFn: captureNotification,
|
|
6674
|
-
saveFn: saveNotification,
|
|
6675
|
-
getConfigFilePath: (config) => config.notificationFilePath,
|
|
6676
|
-
resolveContainerConfig: resolveNotificationContainerConfig,
|
|
6677
|
-
resolveAppContainerConfig: resolveNotificationAppContainerConfig
|
|
6678
|
-
});
|
|
6679
|
-
//#endregion
|
|
6680
6891
|
//#region src/lib/groupByKey.ts
|
|
6681
6892
|
/**
|
|
6682
6893
|
* Groups items by a key function into a Map where each key maps to an array of items.
|
|
@@ -6906,13 +7117,61 @@ async function detectNotificationDiff({ container }) {
|
|
|
6906
7117
|
return NotificationDiffDetector.detect(localConfig, remoteConfig);
|
|
6907
7118
|
}
|
|
6908
7119
|
//#endregion
|
|
7120
|
+
//#region src/cli/notificationConfig.ts
|
|
7121
|
+
const notificationArgs = {
|
|
7122
|
+
...kintoneArgs,
|
|
7123
|
+
...multiAppArgs,
|
|
7124
|
+
"notification-file": {
|
|
7125
|
+
type: "string",
|
|
7126
|
+
description: "Notification file path (default: notification.yaml)"
|
|
7127
|
+
}
|
|
7128
|
+
};
|
|
7129
|
+
const { resolveFilePath: resolveNotificationFilePath, resolveContainerConfig: resolveNotificationContainerConfig, resolveAppContainerConfig: resolveNotificationAppContainerConfig } = createDomainConfigResolver({
|
|
7130
|
+
fileArgKey: "notification-file",
|
|
7131
|
+
envVar: () => process.env.NOTIFICATION_FILE_PATH,
|
|
7132
|
+
appFileField: (a) => a.notificationFile,
|
|
7133
|
+
defaultDir: "notification",
|
|
7134
|
+
defaultFileName: "notification.yaml",
|
|
7135
|
+
buildConfig: (base, filePath) => ({
|
|
7136
|
+
...base,
|
|
7137
|
+
notificationFilePath: filePath
|
|
7138
|
+
})
|
|
7139
|
+
});
|
|
7140
|
+
//#endregion
|
|
6909
7141
|
//#region src/cli/commands/notification/index.ts
|
|
6910
7142
|
var notification_default = define({
|
|
6911
7143
|
name: "notification",
|
|
6912
7144
|
description: "Manage kintone notification settings",
|
|
6913
7145
|
subCommands: {
|
|
6914
|
-
apply:
|
|
6915
|
-
|
|
7146
|
+
apply: createApplyCommand({
|
|
7147
|
+
description: "Apply notification settings from YAML to kintone app",
|
|
7148
|
+
args: notificationArgs,
|
|
7149
|
+
spinnerMessage: "Applying notification settings...",
|
|
7150
|
+
spinnerStopMessage: "Notification settings applied.",
|
|
7151
|
+
successMessage: "Notification settings applied successfully.",
|
|
7152
|
+
createContainer: createNotificationCliContainer,
|
|
7153
|
+
applyFn: applyNotification,
|
|
7154
|
+
diffPreview: {
|
|
7155
|
+
detectDiff: detectNotificationDiff,
|
|
7156
|
+
printResult: printNotificationDiffResult
|
|
7157
|
+
},
|
|
7158
|
+
resolveContainerConfig: resolveNotificationContainerConfig,
|
|
7159
|
+
resolveAppContainerConfig: resolveNotificationAppContainerConfig
|
|
7160
|
+
}),
|
|
7161
|
+
capture: createCaptureCommand({
|
|
7162
|
+
description: "Capture current notification settings from kintone app to file",
|
|
7163
|
+
args: notificationArgs,
|
|
7164
|
+
spinnerMessage: "Capturing notification settings...",
|
|
7165
|
+
spinnerStopMessage: "Notification settings captured.",
|
|
7166
|
+
domainLabel: "Notification settings",
|
|
7167
|
+
multiAppSuccessMessage: "All notification captures completed successfully.",
|
|
7168
|
+
createContainer: createNotificationCliContainer,
|
|
7169
|
+
captureFn: captureNotification,
|
|
7170
|
+
saveFn: saveNotification,
|
|
7171
|
+
getConfigFilePath: (config) => config.notificationFilePath,
|
|
7172
|
+
resolveContainerConfig: resolveNotificationContainerConfig,
|
|
7173
|
+
resolveAppContainerConfig: resolveNotificationAppContainerConfig
|
|
7174
|
+
}),
|
|
6916
7175
|
diff: createDiffCommand({
|
|
6917
7176
|
description: "Compare local notification config with remote kintone app",
|
|
6918
7177
|
args: notificationArgs,
|
|
@@ -6971,56 +7230,6 @@ async function applyPlugin({ container }) {
|
|
|
6971
7230
|
});
|
|
6972
7231
|
}
|
|
6973
7232
|
//#endregion
|
|
6974
|
-
//#region src/cli/pluginConfig.ts
|
|
6975
|
-
const pluginArgs = {
|
|
6976
|
-
...kintoneArgs,
|
|
6977
|
-
...multiAppArgs,
|
|
6978
|
-
"plugin-file": {
|
|
6979
|
-
type: "string",
|
|
6980
|
-
description: "Plugin file path (default: plugins.yaml)"
|
|
6981
|
-
}
|
|
6982
|
-
};
|
|
6983
|
-
const { resolveFilePath: resolvePluginFilePath, resolveContainerConfig: resolvePluginContainerConfig, resolveAppContainerConfig: resolvePluginAppContainerConfig } = createDomainConfigResolver({
|
|
6984
|
-
fileArgKey: "plugin-file",
|
|
6985
|
-
envVar: () => process.env.PLUGIN_FILE_PATH,
|
|
6986
|
-
appFileField: (a) => a.pluginFile,
|
|
6987
|
-
defaultDir: "plugin",
|
|
6988
|
-
defaultFileName: "plugins.yaml",
|
|
6989
|
-
buildConfig: (base, filePath) => ({
|
|
6990
|
-
...base,
|
|
6991
|
-
pluginFilePath: filePath
|
|
6992
|
-
})
|
|
6993
|
-
});
|
|
6994
|
-
//#endregion
|
|
6995
|
-
//#region src/cli/commands/plugin/apply.ts
|
|
6996
|
-
var apply_default$6 = createApplyCommand({
|
|
6997
|
-
description: "Apply plugins from YAML to kintone app",
|
|
6998
|
-
args: pluginArgs,
|
|
6999
|
-
spinnerMessage: "Applying plugins...",
|
|
7000
|
-
spinnerStopMessage: "Plugins applied.",
|
|
7001
|
-
successMessage: "Plugins applied successfully.",
|
|
7002
|
-
createContainer: createPluginCliContainer,
|
|
7003
|
-
applyFn: applyPlugin,
|
|
7004
|
-
resolveContainerConfig: resolvePluginContainerConfig,
|
|
7005
|
-
resolveAppContainerConfig: resolvePluginAppContainerConfig
|
|
7006
|
-
});
|
|
7007
|
-
//#endregion
|
|
7008
|
-
//#region src/cli/commands/plugin/capture.ts
|
|
7009
|
-
var capture_default$7 = createCaptureCommand({
|
|
7010
|
-
description: "Capture current plugins from kintone app to file",
|
|
7011
|
-
args: pluginArgs,
|
|
7012
|
-
spinnerMessage: "Capturing plugins...",
|
|
7013
|
-
spinnerStopMessage: "Plugins captured.",
|
|
7014
|
-
domainLabel: "Plugins",
|
|
7015
|
-
multiAppSuccessMessage: "All plugin captures completed successfully.",
|
|
7016
|
-
createContainer: createPluginCliContainer,
|
|
7017
|
-
captureFn: capturePlugin,
|
|
7018
|
-
saveFn: savePlugin,
|
|
7019
|
-
getConfigFilePath: (config) => config.pluginFilePath,
|
|
7020
|
-
resolveContainerConfig: resolvePluginContainerConfig,
|
|
7021
|
-
resolveAppContainerConfig: resolvePluginAppContainerConfig
|
|
7022
|
-
});
|
|
7023
|
-
//#endregion
|
|
7024
7233
|
//#region src/core/domain/plugin/services/diffDetector.ts
|
|
7025
7234
|
const PluginDiffDetector = { detect: (local, remote) => {
|
|
7026
7235
|
const entries = [];
|
|
@@ -7063,13 +7272,61 @@ async function detectPluginDiff({ container }) {
|
|
|
7063
7272
|
});
|
|
7064
7273
|
}
|
|
7065
7274
|
//#endregion
|
|
7275
|
+
//#region src/cli/pluginConfig.ts
|
|
7276
|
+
const pluginArgs = {
|
|
7277
|
+
...kintoneArgs,
|
|
7278
|
+
...multiAppArgs,
|
|
7279
|
+
"plugin-file": {
|
|
7280
|
+
type: "string",
|
|
7281
|
+
description: "Plugin file path (default: plugins.yaml)"
|
|
7282
|
+
}
|
|
7283
|
+
};
|
|
7284
|
+
const { resolveFilePath: resolvePluginFilePath, resolveContainerConfig: resolvePluginContainerConfig, resolveAppContainerConfig: resolvePluginAppContainerConfig } = createDomainConfigResolver({
|
|
7285
|
+
fileArgKey: "plugin-file",
|
|
7286
|
+
envVar: () => process.env.PLUGIN_FILE_PATH,
|
|
7287
|
+
appFileField: (a) => a.pluginFile,
|
|
7288
|
+
defaultDir: "plugin",
|
|
7289
|
+
defaultFileName: "plugins.yaml",
|
|
7290
|
+
buildConfig: (base, filePath) => ({
|
|
7291
|
+
...base,
|
|
7292
|
+
pluginFilePath: filePath
|
|
7293
|
+
})
|
|
7294
|
+
});
|
|
7295
|
+
//#endregion
|
|
7066
7296
|
//#region src/cli/commands/plugin/index.ts
|
|
7067
7297
|
var plugin_default = define({
|
|
7068
7298
|
name: "plugin",
|
|
7069
7299
|
description: "Manage kintone plugins",
|
|
7070
7300
|
subCommands: {
|
|
7071
|
-
apply:
|
|
7072
|
-
|
|
7301
|
+
apply: createApplyCommand({
|
|
7302
|
+
description: "Apply plugins from YAML to kintone app",
|
|
7303
|
+
args: pluginArgs,
|
|
7304
|
+
spinnerMessage: "Applying plugins...",
|
|
7305
|
+
spinnerStopMessage: "Plugins applied.",
|
|
7306
|
+
successMessage: "Plugins applied successfully.",
|
|
7307
|
+
createContainer: createPluginCliContainer,
|
|
7308
|
+
applyFn: applyPlugin,
|
|
7309
|
+
diffPreview: {
|
|
7310
|
+
detectDiff: detectPluginDiff,
|
|
7311
|
+
printResult: printPluginDiffResult
|
|
7312
|
+
},
|
|
7313
|
+
resolveContainerConfig: resolvePluginContainerConfig,
|
|
7314
|
+
resolveAppContainerConfig: resolvePluginAppContainerConfig
|
|
7315
|
+
}),
|
|
7316
|
+
capture: createCaptureCommand({
|
|
7317
|
+
description: "Capture current plugins from kintone app to file",
|
|
7318
|
+
args: pluginArgs,
|
|
7319
|
+
spinnerMessage: "Capturing plugins...",
|
|
7320
|
+
spinnerStopMessage: "Plugins captured.",
|
|
7321
|
+
domainLabel: "Plugins",
|
|
7322
|
+
multiAppSuccessMessage: "All plugin captures completed successfully.",
|
|
7323
|
+
createContainer: createPluginCliContainer,
|
|
7324
|
+
captureFn: capturePlugin,
|
|
7325
|
+
saveFn: savePlugin,
|
|
7326
|
+
getConfigFilePath: (config) => config.pluginFilePath,
|
|
7327
|
+
resolveContainerConfig: resolvePluginContainerConfig,
|
|
7328
|
+
resolveAppContainerConfig: resolvePluginAppContainerConfig
|
|
7329
|
+
}),
|
|
7073
7330
|
diff: createDiffCommand({
|
|
7074
7331
|
description: "Compare local plugin config with remote kintone app",
|
|
7075
7332
|
args: pluginArgs,
|
|
@@ -7204,59 +7461,6 @@ async function applyProcessManagement({ container }) {
|
|
|
7204
7461
|
};
|
|
7205
7462
|
}
|
|
7206
7463
|
//#endregion
|
|
7207
|
-
//#region src/cli/processConfig.ts
|
|
7208
|
-
const processArgs = {
|
|
7209
|
-
...kintoneArgs,
|
|
7210
|
-
...multiAppArgs,
|
|
7211
|
-
"process-file": {
|
|
7212
|
-
type: "string",
|
|
7213
|
-
description: "Process management file path (default: process.yaml)"
|
|
7214
|
-
}
|
|
7215
|
-
};
|
|
7216
|
-
const { resolveFilePath: resolveProcessFilePath, resolveContainerConfig: resolveProcessContainerConfig, resolveAppContainerConfig: resolveProcessAppContainerConfig } = createDomainConfigResolver({
|
|
7217
|
-
fileArgKey: "process-file",
|
|
7218
|
-
envVar: () => process.env.PROCESS_FILE_PATH,
|
|
7219
|
-
appFileField: (a) => a.processFile,
|
|
7220
|
-
defaultDir: "process",
|
|
7221
|
-
defaultFileName: "process.yaml",
|
|
7222
|
-
buildConfig: (base, filePath) => ({
|
|
7223
|
-
...base,
|
|
7224
|
-
processFilePath: filePath
|
|
7225
|
-
})
|
|
7226
|
-
});
|
|
7227
|
-
//#endregion
|
|
7228
|
-
//#region src/cli/commands/process/apply.ts
|
|
7229
|
-
var apply_default$5 = createApplyCommand({
|
|
7230
|
-
description: "Apply process management settings from YAML to kintone app",
|
|
7231
|
-
args: processArgs,
|
|
7232
|
-
spinnerMessage: "Applying process management settings...",
|
|
7233
|
-
spinnerStopMessage: "Process management settings applied.",
|
|
7234
|
-
successMessage: "Process management settings applied successfully.",
|
|
7235
|
-
createContainer: createProcessManagementCliContainer,
|
|
7236
|
-
applyFn: applyProcessManagement,
|
|
7237
|
-
onResult: (result) => {
|
|
7238
|
-
if (result.enableChanged) p.log.warn(result.newEnable ? "Process management will be ENABLED. This activates workflow processing for this app." : "Process management will be DISABLED. This deactivates workflow processing for this app.");
|
|
7239
|
-
},
|
|
7240
|
-
resolveContainerConfig: resolveProcessContainerConfig,
|
|
7241
|
-
resolveAppContainerConfig: resolveProcessAppContainerConfig
|
|
7242
|
-
});
|
|
7243
|
-
//#endregion
|
|
7244
|
-
//#region src/cli/commands/process/capture.ts
|
|
7245
|
-
var capture_default$6 = createCaptureCommand({
|
|
7246
|
-
description: "Capture current process management settings from kintone app to file",
|
|
7247
|
-
args: processArgs,
|
|
7248
|
-
spinnerMessage: "Capturing process management settings...",
|
|
7249
|
-
spinnerStopMessage: "Process management settings captured.",
|
|
7250
|
-
domainLabel: "Process management settings",
|
|
7251
|
-
multiAppSuccessMessage: "All process management captures completed successfully.",
|
|
7252
|
-
createContainer: createProcessManagementCliContainer,
|
|
7253
|
-
captureFn: captureProcessManagement,
|
|
7254
|
-
saveFn: saveProcessManagement,
|
|
7255
|
-
getConfigFilePath: (config) => config.processFilePath,
|
|
7256
|
-
resolveContainerConfig: resolveProcessContainerConfig,
|
|
7257
|
-
resolveAppContainerConfig: resolveProcessAppContainerConfig
|
|
7258
|
-
});
|
|
7259
|
-
//#endregion
|
|
7260
7464
|
//#region src/core/domain/processManagement/services/diffDetector.ts
|
|
7261
7465
|
function isEntityEqual(a, b) {
|
|
7262
7466
|
if (a.type !== b.type) return false;
|
|
@@ -7368,13 +7572,64 @@ async function detectProcessManagementDiff({ container }) {
|
|
|
7368
7572
|
});
|
|
7369
7573
|
}
|
|
7370
7574
|
//#endregion
|
|
7575
|
+
//#region src/cli/processConfig.ts
|
|
7576
|
+
const processArgs = {
|
|
7577
|
+
...kintoneArgs,
|
|
7578
|
+
...multiAppArgs,
|
|
7579
|
+
"process-file": {
|
|
7580
|
+
type: "string",
|
|
7581
|
+
description: "Process management file path (default: process.yaml)"
|
|
7582
|
+
}
|
|
7583
|
+
};
|
|
7584
|
+
const { resolveFilePath: resolveProcessFilePath, resolveContainerConfig: resolveProcessContainerConfig, resolveAppContainerConfig: resolveProcessAppContainerConfig } = createDomainConfigResolver({
|
|
7585
|
+
fileArgKey: "process-file",
|
|
7586
|
+
envVar: () => process.env.PROCESS_FILE_PATH,
|
|
7587
|
+
appFileField: (a) => a.processFile,
|
|
7588
|
+
defaultDir: "process",
|
|
7589
|
+
defaultFileName: "process.yaml",
|
|
7590
|
+
buildConfig: (base, filePath) => ({
|
|
7591
|
+
...base,
|
|
7592
|
+
processFilePath: filePath
|
|
7593
|
+
})
|
|
7594
|
+
});
|
|
7595
|
+
//#endregion
|
|
7371
7596
|
//#region src/cli/commands/process/index.ts
|
|
7372
7597
|
var process_default = define({
|
|
7373
7598
|
name: "process",
|
|
7374
7599
|
description: "Manage kintone process management settings",
|
|
7375
7600
|
subCommands: {
|
|
7376
|
-
apply:
|
|
7377
|
-
|
|
7601
|
+
apply: createApplyCommand({
|
|
7602
|
+
description: "Apply process management settings from YAML to kintone app",
|
|
7603
|
+
args: processArgs,
|
|
7604
|
+
spinnerMessage: "Applying process management settings...",
|
|
7605
|
+
spinnerStopMessage: "Process management settings applied.",
|
|
7606
|
+
successMessage: "Process management settings applied successfully.",
|
|
7607
|
+
createContainer: createProcessManagementCliContainer,
|
|
7608
|
+
applyFn: applyProcessManagement,
|
|
7609
|
+
onResult: (result) => {
|
|
7610
|
+
if (result.enableChanged) p.log.warn(result.newEnable ? "Process management will be ENABLED. This activates workflow processing for this app." : "Process management will be DISABLED. This deactivates workflow processing for this app.");
|
|
7611
|
+
},
|
|
7612
|
+
diffPreview: {
|
|
7613
|
+
detectDiff: detectProcessManagementDiff,
|
|
7614
|
+
printResult: printProcessDiffResult
|
|
7615
|
+
},
|
|
7616
|
+
resolveContainerConfig: resolveProcessContainerConfig,
|
|
7617
|
+
resolveAppContainerConfig: resolveProcessAppContainerConfig
|
|
7618
|
+
}),
|
|
7619
|
+
capture: createCaptureCommand({
|
|
7620
|
+
description: "Capture current process management settings from kintone app to file",
|
|
7621
|
+
args: processArgs,
|
|
7622
|
+
spinnerMessage: "Capturing process management settings...",
|
|
7623
|
+
spinnerStopMessage: "Process management settings captured.",
|
|
7624
|
+
domainLabel: "Process management settings",
|
|
7625
|
+
multiAppSuccessMessage: "All process management captures completed successfully.",
|
|
7626
|
+
createContainer: createProcessManagementCliContainer,
|
|
7627
|
+
captureFn: captureProcessManagement,
|
|
7628
|
+
saveFn: saveProcessManagement,
|
|
7629
|
+
getConfigFilePath: (config) => config.processFilePath,
|
|
7630
|
+
resolveContainerConfig: resolveProcessContainerConfig,
|
|
7631
|
+
resolveAppContainerConfig: resolveProcessAppContainerConfig
|
|
7632
|
+
}),
|
|
7378
7633
|
diff: createDiffCommand({
|
|
7379
7634
|
description: "Compare local process management settings with remote kintone app",
|
|
7380
7635
|
args: processArgs,
|
|
@@ -7462,56 +7717,6 @@ async function applyRecordPermission({ container }) {
|
|
|
7462
7717
|
});
|
|
7463
7718
|
}
|
|
7464
7719
|
//#endregion
|
|
7465
|
-
//#region src/cli/recordAclConfig.ts
|
|
7466
|
-
const recordAclArgs = {
|
|
7467
|
-
...kintoneArgs,
|
|
7468
|
-
...multiAppArgs,
|
|
7469
|
-
"record-acl-file": {
|
|
7470
|
-
type: "string",
|
|
7471
|
-
description: "Record ACL file path (default: record-acl.yaml)"
|
|
7472
|
-
}
|
|
7473
|
-
};
|
|
7474
|
-
const { resolveFilePath: resolveRecordAclFilePath, resolveContainerConfig: resolveRecordAclContainerConfig, resolveAppContainerConfig: resolveRecordAclAppContainerConfig } = createDomainConfigResolver({
|
|
7475
|
-
fileArgKey: "record-acl-file",
|
|
7476
|
-
envVar: () => process.env.RECORD_ACL_FILE_PATH,
|
|
7477
|
-
appFileField: (a) => a.recordAclFile,
|
|
7478
|
-
defaultDir: "record-acl",
|
|
7479
|
-
defaultFileName: "record-acl.yaml",
|
|
7480
|
-
buildConfig: (base, filePath) => ({
|
|
7481
|
-
...base,
|
|
7482
|
-
recordAclFilePath: filePath
|
|
7483
|
-
})
|
|
7484
|
-
});
|
|
7485
|
-
//#endregion
|
|
7486
|
-
//#region src/cli/commands/record-acl/apply.ts
|
|
7487
|
-
var apply_default$4 = createApplyCommand({
|
|
7488
|
-
description: "Apply record access permissions from YAML to kintone app",
|
|
7489
|
-
args: recordAclArgs,
|
|
7490
|
-
spinnerMessage: "Applying record access permissions...",
|
|
7491
|
-
spinnerStopMessage: "Record access permissions applied.",
|
|
7492
|
-
successMessage: "Record access permissions applied successfully.",
|
|
7493
|
-
createContainer: createRecordPermissionCliContainer,
|
|
7494
|
-
applyFn: applyRecordPermission,
|
|
7495
|
-
resolveContainerConfig: resolveRecordAclContainerConfig,
|
|
7496
|
-
resolveAppContainerConfig: resolveRecordAclAppContainerConfig
|
|
7497
|
-
});
|
|
7498
|
-
//#endregion
|
|
7499
|
-
//#region src/cli/commands/record-acl/capture.ts
|
|
7500
|
-
var capture_default$5 = createCaptureCommand({
|
|
7501
|
-
description: "Capture current record access permissions from kintone app to file",
|
|
7502
|
-
args: recordAclArgs,
|
|
7503
|
-
spinnerMessage: "Capturing record access permissions...",
|
|
7504
|
-
spinnerStopMessage: "Record access permissions captured.",
|
|
7505
|
-
domainLabel: "Record ACL",
|
|
7506
|
-
multiAppSuccessMessage: "All record ACL captures completed successfully.",
|
|
7507
|
-
createContainer: createRecordPermissionCliContainer,
|
|
7508
|
-
captureFn: captureRecordPermission,
|
|
7509
|
-
saveFn: saveRecordPermission,
|
|
7510
|
-
getConfigFilePath: (config) => config.recordAclFilePath,
|
|
7511
|
-
resolveContainerConfig: resolveRecordAclContainerConfig,
|
|
7512
|
-
resolveAppContainerConfig: resolveRecordAclAppContainerConfig
|
|
7513
|
-
});
|
|
7514
|
-
//#endregion
|
|
7515
7720
|
//#region src/core/domain/recordPermission/services/diffDetector.ts
|
|
7516
7721
|
function isRightEqual(a, b) {
|
|
7517
7722
|
return deepEqual(a.entities.map((e) => ({
|
|
@@ -7558,7 +7763,7 @@ function compareRightsForFilter(filterCond, localRights, remoteRights, entries)
|
|
|
7558
7763
|
});
|
|
7559
7764
|
else if (localRight && remoteRight) {
|
|
7560
7765
|
if (!isRightEqual(localRight, remoteRight)) {
|
|
7561
|
-
const detail = localRight.entities.length !== remoteRight.entities.length ? `entities: ${remoteRight.entities.length} -> ${localRight.entities.length}` :
|
|
7766
|
+
const detail = localRight.entities.length !== remoteRight.entities.length ? `entities: ${remoteRight.entities.length} -> ${localRight.entities.length}` : `entities: ${describeRight(localRight)}`;
|
|
7562
7767
|
entries.push({
|
|
7563
7768
|
type: "modified",
|
|
7564
7769
|
filterCond,
|
|
@@ -7592,13 +7797,61 @@ async function detectRecordPermissionDiff({ container }) {
|
|
|
7592
7797
|
});
|
|
7593
7798
|
}
|
|
7594
7799
|
//#endregion
|
|
7800
|
+
//#region src/cli/recordAclConfig.ts
|
|
7801
|
+
const recordAclArgs = {
|
|
7802
|
+
...kintoneArgs,
|
|
7803
|
+
...multiAppArgs,
|
|
7804
|
+
"record-acl-file": {
|
|
7805
|
+
type: "string",
|
|
7806
|
+
description: "Record ACL file path (default: record-acl.yaml)"
|
|
7807
|
+
}
|
|
7808
|
+
};
|
|
7809
|
+
const { resolveFilePath: resolveRecordAclFilePath, resolveContainerConfig: resolveRecordAclContainerConfig, resolveAppContainerConfig: resolveRecordAclAppContainerConfig } = createDomainConfigResolver({
|
|
7810
|
+
fileArgKey: "record-acl-file",
|
|
7811
|
+
envVar: () => process.env.RECORD_ACL_FILE_PATH,
|
|
7812
|
+
appFileField: (a) => a.recordAclFile,
|
|
7813
|
+
defaultDir: "record-acl",
|
|
7814
|
+
defaultFileName: "record-acl.yaml",
|
|
7815
|
+
buildConfig: (base, filePath) => ({
|
|
7816
|
+
...base,
|
|
7817
|
+
recordAclFilePath: filePath
|
|
7818
|
+
})
|
|
7819
|
+
});
|
|
7820
|
+
//#endregion
|
|
7595
7821
|
//#region src/cli/commands/record-acl/index.ts
|
|
7596
7822
|
var record_acl_default = define({
|
|
7597
7823
|
name: "record-acl",
|
|
7598
7824
|
description: "Manage kintone record access permissions",
|
|
7599
7825
|
subCommands: {
|
|
7600
|
-
apply:
|
|
7601
|
-
|
|
7826
|
+
apply: createApplyCommand({
|
|
7827
|
+
description: "Apply record access permissions from YAML to kintone app",
|
|
7828
|
+
args: recordAclArgs,
|
|
7829
|
+
spinnerMessage: "Applying record access permissions...",
|
|
7830
|
+
spinnerStopMessage: "Record access permissions applied.",
|
|
7831
|
+
successMessage: "Record access permissions applied successfully.",
|
|
7832
|
+
createContainer: createRecordPermissionCliContainer,
|
|
7833
|
+
applyFn: applyRecordPermission,
|
|
7834
|
+
diffPreview: {
|
|
7835
|
+
detectDiff: detectRecordPermissionDiff,
|
|
7836
|
+
printResult: printRecordPermissionDiffResult
|
|
7837
|
+
},
|
|
7838
|
+
resolveContainerConfig: resolveRecordAclContainerConfig,
|
|
7839
|
+
resolveAppContainerConfig: resolveRecordAclAppContainerConfig
|
|
7840
|
+
}),
|
|
7841
|
+
capture: createCaptureCommand({
|
|
7842
|
+
description: "Capture current record access permissions from kintone app to file",
|
|
7843
|
+
args: recordAclArgs,
|
|
7844
|
+
spinnerMessage: "Capturing record access permissions...",
|
|
7845
|
+
spinnerStopMessage: "Record access permissions captured.",
|
|
7846
|
+
domainLabel: "Record ACL",
|
|
7847
|
+
multiAppSuccessMessage: "All record ACL captures completed successfully.",
|
|
7848
|
+
createContainer: createRecordPermissionCliContainer,
|
|
7849
|
+
captureFn: captureRecordPermission,
|
|
7850
|
+
saveFn: saveRecordPermission,
|
|
7851
|
+
getConfigFilePath: (config) => config.recordAclFilePath,
|
|
7852
|
+
resolveContainerConfig: resolveRecordAclContainerConfig,
|
|
7853
|
+
resolveAppContainerConfig: resolveRecordAclAppContainerConfig
|
|
7854
|
+
}),
|
|
7602
7855
|
diff: createDiffCommand({
|
|
7603
7856
|
description: "Compare local record permission config with remote kintone app",
|
|
7604
7857
|
args: recordAclArgs,
|
|
@@ -7777,56 +8030,6 @@ async function applyReport({ container }) {
|
|
|
7777
8030
|
});
|
|
7778
8031
|
}
|
|
7779
8032
|
//#endregion
|
|
7780
|
-
//#region src/cli/reportConfig.ts
|
|
7781
|
-
const reportArgs = {
|
|
7782
|
-
...kintoneArgs,
|
|
7783
|
-
...multiAppArgs,
|
|
7784
|
-
"report-file": {
|
|
7785
|
-
type: "string",
|
|
7786
|
-
description: "Report file path (default: reports.yaml)"
|
|
7787
|
-
}
|
|
7788
|
-
};
|
|
7789
|
-
const { resolveFilePath: resolveReportFilePath, resolveContainerConfig: resolveReportContainerConfig, resolveAppContainerConfig: resolveReportAppContainerConfig } = createDomainConfigResolver({
|
|
7790
|
-
fileArgKey: "report-file",
|
|
7791
|
-
envVar: () => process.env.REPORT_FILE_PATH,
|
|
7792
|
-
appFileField: (a) => a.reportFile,
|
|
7793
|
-
defaultDir: "report",
|
|
7794
|
-
defaultFileName: "reports.yaml",
|
|
7795
|
-
buildConfig: (base, filePath) => ({
|
|
7796
|
-
...base,
|
|
7797
|
-
reportFilePath: filePath
|
|
7798
|
-
})
|
|
7799
|
-
});
|
|
7800
|
-
//#endregion
|
|
7801
|
-
//#region src/cli/commands/report/apply.ts
|
|
7802
|
-
var apply_default$3 = createApplyCommand({
|
|
7803
|
-
description: "Apply report settings from YAML to kintone app",
|
|
7804
|
-
args: reportArgs,
|
|
7805
|
-
spinnerMessage: "Applying report settings...",
|
|
7806
|
-
spinnerStopMessage: "Report settings applied.",
|
|
7807
|
-
successMessage: "Report settings applied successfully.",
|
|
7808
|
-
createContainer: createReportCliContainer,
|
|
7809
|
-
applyFn: applyReport,
|
|
7810
|
-
resolveContainerConfig: resolveReportContainerConfig,
|
|
7811
|
-
resolveAppContainerConfig: resolveReportAppContainerConfig
|
|
7812
|
-
});
|
|
7813
|
-
//#endregion
|
|
7814
|
-
//#region src/cli/commands/report/capture.ts
|
|
7815
|
-
var capture_default$4 = createCaptureCommand({
|
|
7816
|
-
description: "Capture current report settings from kintone app to file",
|
|
7817
|
-
args: reportArgs,
|
|
7818
|
-
spinnerMessage: "Capturing report settings...",
|
|
7819
|
-
spinnerStopMessage: "Report settings captured.",
|
|
7820
|
-
domainLabel: "Reports",
|
|
7821
|
-
multiAppSuccessMessage: "All report captures completed successfully.",
|
|
7822
|
-
createContainer: createReportCliContainer,
|
|
7823
|
-
captureFn: captureReport,
|
|
7824
|
-
saveFn: saveReport,
|
|
7825
|
-
getConfigFilePath: (config) => config.reportFilePath,
|
|
7826
|
-
resolveContainerConfig: resolveReportContainerConfig,
|
|
7827
|
-
resolveAppContainerConfig: resolveReportAppContainerConfig
|
|
7828
|
-
});
|
|
7829
|
-
//#endregion
|
|
7830
8033
|
//#region src/core/domain/report/services/diffDetector.ts
|
|
7831
8034
|
function compareReports(local, remote) {
|
|
7832
8035
|
const diffs = [];
|
|
@@ -7875,13 +8078,61 @@ async function detectReportDiff({ container }) {
|
|
|
7875
8078
|
});
|
|
7876
8079
|
}
|
|
7877
8080
|
//#endregion
|
|
8081
|
+
//#region src/cli/reportConfig.ts
|
|
8082
|
+
const reportArgs = {
|
|
8083
|
+
...kintoneArgs,
|
|
8084
|
+
...multiAppArgs,
|
|
8085
|
+
"report-file": {
|
|
8086
|
+
type: "string",
|
|
8087
|
+
description: "Report file path (default: reports.yaml)"
|
|
8088
|
+
}
|
|
8089
|
+
};
|
|
8090
|
+
const { resolveFilePath: resolveReportFilePath, resolveContainerConfig: resolveReportContainerConfig, resolveAppContainerConfig: resolveReportAppContainerConfig } = createDomainConfigResolver({
|
|
8091
|
+
fileArgKey: "report-file",
|
|
8092
|
+
envVar: () => process.env.REPORT_FILE_PATH,
|
|
8093
|
+
appFileField: (a) => a.reportFile,
|
|
8094
|
+
defaultDir: "report",
|
|
8095
|
+
defaultFileName: "reports.yaml",
|
|
8096
|
+
buildConfig: (base, filePath) => ({
|
|
8097
|
+
...base,
|
|
8098
|
+
reportFilePath: filePath
|
|
8099
|
+
})
|
|
8100
|
+
});
|
|
8101
|
+
//#endregion
|
|
7878
8102
|
//#region src/cli/commands/report/index.ts
|
|
7879
8103
|
var report_default = define({
|
|
7880
8104
|
name: "report",
|
|
7881
8105
|
description: "Manage kintone report settings",
|
|
7882
8106
|
subCommands: {
|
|
7883
|
-
apply:
|
|
7884
|
-
|
|
8107
|
+
apply: createApplyCommand({
|
|
8108
|
+
description: "Apply report settings from YAML to kintone app",
|
|
8109
|
+
args: reportArgs,
|
|
8110
|
+
spinnerMessage: "Applying report settings...",
|
|
8111
|
+
spinnerStopMessage: "Report settings applied.",
|
|
8112
|
+
successMessage: "Report settings applied successfully.",
|
|
8113
|
+
createContainer: createReportCliContainer,
|
|
8114
|
+
applyFn: applyReport,
|
|
8115
|
+
diffPreview: {
|
|
8116
|
+
detectDiff: detectReportDiff,
|
|
8117
|
+
printResult: printReportDiffResult
|
|
8118
|
+
},
|
|
8119
|
+
resolveContainerConfig: resolveReportContainerConfig,
|
|
8120
|
+
resolveAppContainerConfig: resolveReportAppContainerConfig
|
|
8121
|
+
}),
|
|
8122
|
+
capture: createCaptureCommand({
|
|
8123
|
+
description: "Capture current report settings from kintone app to file",
|
|
8124
|
+
args: reportArgs,
|
|
8125
|
+
spinnerMessage: "Capturing report settings...",
|
|
8126
|
+
spinnerStopMessage: "Report settings captured.",
|
|
8127
|
+
domainLabel: "Reports",
|
|
8128
|
+
multiAppSuccessMessage: "All report captures completed successfully.",
|
|
8129
|
+
createContainer: createReportCliContainer,
|
|
8130
|
+
captureFn: captureReport,
|
|
8131
|
+
saveFn: saveReport,
|
|
8132
|
+
getConfigFilePath: (config) => config.reportFilePath,
|
|
8133
|
+
resolveContainerConfig: resolveReportContainerConfig,
|
|
8134
|
+
resolveAppContainerConfig: resolveReportAppContainerConfig
|
|
8135
|
+
}),
|
|
7885
8136
|
diff: createDiffCommand({
|
|
7886
8137
|
description: "Compare local report config with remote kintone app",
|
|
7887
8138
|
args: reportArgs,
|
|
@@ -9762,56 +10013,6 @@ async function applyGeneralSettings({ container }) {
|
|
|
9762
10013
|
});
|
|
9763
10014
|
}
|
|
9764
10015
|
//#endregion
|
|
9765
|
-
//#region src/cli/settingsConfig.ts
|
|
9766
|
-
const settingsArgs = {
|
|
9767
|
-
...kintoneArgs,
|
|
9768
|
-
...multiAppArgs,
|
|
9769
|
-
"settings-file": {
|
|
9770
|
-
type: "string",
|
|
9771
|
-
description: "General settings file path (default: settings.yaml)"
|
|
9772
|
-
}
|
|
9773
|
-
};
|
|
9774
|
-
const { resolveFilePath: resolveSettingsFilePath, resolveContainerConfig: resolveSettingsContainerConfig, resolveAppContainerConfig: resolveSettingsAppContainerConfig } = createDomainConfigResolver({
|
|
9775
|
-
fileArgKey: "settings-file",
|
|
9776
|
-
envVar: () => process.env.SETTINGS_FILE_PATH,
|
|
9777
|
-
appFileField: (a) => a.settingsFile,
|
|
9778
|
-
defaultDir: "settings",
|
|
9779
|
-
defaultFileName: "settings.yaml",
|
|
9780
|
-
buildConfig: (base, filePath) => ({
|
|
9781
|
-
...base,
|
|
9782
|
-
settingsFilePath: filePath
|
|
9783
|
-
})
|
|
9784
|
-
});
|
|
9785
|
-
//#endregion
|
|
9786
|
-
//#region src/cli/commands/settings/apply.ts
|
|
9787
|
-
var apply_default$1 = createApplyCommand({
|
|
9788
|
-
description: "Apply general settings from YAML to kintone app",
|
|
9789
|
-
args: settingsArgs,
|
|
9790
|
-
spinnerMessage: "Applying general settings...",
|
|
9791
|
-
spinnerStopMessage: "General settings applied.",
|
|
9792
|
-
successMessage: "General settings applied successfully.",
|
|
9793
|
-
createContainer: createGeneralSettingsCliContainer,
|
|
9794
|
-
applyFn: applyGeneralSettings,
|
|
9795
|
-
resolveContainerConfig: resolveSettingsContainerConfig,
|
|
9796
|
-
resolveAppContainerConfig: resolveSettingsAppContainerConfig
|
|
9797
|
-
});
|
|
9798
|
-
//#endregion
|
|
9799
|
-
//#region src/cli/commands/settings/capture.ts
|
|
9800
|
-
var capture_default$1 = createCaptureCommand({
|
|
9801
|
-
description: "Capture current general settings from kintone app to file",
|
|
9802
|
-
args: settingsArgs,
|
|
9803
|
-
spinnerMessage: "Capturing general settings...",
|
|
9804
|
-
spinnerStopMessage: "General settings captured.",
|
|
9805
|
-
domainLabel: "General settings",
|
|
9806
|
-
multiAppSuccessMessage: "All general settings captures completed successfully.",
|
|
9807
|
-
createContainer: createGeneralSettingsCliContainer,
|
|
9808
|
-
captureFn: captureGeneralSettings,
|
|
9809
|
-
saveFn: saveGeneralSettings,
|
|
9810
|
-
getConfigFilePath: (config) => config.settingsFilePath,
|
|
9811
|
-
resolveContainerConfig: resolveSettingsContainerConfig,
|
|
9812
|
-
resolveAppContainerConfig: resolveSettingsAppContainerConfig
|
|
9813
|
-
});
|
|
9814
|
-
//#endregion
|
|
9815
10016
|
//#region src/core/domain/generalSettings/services/diffDetector.ts
|
|
9816
10017
|
const DEFAULT_STRING = "";
|
|
9817
10018
|
const DEFAULT_BOOLEAN = false;
|
|
@@ -9884,13 +10085,61 @@ async function detectGeneralSettingsDiff({ container }) {
|
|
|
9884
10085
|
});
|
|
9885
10086
|
}
|
|
9886
10087
|
//#endregion
|
|
10088
|
+
//#region src/cli/settingsConfig.ts
|
|
10089
|
+
const settingsArgs = {
|
|
10090
|
+
...kintoneArgs,
|
|
10091
|
+
...multiAppArgs,
|
|
10092
|
+
"settings-file": {
|
|
10093
|
+
type: "string",
|
|
10094
|
+
description: "General settings file path (default: settings.yaml)"
|
|
10095
|
+
}
|
|
10096
|
+
};
|
|
10097
|
+
const { resolveFilePath: resolveSettingsFilePath, resolveContainerConfig: resolveSettingsContainerConfig, resolveAppContainerConfig: resolveSettingsAppContainerConfig } = createDomainConfigResolver({
|
|
10098
|
+
fileArgKey: "settings-file",
|
|
10099
|
+
envVar: () => process.env.SETTINGS_FILE_PATH,
|
|
10100
|
+
appFileField: (a) => a.settingsFile,
|
|
10101
|
+
defaultDir: "settings",
|
|
10102
|
+
defaultFileName: "settings.yaml",
|
|
10103
|
+
buildConfig: (base, filePath) => ({
|
|
10104
|
+
...base,
|
|
10105
|
+
settingsFilePath: filePath
|
|
10106
|
+
})
|
|
10107
|
+
});
|
|
10108
|
+
//#endregion
|
|
9887
10109
|
//#region src/cli/commands/settings/index.ts
|
|
9888
10110
|
var settings_default = define({
|
|
9889
10111
|
name: "settings",
|
|
9890
10112
|
description: "Manage kintone general settings",
|
|
9891
10113
|
subCommands: {
|
|
9892
|
-
apply:
|
|
9893
|
-
|
|
10114
|
+
apply: createApplyCommand({
|
|
10115
|
+
description: "Apply general settings from YAML to kintone app",
|
|
10116
|
+
args: settingsArgs,
|
|
10117
|
+
spinnerMessage: "Applying general settings...",
|
|
10118
|
+
spinnerStopMessage: "General settings applied.",
|
|
10119
|
+
successMessage: "General settings applied successfully.",
|
|
10120
|
+
createContainer: createGeneralSettingsCliContainer,
|
|
10121
|
+
applyFn: applyGeneralSettings,
|
|
10122
|
+
diffPreview: {
|
|
10123
|
+
detectDiff: detectGeneralSettingsDiff,
|
|
10124
|
+
printResult: printGeneralSettingsDiffResult
|
|
10125
|
+
},
|
|
10126
|
+
resolveContainerConfig: resolveSettingsContainerConfig,
|
|
10127
|
+
resolveAppContainerConfig: resolveSettingsAppContainerConfig
|
|
10128
|
+
}),
|
|
10129
|
+
capture: createCaptureCommand({
|
|
10130
|
+
description: "Capture current general settings from kintone app to file",
|
|
10131
|
+
args: settingsArgs,
|
|
10132
|
+
spinnerMessage: "Capturing general settings...",
|
|
10133
|
+
spinnerStopMessage: "General settings captured.",
|
|
10134
|
+
domainLabel: "General settings",
|
|
10135
|
+
multiAppSuccessMessage: "All general settings captures completed successfully.",
|
|
10136
|
+
createContainer: createGeneralSettingsCliContainer,
|
|
10137
|
+
captureFn: captureGeneralSettings,
|
|
10138
|
+
saveFn: saveGeneralSettings,
|
|
10139
|
+
getConfigFilePath: (config) => config.settingsFilePath,
|
|
10140
|
+
resolveContainerConfig: resolveSettingsContainerConfig,
|
|
10141
|
+
resolveAppContainerConfig: resolveSettingsAppContainerConfig
|
|
10142
|
+
}),
|
|
9894
10143
|
diff: createDiffCommand({
|
|
9895
10144
|
description: "Compare local general settings config with remote kintone app",
|
|
9896
10145
|
args: settingsArgs,
|
|
@@ -9974,59 +10223,6 @@ async function applyView({ container }) {
|
|
|
9974
10223
|
return { skippedBuiltinViews };
|
|
9975
10224
|
}
|
|
9976
10225
|
//#endregion
|
|
9977
|
-
//#region src/cli/viewConfig.ts
|
|
9978
|
-
const viewArgs = {
|
|
9979
|
-
...kintoneArgs,
|
|
9980
|
-
...multiAppArgs,
|
|
9981
|
-
"view-file": {
|
|
9982
|
-
type: "string",
|
|
9983
|
-
description: "View file path (default: views.yaml)"
|
|
9984
|
-
}
|
|
9985
|
-
};
|
|
9986
|
-
const { resolveFilePath: resolveViewFilePath, resolveContainerConfig: resolveViewContainerConfig, resolveAppContainerConfig: resolveViewAppContainerConfig } = createDomainConfigResolver({
|
|
9987
|
-
fileArgKey: "view-file",
|
|
9988
|
-
envVar: () => process.env.VIEW_FILE_PATH,
|
|
9989
|
-
appFileField: (a) => a.viewFile,
|
|
9990
|
-
defaultDir: "view",
|
|
9991
|
-
defaultFileName: "views.yaml",
|
|
9992
|
-
buildConfig: (base, filePath) => ({
|
|
9993
|
-
...base,
|
|
9994
|
-
viewFilePath: filePath
|
|
9995
|
-
})
|
|
9996
|
-
});
|
|
9997
|
-
//#endregion
|
|
9998
|
-
//#region src/cli/commands/view/apply.ts
|
|
9999
|
-
var apply_default = createApplyCommand({
|
|
10000
|
-
description: "Apply view settings from YAML to kintone app",
|
|
10001
|
-
args: viewArgs,
|
|
10002
|
-
spinnerMessage: "Applying views...",
|
|
10003
|
-
spinnerStopMessage: "Views applied.",
|
|
10004
|
-
successMessage: "Views applied successfully.",
|
|
10005
|
-
createContainer: createViewCliContainer,
|
|
10006
|
-
applyFn: applyView,
|
|
10007
|
-
onResult: (result) => {
|
|
10008
|
-
if (result.skippedBuiltinViews.length > 0) p.log.warn(`Skipped built-in views: ${result.skippedBuiltinViews.join(", ")}`);
|
|
10009
|
-
},
|
|
10010
|
-
resolveContainerConfig: resolveViewContainerConfig,
|
|
10011
|
-
resolveAppContainerConfig: resolveViewAppContainerConfig
|
|
10012
|
-
});
|
|
10013
|
-
//#endregion
|
|
10014
|
-
//#region src/cli/commands/view/capture.ts
|
|
10015
|
-
var capture_default = createCaptureCommand({
|
|
10016
|
-
description: "Capture current view settings from kintone app to file",
|
|
10017
|
-
args: viewArgs,
|
|
10018
|
-
spinnerMessage: "Capturing views...",
|
|
10019
|
-
spinnerStopMessage: "Views captured.",
|
|
10020
|
-
domainLabel: "Views",
|
|
10021
|
-
multiAppSuccessMessage: "All view captures completed successfully.",
|
|
10022
|
-
createContainer: createViewCliContainer,
|
|
10023
|
-
captureFn: captureView,
|
|
10024
|
-
saveFn: saveView,
|
|
10025
|
-
getConfigFilePath: (config) => config.viewFilePath,
|
|
10026
|
-
resolveContainerConfig: resolveViewContainerConfig,
|
|
10027
|
-
resolveAppContainerConfig: resolveViewAppContainerConfig
|
|
10028
|
-
});
|
|
10029
|
-
//#endregion
|
|
10030
10226
|
//#region src/core/domain/view/services/diffDetector.ts
|
|
10031
10227
|
function checkOptionalStringChange(changes, field, localVal, remoteVal) {
|
|
10032
10228
|
if ((localVal ?? "") !== (remoteVal ?? "")) changes.push(`${field} changed`);
|
|
@@ -10048,10 +10244,10 @@ function describeChanges(local, remote) {
|
|
|
10048
10244
|
}
|
|
10049
10245
|
const ViewDiffDetector = { detect: (localViews, remoteViews) => {
|
|
10050
10246
|
return buildDiffResult(detectRecordDiff(localViews, remoteViews, {
|
|
10051
|
-
onAdded: (name) => ({
|
|
10247
|
+
onAdded: (name, localView) => ({
|
|
10052
10248
|
type: "added",
|
|
10053
10249
|
viewName: name,
|
|
10054
|
-
details:
|
|
10250
|
+
details: `new ${localView.type} view`
|
|
10055
10251
|
}),
|
|
10056
10252
|
onModified: (name, localView, remoteView) => {
|
|
10057
10253
|
const changes = describeChanges(localView, remoteView);
|
|
@@ -10061,10 +10257,10 @@ const ViewDiffDetector = { detect: (localViews, remoteViews) => {
|
|
|
10061
10257
|
details: changes.join(", ")
|
|
10062
10258
|
};
|
|
10063
10259
|
},
|
|
10064
|
-
onDeleted: (name) => ({
|
|
10260
|
+
onDeleted: (name, remoteView) => ({
|
|
10065
10261
|
type: "deleted",
|
|
10066
10262
|
viewName: name,
|
|
10067
|
-
details:
|
|
10263
|
+
details: `removed ${remoteView.type} view`
|
|
10068
10264
|
})
|
|
10069
10265
|
}));
|
|
10070
10266
|
} };
|
|
@@ -10080,13 +10276,64 @@ async function detectViewDiff({ container }) {
|
|
|
10080
10276
|
});
|
|
10081
10277
|
}
|
|
10082
10278
|
//#endregion
|
|
10279
|
+
//#region src/cli/viewConfig.ts
|
|
10280
|
+
const viewArgs = {
|
|
10281
|
+
...kintoneArgs,
|
|
10282
|
+
...multiAppArgs,
|
|
10283
|
+
"view-file": {
|
|
10284
|
+
type: "string",
|
|
10285
|
+
description: "View file path (default: views.yaml)"
|
|
10286
|
+
}
|
|
10287
|
+
};
|
|
10288
|
+
const { resolveFilePath: resolveViewFilePath, resolveContainerConfig: resolveViewContainerConfig, resolveAppContainerConfig: resolveViewAppContainerConfig } = createDomainConfigResolver({
|
|
10289
|
+
fileArgKey: "view-file",
|
|
10290
|
+
envVar: () => process.env.VIEW_FILE_PATH,
|
|
10291
|
+
appFileField: (a) => a.viewFile,
|
|
10292
|
+
defaultDir: "view",
|
|
10293
|
+
defaultFileName: "views.yaml",
|
|
10294
|
+
buildConfig: (base, filePath) => ({
|
|
10295
|
+
...base,
|
|
10296
|
+
viewFilePath: filePath
|
|
10297
|
+
})
|
|
10298
|
+
});
|
|
10299
|
+
//#endregion
|
|
10083
10300
|
//#region src/cli/commands/view/index.ts
|
|
10084
10301
|
var view_default = define({
|
|
10085
10302
|
name: "view",
|
|
10086
10303
|
description: "Manage kintone view settings",
|
|
10087
10304
|
subCommands: {
|
|
10088
|
-
apply:
|
|
10089
|
-
|
|
10305
|
+
apply: createApplyCommand({
|
|
10306
|
+
description: "Apply view settings from YAML to kintone app",
|
|
10307
|
+
args: viewArgs,
|
|
10308
|
+
spinnerMessage: "Applying views...",
|
|
10309
|
+
spinnerStopMessage: "Views applied.",
|
|
10310
|
+
successMessage: "Views applied successfully.",
|
|
10311
|
+
createContainer: createViewCliContainer,
|
|
10312
|
+
applyFn: applyView,
|
|
10313
|
+
onResult: (result) => {
|
|
10314
|
+
if (result.skippedBuiltinViews.length > 0) p.log.warn(`Skipped built-in views: ${result.skippedBuiltinViews.join(", ")}`);
|
|
10315
|
+
},
|
|
10316
|
+
diffPreview: {
|
|
10317
|
+
detectDiff: detectViewDiff,
|
|
10318
|
+
printResult: printViewDiffResult
|
|
10319
|
+
},
|
|
10320
|
+
resolveContainerConfig: resolveViewContainerConfig,
|
|
10321
|
+
resolveAppContainerConfig: resolveViewAppContainerConfig
|
|
10322
|
+
}),
|
|
10323
|
+
capture: createCaptureCommand({
|
|
10324
|
+
description: "Capture current view settings from kintone app to file",
|
|
10325
|
+
args: viewArgs,
|
|
10326
|
+
spinnerMessage: "Capturing views...",
|
|
10327
|
+
spinnerStopMessage: "Views captured.",
|
|
10328
|
+
domainLabel: "Views",
|
|
10329
|
+
multiAppSuccessMessage: "All view captures completed successfully.",
|
|
10330
|
+
createContainer: createViewCliContainer,
|
|
10331
|
+
captureFn: captureView,
|
|
10332
|
+
saveFn: saveView,
|
|
10333
|
+
getConfigFilePath: (config) => config.viewFilePath,
|
|
10334
|
+
resolveContainerConfig: resolveViewContainerConfig,
|
|
10335
|
+
resolveAppContainerConfig: resolveViewAppContainerConfig
|
|
10336
|
+
}),
|
|
10090
10337
|
diff: createDiffCommand({
|
|
10091
10338
|
description: "Compare local view config with remote kintone app",
|
|
10092
10339
|
args: viewArgs,
|