kintone-migrator 0.25.0 → 0.26.1

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 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 runApply(containerConfig) {
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
- await confirmAndDeploy([await runApply(config.resolveContainerConfig(values))], skipConfirm);
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
- await confirmAndDeploy([await runApply(config.resolveAppContainerConfig(app, projectConfig, values))], skipConfirm);
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
- const containers = [];
2547
- await runMultiAppWithHeaders(plan, async (app) => {
2548
- const container = await runApply(config.resolveAppContainerConfig(app, projectConfig, values));
2549
- containers.push({
2550
- appDeployer: container.appDeployer,
2551
- appName: app.name
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
- await confirmAndDeploy(containers, skipConfirm);
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: capture_default$12,
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
  });
@@ -3416,121 +3580,56 @@ function serializeAppRight(right) {
3416
3580
  return {
3417
3581
  entity: {
3418
3582
  type: right.entity.type,
3419
- code: right.entity.code
3420
- },
3421
- includeSubs: right.includeSubs,
3422
- appEditable: right.appEditable,
3423
- recordViewable: right.recordViewable,
3424
- recordAddable: right.recordAddable,
3425
- recordEditable: right.recordEditable,
3426
- recordDeletable: right.recordDeletable,
3427
- recordImportable: right.recordImportable,
3428
- recordExportable: right.recordExportable
3429
- };
3430
- }
3431
- const AppPermissionConfigSerializer = { serialize: (config) => {
3432
- return { rights: config.rights.map(serializeAppRight) };
3433
- } };
3434
- //#endregion
3435
- //#region src/core/application/appPermission/captureAppPermission.ts
3436
- async function captureAppPermission({ container }) {
3437
- return captureFromConfig({
3438
- fetchRemote: () => container.appPermissionConfigurator.getAppPermissions(),
3439
- serialize: ({ rights }) => stringifyConfig(container.configCodec, AppPermissionConfigSerializer.serialize({ rights })),
3440
- getStorage: () => container.appPermissionStorage.get()
3441
- });
3442
- }
3443
- //#endregion
3444
- //#region src/core/application/appPermission/saveAppPermission.ts
3445
- async function saveAppPermission({ container, input }) {
3446
- await container.appPermissionStorage.update(input.configText);
3447
- }
3448
- //#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);
3583
+ code: right.entity.code
3584
+ },
3585
+ includeSubs: right.includeSubs,
3586
+ appEditable: right.appEditable,
3587
+ recordViewable: right.recordViewable,
3588
+ recordAddable: right.recordAddable,
3589
+ recordEditable: right.recordEditable,
3590
+ recordDeletable: right.recordDeletable,
3591
+ recordImportable: right.recordImportable,
3592
+ recordExportable: right.recordExportable
3593
+ };
3594
+ }
3595
+ const AppPermissionConfigSerializer = { serialize: (config) => {
3596
+ return { rights: config.rights.map(serializeAppRight) };
3514
3597
  } };
3515
3598
  //#endregion
3516
- //#region src/core/application/appPermission/detectAppPermissionDiff.ts
3517
- async function detectAppPermissionDiff({ container }) {
3518
- return detectDiffFromConfig({
3519
- getStorage: () => container.appPermissionStorage.get(),
3599
+ //#region src/core/application/appPermission/captureAppPermission.ts
3600
+ async function captureAppPermission({ container }) {
3601
+ return captureFromConfig({
3520
3602
  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"
3603
+ serialize: ({ rights }) => stringifyConfig(container.configCodec, AppPermissionConfigSerializer.serialize({ rights })),
3604
+ getStorage: () => container.appPermissionStorage.get()
3524
3605
  });
3525
3606
  }
3526
3607
  //#endregion
3608
+ //#region src/core/application/appPermission/saveAppPermission.ts
3609
+ async function saveAppPermission({ container, input }) {
3610
+ await container.appPermissionStorage.update(input.configText);
3611
+ }
3612
+ //#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: capture_default$11,
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,
@@ -3682,6 +3781,95 @@ async function applyCustomization({ container, input }) {
3682
3781
  });
3683
3782
  }
3684
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)];
3845
+ }
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"
3870
+ });
3871
+ }
3872
+ //#endregion
3685
3873
  //#region src/cli/customizeConfig.ts
3686
3874
  const customizeArgs = {
3687
3875
  ...kintoneArgs,
@@ -3945,132 +4133,47 @@ var capture_default$10 = define({
3945
4133
  });
3946
4134
  //#endregion
3947
4135
  //#region src/cli/commands/customize/apply.ts
3948
- async function applyCustomizationForApp(config) {
4136
+ function createCustomizationContainer(config) {
3949
4137
  const container = createCustomizationCliContainer(config);
3950
4138
  const filePrefix = deriveFilePrefix(config.customizeFilePath);
3951
- const basePath = join(dirname(resolve(config.customizeFilePath)), filePrefix);
3952
- const s = p.spinner();
3953
- s.start("Applying customization...");
3954
- await applyCustomization({
4139
+ return {
3955
4140
  container,
3956
- input: { basePath }
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 compareResourceLists(localResources, remoteResources, platform, resourceType, warnings) {
4008
- const entries = [];
4009
- const localNames = localResources.map(resourceName);
4010
- const remoteNames = remoteResources.map(remoteResourceName);
4011
- const localNameSet = new Set(localNames);
4012
- const remoteNameSet = new Set(remoteNames);
4013
- const hasDuplicates = localNames.length !== localNameSet.size || remoteNames.length !== remoteNameSet.size;
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
- return entries;
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
- const CustomizationDiffDetector = { detect: (local, remote) => {
4050
- const entries = [];
4051
- const warnings = [];
4052
- const localScope = local.scope ?? "ALL";
4053
- if (localScope !== remote.scope) entries.push({
4054
- type: "modified",
4055
- platform: "config",
4056
- category: "scope",
4057
- name: "scope",
4058
- details: `${remote.scope} -> ${localScope}`
4059
- });
4060
- entries.push(...comparePlatform(local.desktop.js, local.desktop.css, remote.desktop, "desktop", warnings));
4061
- entries.push(...comparePlatform(local.mobile.js, local.mobile.css, remote.mobile, "mobile", warnings));
4062
- return buildDiffResult(entries, warnings);
4063
- } };
4064
- //#endregion
4065
- //#region src/core/application/customization/detectCustomizationDiff.ts
4066
- async function detectCustomizationDiff({ container }) {
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
+ const hasFileContentWarning = result.warnings.some((w) => w.includes("content changes are not detected"));
4172
+ return {
4173
+ container,
4174
+ basePath,
4175
+ hasChanges: !result.isEmpty || hasFileContentWarning
4176
+ };
4074
4177
  }
4075
4178
  //#endregion
4076
4179
  //#region src/cli/commands/customize/index.ts
@@ -4078,7 +4181,96 @@ var customize_default = define({
4078
4181
  name: "customize",
4079
4182
  description: "Manage kintone JS/CSS customizations",
4080
4183
  subCommands: {
4081
- apply: apply_default$9,
4184
+ apply: define({
4185
+ name: "apply",
4186
+ description: "Apply JS/CSS customization to kintone app",
4187
+ args: {
4188
+ ...customizeArgs,
4189
+ ...confirmArgs
4190
+ },
4191
+ run: async (ctx) => {
4192
+ try {
4193
+ const values = ctx.values;
4194
+ const skipConfirm = values.yes === true;
4195
+ await routeMultiApp(values, {
4196
+ singleLegacy: async () => {
4197
+ const { container, basePath, hasChanges } = await runDiffPreview(resolveCustomizeConfig(values));
4198
+ if (!hasChanges) {
4199
+ p.log.success("No changes detected.");
4200
+ return;
4201
+ }
4202
+ if (!skipConfirm) {
4203
+ const shouldContinue = await p.confirm({ message: "Apply these changes?" });
4204
+ if (p.isCancel(shouldContinue) || !shouldContinue) {
4205
+ p.cancel("Apply cancelled.");
4206
+ return;
4207
+ }
4208
+ }
4209
+ await runCustomizationApply(container, basePath);
4210
+ await confirmAndDeploy([container], skipConfirm, "Customization applied and deployed successfully.");
4211
+ },
4212
+ singleApp: async (app, projectConfig) => {
4213
+ const { container, basePath, hasChanges } = await runDiffPreview(resolveCustomizeAppConfig(app, projectConfig, values));
4214
+ if (!hasChanges) {
4215
+ p.log.success("No changes detected.");
4216
+ return;
4217
+ }
4218
+ if (!skipConfirm) {
4219
+ const shouldContinue = await p.confirm({ message: "Apply these changes?" });
4220
+ if (p.isCancel(shouldContinue) || !shouldContinue) {
4221
+ p.cancel("Apply cancelled.");
4222
+ return;
4223
+ }
4224
+ }
4225
+ await runCustomizationApply(container, basePath);
4226
+ await confirmAndDeploy([container], skipConfirm, "Customization applied and deployed successfully.");
4227
+ },
4228
+ multiApp: async (plan, projectConfig) => {
4229
+ const appDiffResults = [];
4230
+ for (const app of plan.orderedApps) {
4231
+ const config = resolveCustomizeAppConfig(app, projectConfig, values);
4232
+ printAppHeader(app.name, app.appId);
4233
+ const { container, basePath, hasChanges } = await runDiffPreview(config);
4234
+ appDiffResults.push({
4235
+ app,
4236
+ container,
4237
+ basePath,
4238
+ hasChanges
4239
+ });
4240
+ }
4241
+ if (!appDiffResults.some((a) => a.hasChanges)) {
4242
+ p.log.success("No changes detected in any app.");
4243
+ return;
4244
+ }
4245
+ if (!skipConfirm) {
4246
+ const shouldContinue = await p.confirm({ message: "Apply these changes to all apps?" });
4247
+ if (p.isCancel(shouldContinue) || !shouldContinue) {
4248
+ p.cancel("Apply cancelled.");
4249
+ return;
4250
+ }
4251
+ }
4252
+ const containers = [];
4253
+ await runMultiAppWithHeaders(plan, async (app) => {
4254
+ const entry = appDiffResults.find((a) => a.app.name === app.name);
4255
+ if (!entry) throw new SystemError(SystemErrorCode.InternalServerError, `App container not found for "${app.name}"`);
4256
+ if (!entry.hasChanges) {
4257
+ p.log.info("No changes. Skipping.");
4258
+ return;
4259
+ }
4260
+ await runCustomizationApply(entry.container, entry.basePath);
4261
+ containers.push({
4262
+ appDeployer: entry.container.appDeployer,
4263
+ appName: app.name
4264
+ });
4265
+ });
4266
+ await confirmAndDeploy(containers, skipConfirm, "Customization applied and deployed successfully.");
4267
+ }
4268
+ });
4269
+ } catch (error) {
4270
+ handleCliError(error);
4271
+ }
4272
+ }
4273
+ }),
4082
4274
  capture: capture_default$10,
4083
4275
  diff: createDiffCommand({
4084
4276
  description: "Compare local customization config with remote kintone app",
@@ -4270,6 +4462,63 @@ async function applyFieldPermission({ container }) {
4270
4462
  });
4271
4463
  }
4272
4464
  //#endregion
4465
+ //#region src/core/domain/fieldPermission/services/diffDetector.ts
4466
+ function isEntitiesEqual$1(a, b) {
4467
+ return deepEqual(a.entities.map((e) => ({
4468
+ accessibility: e.accessibility,
4469
+ type: e.entity.type,
4470
+ code: e.entity.code,
4471
+ includeSubs: e.includeSubs ?? false
4472
+ })), b.entities.map((e) => ({
4473
+ accessibility: e.accessibility,
4474
+ type: e.entity.type,
4475
+ code: e.entity.code,
4476
+ includeSubs: e.includeSubs ?? false
4477
+ })));
4478
+ }
4479
+ function describeEntities(entities) {
4480
+ if (entities.length === 0) return "no entities";
4481
+ return entities.map((e) => {
4482
+ const access = e.accessibility.toLowerCase();
4483
+ return `${e.entity.type}:${e.entity.code}(${access})`;
4484
+ }).join(", ");
4485
+ }
4486
+ const FieldPermissionDiffDetector = { detect: (local, remote) => {
4487
+ const entries = [];
4488
+ const localMap = new Map(local.rights.map((r) => [r.code, r]));
4489
+ const remoteMap = new Map(remote.rights.map((r) => [r.code, r]));
4490
+ for (const [code, localRight] of localMap) {
4491
+ const remoteRight = remoteMap.get(code);
4492
+ if (!remoteRight) entries.push({
4493
+ type: "added",
4494
+ fieldCode: code,
4495
+ details: `entities: ${describeEntities(localRight.entities)}`
4496
+ });
4497
+ else if (!isEntitiesEqual$1(localRight, remoteRight)) entries.push({
4498
+ type: "modified",
4499
+ fieldCode: code,
4500
+ details: `entities: ${describeEntities(localRight.entities)}`
4501
+ });
4502
+ }
4503
+ for (const [code, remoteRight] of remoteMap) if (!localMap.has(code)) entries.push({
4504
+ type: "deleted",
4505
+ fieldCode: code,
4506
+ details: `entities: ${describeEntities(remoteRight.entities)}`
4507
+ });
4508
+ return buildDiffResult(entries);
4509
+ } };
4510
+ //#endregion
4511
+ //#region src/core/application/fieldPermission/detectFieldPermissionDiff.ts
4512
+ async function detectFieldPermissionDiff({ container }) {
4513
+ return detectDiffFromConfig({
4514
+ getStorage: () => container.fieldPermissionStorage.get(),
4515
+ fetchRemote: () => container.fieldPermissionConfigurator.getFieldPermissions(),
4516
+ parseConfig: (content) => parseFieldPermissionConfigText(container.configCodec, content),
4517
+ detect: (local, remote) => FieldPermissionDiffDetector.detect(local, { rights: remote.rights }),
4518
+ notFoundMessage: "Field permission config file not found"
4519
+ });
4520
+ }
4521
+ //#endregion
4273
4522
  //#region src/cli/fieldAclConfig.ts
4274
4523
  const fieldAclArgs = {
4275
4524
  ...kintoneArgs,
@@ -4300,6 +4549,10 @@ var apply_default$8 = createApplyCommand({
4300
4549
  successMessage: "Field access permissions applied successfully.",
4301
4550
  createContainer: createFieldPermissionCliContainer,
4302
4551
  applyFn: applyFieldPermission,
4552
+ diffPreview: {
4553
+ detectDiff: detectFieldPermissionDiff,
4554
+ printResult: printFieldPermissionDiffResult
4555
+ },
4303
4556
  resolveContainerConfig: resolveFieldAclContainerConfig,
4304
4557
  resolveAppContainerConfig: resolveFieldAclAppContainerConfig
4305
4558
  });
@@ -4337,79 +4590,26 @@ async function saveFieldPermission({ container, input }) {
4337
4590
  await container.fieldPermissionStorage.update(input.configText);
4338
4591
  }
4339
4592
  //#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
4593
  //#region src/cli/commands/field-acl/index.ts
4407
4594
  var field_acl_default = define({
4408
4595
  name: "field-acl",
4409
4596
  description: "Manage kintone field access permissions",
4410
4597
  subCommands: {
4411
4598
  apply: apply_default$8,
4412
- capture: capture_default$9,
4599
+ capture: createCaptureCommand({
4600
+ description: "Capture current field access permissions from kintone app to file",
4601
+ args: fieldAclArgs,
4602
+ spinnerMessage: "Capturing field access permissions...",
4603
+ spinnerStopMessage: "Field access permissions captured.",
4604
+ domainLabel: "Field ACL",
4605
+ multiAppSuccessMessage: "All field ACL captures completed successfully.",
4606
+ createContainer: createFieldPermissionCliContainer,
4607
+ captureFn: captureFieldPermission,
4608
+ saveFn: saveFieldPermission,
4609
+ getConfigFilePath: (config) => config.fieldAclFilePath,
4610
+ resolveContainerConfig: resolveFieldAclContainerConfig,
4611
+ resolveAppContainerConfig: resolveFieldAclAppContainerConfig
4612
+ }),
4413
4613
  diff: createDiffCommand({
4414
4614
  description: "Compare local field permission config with remote kintone app",
4415
4615
  args: fieldAclArgs,
@@ -6352,7 +6552,7 @@ async function fetchSpaceApps(args) {
6352
6552
  return apps;
6353
6553
  }
6354
6554
  //#endregion
6355
- //#region src/core/domain/space/entity.ts
6555
+ //#region src/core/domain/app/entity.ts
6356
6556
  const UNSAFE_PATH_CHARS = /[<>:"/\\|?*\u0000-\u001f]/g;
6357
6557
  function sanitizeForFileSystem(name) {
6358
6558
  const sanitized = name.replace(UNSAFE_PATH_CHARS, "_").replace(/\.+$/, "");
@@ -6689,56 +6889,6 @@ async function applyNotification({ container }) {
6689
6889
  }
6690
6890
  }
6691
6891
  //#endregion
6692
- //#region src/cli/notificationConfig.ts
6693
- const notificationArgs = {
6694
- ...kintoneArgs,
6695
- ...multiAppArgs,
6696
- "notification-file": {
6697
- type: "string",
6698
- description: "Notification file path (default: notification.yaml)"
6699
- }
6700
- };
6701
- const { resolveFilePath: resolveNotificationFilePath, resolveContainerConfig: resolveNotificationContainerConfig, resolveAppContainerConfig: resolveNotificationAppContainerConfig } = createDomainConfigResolver({
6702
- fileArgKey: "notification-file",
6703
- envVar: () => process.env.NOTIFICATION_FILE_PATH,
6704
- appFileField: (a) => a.notificationFile,
6705
- defaultDir: "notification",
6706
- defaultFileName: "notification.yaml",
6707
- buildConfig: (base, filePath) => ({
6708
- ...base,
6709
- notificationFilePath: filePath
6710
- })
6711
- });
6712
- //#endregion
6713
- //#region src/cli/commands/notification/apply.ts
6714
- var apply_default$7 = createApplyCommand({
6715
- description: "Apply notification settings from YAML to kintone app",
6716
- args: notificationArgs,
6717
- spinnerMessage: "Applying notification settings...",
6718
- spinnerStopMessage: "Notification settings applied.",
6719
- successMessage: "Notification settings applied successfully.",
6720
- createContainer: createNotificationCliContainer,
6721
- applyFn: applyNotification,
6722
- resolveContainerConfig: resolveNotificationContainerConfig,
6723
- resolveAppContainerConfig: resolveNotificationAppContainerConfig
6724
- });
6725
- //#endregion
6726
- //#region src/cli/commands/notification/capture.ts
6727
- var capture_default$8 = createCaptureCommand({
6728
- description: "Capture current notification settings from kintone app to file",
6729
- args: notificationArgs,
6730
- spinnerMessage: "Capturing notification settings...",
6731
- spinnerStopMessage: "Notification settings captured.",
6732
- domainLabel: "Notification settings",
6733
- multiAppSuccessMessage: "All notification captures completed successfully.",
6734
- createContainer: createNotificationCliContainer,
6735
- captureFn: captureNotification,
6736
- saveFn: saveNotification,
6737
- getConfigFilePath: (config) => config.notificationFilePath,
6738
- resolveContainerConfig: resolveNotificationContainerConfig,
6739
- resolveAppContainerConfig: resolveNotificationAppContainerConfig
6740
- });
6741
- //#endregion
6742
6892
  //#region src/lib/groupByKey.ts
6743
6893
  /**
6744
6894
  * Groups items by a key function into a Map where each key maps to an array of items.
@@ -6968,13 +7118,61 @@ async function detectNotificationDiff({ container }) {
6968
7118
  return NotificationDiffDetector.detect(localConfig, remoteConfig);
6969
7119
  }
6970
7120
  //#endregion
7121
+ //#region src/cli/notificationConfig.ts
7122
+ const notificationArgs = {
7123
+ ...kintoneArgs,
7124
+ ...multiAppArgs,
7125
+ "notification-file": {
7126
+ type: "string",
7127
+ description: "Notification file path (default: notification.yaml)"
7128
+ }
7129
+ };
7130
+ const { resolveFilePath: resolveNotificationFilePath, resolveContainerConfig: resolveNotificationContainerConfig, resolveAppContainerConfig: resolveNotificationAppContainerConfig } = createDomainConfigResolver({
7131
+ fileArgKey: "notification-file",
7132
+ envVar: () => process.env.NOTIFICATION_FILE_PATH,
7133
+ appFileField: (a) => a.notificationFile,
7134
+ defaultDir: "notification",
7135
+ defaultFileName: "notification.yaml",
7136
+ buildConfig: (base, filePath) => ({
7137
+ ...base,
7138
+ notificationFilePath: filePath
7139
+ })
7140
+ });
7141
+ //#endregion
6971
7142
  //#region src/cli/commands/notification/index.ts
6972
7143
  var notification_default = define({
6973
7144
  name: "notification",
6974
7145
  description: "Manage kintone notification settings",
6975
7146
  subCommands: {
6976
- apply: apply_default$7,
6977
- capture: capture_default$8,
7147
+ apply: createApplyCommand({
7148
+ description: "Apply notification settings from YAML to kintone app",
7149
+ args: notificationArgs,
7150
+ spinnerMessage: "Applying notification settings...",
7151
+ spinnerStopMessage: "Notification settings applied.",
7152
+ successMessage: "Notification settings applied successfully.",
7153
+ createContainer: createNotificationCliContainer,
7154
+ applyFn: applyNotification,
7155
+ diffPreview: {
7156
+ detectDiff: detectNotificationDiff,
7157
+ printResult: printNotificationDiffResult
7158
+ },
7159
+ resolveContainerConfig: resolveNotificationContainerConfig,
7160
+ resolveAppContainerConfig: resolveNotificationAppContainerConfig
7161
+ }),
7162
+ capture: createCaptureCommand({
7163
+ description: "Capture current notification settings from kintone app to file",
7164
+ args: notificationArgs,
7165
+ spinnerMessage: "Capturing notification settings...",
7166
+ spinnerStopMessage: "Notification settings captured.",
7167
+ domainLabel: "Notification settings",
7168
+ multiAppSuccessMessage: "All notification captures completed successfully.",
7169
+ createContainer: createNotificationCliContainer,
7170
+ captureFn: captureNotification,
7171
+ saveFn: saveNotification,
7172
+ getConfigFilePath: (config) => config.notificationFilePath,
7173
+ resolveContainerConfig: resolveNotificationContainerConfig,
7174
+ resolveAppContainerConfig: resolveNotificationAppContainerConfig
7175
+ }),
6978
7176
  diff: createDiffCommand({
6979
7177
  description: "Compare local notification config with remote kintone app",
6980
7178
  args: notificationArgs,
@@ -7033,56 +7231,6 @@ async function applyPlugin({ container }) {
7033
7231
  });
7034
7232
  }
7035
7233
  //#endregion
7036
- //#region src/cli/pluginConfig.ts
7037
- const pluginArgs = {
7038
- ...kintoneArgs,
7039
- ...multiAppArgs,
7040
- "plugin-file": {
7041
- type: "string",
7042
- description: "Plugin file path (default: plugins.yaml)"
7043
- }
7044
- };
7045
- const { resolveFilePath: resolvePluginFilePath, resolveContainerConfig: resolvePluginContainerConfig, resolveAppContainerConfig: resolvePluginAppContainerConfig } = createDomainConfigResolver({
7046
- fileArgKey: "plugin-file",
7047
- envVar: () => process.env.PLUGIN_FILE_PATH,
7048
- appFileField: (a) => a.pluginFile,
7049
- defaultDir: "plugin",
7050
- defaultFileName: "plugins.yaml",
7051
- buildConfig: (base, filePath) => ({
7052
- ...base,
7053
- pluginFilePath: filePath
7054
- })
7055
- });
7056
- //#endregion
7057
- //#region src/cli/commands/plugin/apply.ts
7058
- var apply_default$6 = createApplyCommand({
7059
- description: "Apply plugins from YAML to kintone app",
7060
- args: pluginArgs,
7061
- spinnerMessage: "Applying plugins...",
7062
- spinnerStopMessage: "Plugins applied.",
7063
- successMessage: "Plugins applied successfully.",
7064
- createContainer: createPluginCliContainer,
7065
- applyFn: applyPlugin,
7066
- resolveContainerConfig: resolvePluginContainerConfig,
7067
- resolveAppContainerConfig: resolvePluginAppContainerConfig
7068
- });
7069
- //#endregion
7070
- //#region src/cli/commands/plugin/capture.ts
7071
- var capture_default$7 = createCaptureCommand({
7072
- description: "Capture current plugins from kintone app to file",
7073
- args: pluginArgs,
7074
- spinnerMessage: "Capturing plugins...",
7075
- spinnerStopMessage: "Plugins captured.",
7076
- domainLabel: "Plugins",
7077
- multiAppSuccessMessage: "All plugin captures completed successfully.",
7078
- createContainer: createPluginCliContainer,
7079
- captureFn: capturePlugin,
7080
- saveFn: savePlugin,
7081
- getConfigFilePath: (config) => config.pluginFilePath,
7082
- resolveContainerConfig: resolvePluginContainerConfig,
7083
- resolveAppContainerConfig: resolvePluginAppContainerConfig
7084
- });
7085
- //#endregion
7086
7234
  //#region src/core/domain/plugin/services/diffDetector.ts
7087
7235
  const PluginDiffDetector = { detect: (local, remote) => {
7088
7236
  const entries = [];
@@ -7125,13 +7273,61 @@ async function detectPluginDiff({ container }) {
7125
7273
  });
7126
7274
  }
7127
7275
  //#endregion
7276
+ //#region src/cli/pluginConfig.ts
7277
+ const pluginArgs = {
7278
+ ...kintoneArgs,
7279
+ ...multiAppArgs,
7280
+ "plugin-file": {
7281
+ type: "string",
7282
+ description: "Plugin file path (default: plugins.yaml)"
7283
+ }
7284
+ };
7285
+ const { resolveFilePath: resolvePluginFilePath, resolveContainerConfig: resolvePluginContainerConfig, resolveAppContainerConfig: resolvePluginAppContainerConfig } = createDomainConfigResolver({
7286
+ fileArgKey: "plugin-file",
7287
+ envVar: () => process.env.PLUGIN_FILE_PATH,
7288
+ appFileField: (a) => a.pluginFile,
7289
+ defaultDir: "plugin",
7290
+ defaultFileName: "plugins.yaml",
7291
+ buildConfig: (base, filePath) => ({
7292
+ ...base,
7293
+ pluginFilePath: filePath
7294
+ })
7295
+ });
7296
+ //#endregion
7128
7297
  //#region src/cli/commands/plugin/index.ts
7129
7298
  var plugin_default = define({
7130
7299
  name: "plugin",
7131
7300
  description: "Manage kintone plugins",
7132
7301
  subCommands: {
7133
- apply: apply_default$6,
7134
- capture: capture_default$7,
7302
+ apply: createApplyCommand({
7303
+ description: "Apply plugins from YAML to kintone app",
7304
+ args: pluginArgs,
7305
+ spinnerMessage: "Applying plugins...",
7306
+ spinnerStopMessage: "Plugins applied.",
7307
+ successMessage: "Plugins applied successfully.",
7308
+ createContainer: createPluginCliContainer,
7309
+ applyFn: applyPlugin,
7310
+ diffPreview: {
7311
+ detectDiff: detectPluginDiff,
7312
+ printResult: printPluginDiffResult
7313
+ },
7314
+ resolveContainerConfig: resolvePluginContainerConfig,
7315
+ resolveAppContainerConfig: resolvePluginAppContainerConfig
7316
+ }),
7317
+ capture: createCaptureCommand({
7318
+ description: "Capture current plugins from kintone app to file",
7319
+ args: pluginArgs,
7320
+ spinnerMessage: "Capturing plugins...",
7321
+ spinnerStopMessage: "Plugins captured.",
7322
+ domainLabel: "Plugins",
7323
+ multiAppSuccessMessage: "All plugin captures completed successfully.",
7324
+ createContainer: createPluginCliContainer,
7325
+ captureFn: capturePlugin,
7326
+ saveFn: savePlugin,
7327
+ getConfigFilePath: (config) => config.pluginFilePath,
7328
+ resolveContainerConfig: resolvePluginContainerConfig,
7329
+ resolveAppContainerConfig: resolvePluginAppContainerConfig
7330
+ }),
7135
7331
  diff: createDiffCommand({
7136
7332
  description: "Compare local plugin config with remote kintone app",
7137
7333
  args: pluginArgs,
@@ -7266,59 +7462,6 @@ async function applyProcessManagement({ container }) {
7266
7462
  };
7267
7463
  }
7268
7464
  //#endregion
7269
- //#region src/cli/processConfig.ts
7270
- const processArgs = {
7271
- ...kintoneArgs,
7272
- ...multiAppArgs,
7273
- "process-file": {
7274
- type: "string",
7275
- description: "Process management file path (default: process.yaml)"
7276
- }
7277
- };
7278
- const { resolveFilePath: resolveProcessFilePath, resolveContainerConfig: resolveProcessContainerConfig, resolveAppContainerConfig: resolveProcessAppContainerConfig } = createDomainConfigResolver({
7279
- fileArgKey: "process-file",
7280
- envVar: () => process.env.PROCESS_FILE_PATH,
7281
- appFileField: (a) => a.processFile,
7282
- defaultDir: "process",
7283
- defaultFileName: "process.yaml",
7284
- buildConfig: (base, filePath) => ({
7285
- ...base,
7286
- processFilePath: filePath
7287
- })
7288
- });
7289
- //#endregion
7290
- //#region src/cli/commands/process/apply.ts
7291
- var apply_default$5 = createApplyCommand({
7292
- description: "Apply process management settings from YAML to kintone app",
7293
- args: processArgs,
7294
- spinnerMessage: "Applying process management settings...",
7295
- spinnerStopMessage: "Process management settings applied.",
7296
- successMessage: "Process management settings applied successfully.",
7297
- createContainer: createProcessManagementCliContainer,
7298
- applyFn: applyProcessManagement,
7299
- onResult: (result) => {
7300
- 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.");
7301
- },
7302
- resolveContainerConfig: resolveProcessContainerConfig,
7303
- resolveAppContainerConfig: resolveProcessAppContainerConfig
7304
- });
7305
- //#endregion
7306
- //#region src/cli/commands/process/capture.ts
7307
- var capture_default$6 = createCaptureCommand({
7308
- description: "Capture current process management settings from kintone app to file",
7309
- args: processArgs,
7310
- spinnerMessage: "Capturing process management settings...",
7311
- spinnerStopMessage: "Process management settings captured.",
7312
- domainLabel: "Process management settings",
7313
- multiAppSuccessMessage: "All process management captures completed successfully.",
7314
- createContainer: createProcessManagementCliContainer,
7315
- captureFn: captureProcessManagement,
7316
- saveFn: saveProcessManagement,
7317
- getConfigFilePath: (config) => config.processFilePath,
7318
- resolveContainerConfig: resolveProcessContainerConfig,
7319
- resolveAppContainerConfig: resolveProcessAppContainerConfig
7320
- });
7321
- //#endregion
7322
7465
  //#region src/core/domain/processManagement/services/diffDetector.ts
7323
7466
  function isEntityEqual(a, b) {
7324
7467
  if (a.type !== b.type) return false;
@@ -7430,13 +7573,64 @@ async function detectProcessManagementDiff({ container }) {
7430
7573
  });
7431
7574
  }
7432
7575
  //#endregion
7576
+ //#region src/cli/processConfig.ts
7577
+ const processArgs = {
7578
+ ...kintoneArgs,
7579
+ ...multiAppArgs,
7580
+ "process-file": {
7581
+ type: "string",
7582
+ description: "Process management file path (default: process.yaml)"
7583
+ }
7584
+ };
7585
+ const { resolveFilePath: resolveProcessFilePath, resolveContainerConfig: resolveProcessContainerConfig, resolveAppContainerConfig: resolveProcessAppContainerConfig } = createDomainConfigResolver({
7586
+ fileArgKey: "process-file",
7587
+ envVar: () => process.env.PROCESS_FILE_PATH,
7588
+ appFileField: (a) => a.processFile,
7589
+ defaultDir: "process",
7590
+ defaultFileName: "process.yaml",
7591
+ buildConfig: (base, filePath) => ({
7592
+ ...base,
7593
+ processFilePath: filePath
7594
+ })
7595
+ });
7596
+ //#endregion
7433
7597
  //#region src/cli/commands/process/index.ts
7434
7598
  var process_default = define({
7435
7599
  name: "process",
7436
7600
  description: "Manage kintone process management settings",
7437
7601
  subCommands: {
7438
- apply: apply_default$5,
7439
- capture: capture_default$6,
7602
+ apply: createApplyCommand({
7603
+ description: "Apply process management settings from YAML to kintone app",
7604
+ args: processArgs,
7605
+ spinnerMessage: "Applying process management settings...",
7606
+ spinnerStopMessage: "Process management settings applied.",
7607
+ successMessage: "Process management settings applied successfully.",
7608
+ createContainer: createProcessManagementCliContainer,
7609
+ applyFn: applyProcessManagement,
7610
+ onResult: (result) => {
7611
+ 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.");
7612
+ },
7613
+ diffPreview: {
7614
+ detectDiff: detectProcessManagementDiff,
7615
+ printResult: printProcessDiffResult
7616
+ },
7617
+ resolveContainerConfig: resolveProcessContainerConfig,
7618
+ resolveAppContainerConfig: resolveProcessAppContainerConfig
7619
+ }),
7620
+ capture: createCaptureCommand({
7621
+ description: "Capture current process management settings from kintone app to file",
7622
+ args: processArgs,
7623
+ spinnerMessage: "Capturing process management settings...",
7624
+ spinnerStopMessage: "Process management settings captured.",
7625
+ domainLabel: "Process management settings",
7626
+ multiAppSuccessMessage: "All process management captures completed successfully.",
7627
+ createContainer: createProcessManagementCliContainer,
7628
+ captureFn: captureProcessManagement,
7629
+ saveFn: saveProcessManagement,
7630
+ getConfigFilePath: (config) => config.processFilePath,
7631
+ resolveContainerConfig: resolveProcessContainerConfig,
7632
+ resolveAppContainerConfig: resolveProcessAppContainerConfig
7633
+ }),
7440
7634
  diff: createDiffCommand({
7441
7635
  description: "Compare local process management settings with remote kintone app",
7442
7636
  args: processArgs,
@@ -7524,56 +7718,6 @@ async function applyRecordPermission({ container }) {
7524
7718
  });
7525
7719
  }
7526
7720
  //#endregion
7527
- //#region src/cli/recordAclConfig.ts
7528
- const recordAclArgs = {
7529
- ...kintoneArgs,
7530
- ...multiAppArgs,
7531
- "record-acl-file": {
7532
- type: "string",
7533
- description: "Record ACL file path (default: record-acl.yaml)"
7534
- }
7535
- };
7536
- const { resolveFilePath: resolveRecordAclFilePath, resolveContainerConfig: resolveRecordAclContainerConfig, resolveAppContainerConfig: resolveRecordAclAppContainerConfig } = createDomainConfigResolver({
7537
- fileArgKey: "record-acl-file",
7538
- envVar: () => process.env.RECORD_ACL_FILE_PATH,
7539
- appFileField: (a) => a.recordAclFile,
7540
- defaultDir: "record-acl",
7541
- defaultFileName: "record-acl.yaml",
7542
- buildConfig: (base, filePath) => ({
7543
- ...base,
7544
- recordAclFilePath: filePath
7545
- })
7546
- });
7547
- //#endregion
7548
- //#region src/cli/commands/record-acl/apply.ts
7549
- var apply_default$4 = createApplyCommand({
7550
- description: "Apply record access permissions from YAML to kintone app",
7551
- args: recordAclArgs,
7552
- spinnerMessage: "Applying record access permissions...",
7553
- spinnerStopMessage: "Record access permissions applied.",
7554
- successMessage: "Record access permissions applied successfully.",
7555
- createContainer: createRecordPermissionCliContainer,
7556
- applyFn: applyRecordPermission,
7557
- resolveContainerConfig: resolveRecordAclContainerConfig,
7558
- resolveAppContainerConfig: resolveRecordAclAppContainerConfig
7559
- });
7560
- //#endregion
7561
- //#region src/cli/commands/record-acl/capture.ts
7562
- var capture_default$5 = createCaptureCommand({
7563
- description: "Capture current record access permissions from kintone app to file",
7564
- args: recordAclArgs,
7565
- spinnerMessage: "Capturing record access permissions...",
7566
- spinnerStopMessage: "Record access permissions captured.",
7567
- domainLabel: "Record ACL",
7568
- multiAppSuccessMessage: "All record ACL captures completed successfully.",
7569
- createContainer: createRecordPermissionCliContainer,
7570
- captureFn: captureRecordPermission,
7571
- saveFn: saveRecordPermission,
7572
- getConfigFilePath: (config) => config.recordAclFilePath,
7573
- resolveContainerConfig: resolveRecordAclContainerConfig,
7574
- resolveAppContainerConfig: resolveRecordAclAppContainerConfig
7575
- });
7576
- //#endregion
7577
7721
  //#region src/core/domain/recordPermission/services/diffDetector.ts
7578
7722
  function isRightEqual(a, b) {
7579
7723
  return deepEqual(a.entities.map((e) => ({
@@ -7620,7 +7764,7 @@ function compareRightsForFilter(filterCond, localRights, remoteRights, entries)
7620
7764
  });
7621
7765
  else if (localRight && remoteRight) {
7622
7766
  if (!isRightEqual(localRight, remoteRight)) {
7623
- const detail = localRight.entities.length !== remoteRight.entities.length ? `entities: ${remoteRight.entities.length} -> ${localRight.entities.length}` : "entities changed";
7767
+ const detail = localRight.entities.length !== remoteRight.entities.length ? `entities: ${remoteRight.entities.length} -> ${localRight.entities.length}` : `entities: ${describeRight(localRight)}`;
7624
7768
  entries.push({
7625
7769
  type: "modified",
7626
7770
  filterCond,
@@ -7654,13 +7798,61 @@ async function detectRecordPermissionDiff({ container }) {
7654
7798
  });
7655
7799
  }
7656
7800
  //#endregion
7801
+ //#region src/cli/recordAclConfig.ts
7802
+ const recordAclArgs = {
7803
+ ...kintoneArgs,
7804
+ ...multiAppArgs,
7805
+ "record-acl-file": {
7806
+ type: "string",
7807
+ description: "Record ACL file path (default: record-acl.yaml)"
7808
+ }
7809
+ };
7810
+ const { resolveFilePath: resolveRecordAclFilePath, resolveContainerConfig: resolveRecordAclContainerConfig, resolveAppContainerConfig: resolveRecordAclAppContainerConfig } = createDomainConfigResolver({
7811
+ fileArgKey: "record-acl-file",
7812
+ envVar: () => process.env.RECORD_ACL_FILE_PATH,
7813
+ appFileField: (a) => a.recordAclFile,
7814
+ defaultDir: "record-acl",
7815
+ defaultFileName: "record-acl.yaml",
7816
+ buildConfig: (base, filePath) => ({
7817
+ ...base,
7818
+ recordAclFilePath: filePath
7819
+ })
7820
+ });
7821
+ //#endregion
7657
7822
  //#region src/cli/commands/record-acl/index.ts
7658
7823
  var record_acl_default = define({
7659
7824
  name: "record-acl",
7660
7825
  description: "Manage kintone record access permissions",
7661
7826
  subCommands: {
7662
- apply: apply_default$4,
7663
- capture: capture_default$5,
7827
+ apply: createApplyCommand({
7828
+ description: "Apply record access permissions from YAML to kintone app",
7829
+ args: recordAclArgs,
7830
+ spinnerMessage: "Applying record access permissions...",
7831
+ spinnerStopMessage: "Record access permissions applied.",
7832
+ successMessage: "Record access permissions applied successfully.",
7833
+ createContainer: createRecordPermissionCliContainer,
7834
+ applyFn: applyRecordPermission,
7835
+ diffPreview: {
7836
+ detectDiff: detectRecordPermissionDiff,
7837
+ printResult: printRecordPermissionDiffResult
7838
+ },
7839
+ resolveContainerConfig: resolveRecordAclContainerConfig,
7840
+ resolveAppContainerConfig: resolveRecordAclAppContainerConfig
7841
+ }),
7842
+ capture: createCaptureCommand({
7843
+ description: "Capture current record access permissions from kintone app to file",
7844
+ args: recordAclArgs,
7845
+ spinnerMessage: "Capturing record access permissions...",
7846
+ spinnerStopMessage: "Record access permissions captured.",
7847
+ domainLabel: "Record ACL",
7848
+ multiAppSuccessMessage: "All record ACL captures completed successfully.",
7849
+ createContainer: createRecordPermissionCliContainer,
7850
+ captureFn: captureRecordPermission,
7851
+ saveFn: saveRecordPermission,
7852
+ getConfigFilePath: (config) => config.recordAclFilePath,
7853
+ resolveContainerConfig: resolveRecordAclContainerConfig,
7854
+ resolveAppContainerConfig: resolveRecordAclAppContainerConfig
7855
+ }),
7664
7856
  diff: createDiffCommand({
7665
7857
  description: "Compare local record permission config with remote kintone app",
7666
7858
  args: recordAclArgs,
@@ -7839,56 +8031,6 @@ async function applyReport({ container }) {
7839
8031
  });
7840
8032
  }
7841
8033
  //#endregion
7842
- //#region src/cli/reportConfig.ts
7843
- const reportArgs = {
7844
- ...kintoneArgs,
7845
- ...multiAppArgs,
7846
- "report-file": {
7847
- type: "string",
7848
- description: "Report file path (default: reports.yaml)"
7849
- }
7850
- };
7851
- const { resolveFilePath: resolveReportFilePath, resolveContainerConfig: resolveReportContainerConfig, resolveAppContainerConfig: resolveReportAppContainerConfig } = createDomainConfigResolver({
7852
- fileArgKey: "report-file",
7853
- envVar: () => process.env.REPORT_FILE_PATH,
7854
- appFileField: (a) => a.reportFile,
7855
- defaultDir: "report",
7856
- defaultFileName: "reports.yaml",
7857
- buildConfig: (base, filePath) => ({
7858
- ...base,
7859
- reportFilePath: filePath
7860
- })
7861
- });
7862
- //#endregion
7863
- //#region src/cli/commands/report/apply.ts
7864
- var apply_default$3 = createApplyCommand({
7865
- description: "Apply report settings from YAML to kintone app",
7866
- args: reportArgs,
7867
- spinnerMessage: "Applying report settings...",
7868
- spinnerStopMessage: "Report settings applied.",
7869
- successMessage: "Report settings applied successfully.",
7870
- createContainer: createReportCliContainer,
7871
- applyFn: applyReport,
7872
- resolveContainerConfig: resolveReportContainerConfig,
7873
- resolveAppContainerConfig: resolveReportAppContainerConfig
7874
- });
7875
- //#endregion
7876
- //#region src/cli/commands/report/capture.ts
7877
- var capture_default$4 = createCaptureCommand({
7878
- description: "Capture current report settings from kintone app to file",
7879
- args: reportArgs,
7880
- spinnerMessage: "Capturing report settings...",
7881
- spinnerStopMessage: "Report settings captured.",
7882
- domainLabel: "Reports",
7883
- multiAppSuccessMessage: "All report captures completed successfully.",
7884
- createContainer: createReportCliContainer,
7885
- captureFn: captureReport,
7886
- saveFn: saveReport,
7887
- getConfigFilePath: (config) => config.reportFilePath,
7888
- resolveContainerConfig: resolveReportContainerConfig,
7889
- resolveAppContainerConfig: resolveReportAppContainerConfig
7890
- });
7891
- //#endregion
7892
8034
  //#region src/core/domain/report/services/diffDetector.ts
7893
8035
  function compareReports(local, remote) {
7894
8036
  const diffs = [];
@@ -7937,13 +8079,61 @@ async function detectReportDiff({ container }) {
7937
8079
  });
7938
8080
  }
7939
8081
  //#endregion
8082
+ //#region src/cli/reportConfig.ts
8083
+ const reportArgs = {
8084
+ ...kintoneArgs,
8085
+ ...multiAppArgs,
8086
+ "report-file": {
8087
+ type: "string",
8088
+ description: "Report file path (default: reports.yaml)"
8089
+ }
8090
+ };
8091
+ const { resolveFilePath: resolveReportFilePath, resolveContainerConfig: resolveReportContainerConfig, resolveAppContainerConfig: resolveReportAppContainerConfig } = createDomainConfigResolver({
8092
+ fileArgKey: "report-file",
8093
+ envVar: () => process.env.REPORT_FILE_PATH,
8094
+ appFileField: (a) => a.reportFile,
8095
+ defaultDir: "report",
8096
+ defaultFileName: "reports.yaml",
8097
+ buildConfig: (base, filePath) => ({
8098
+ ...base,
8099
+ reportFilePath: filePath
8100
+ })
8101
+ });
8102
+ //#endregion
7940
8103
  //#region src/cli/commands/report/index.ts
7941
8104
  var report_default = define({
7942
8105
  name: "report",
7943
8106
  description: "Manage kintone report settings",
7944
8107
  subCommands: {
7945
- apply: apply_default$3,
7946
- capture: capture_default$4,
8108
+ apply: createApplyCommand({
8109
+ description: "Apply report settings from YAML to kintone app",
8110
+ args: reportArgs,
8111
+ spinnerMessage: "Applying report settings...",
8112
+ spinnerStopMessage: "Report settings applied.",
8113
+ successMessage: "Report settings applied successfully.",
8114
+ createContainer: createReportCliContainer,
8115
+ applyFn: applyReport,
8116
+ diffPreview: {
8117
+ detectDiff: detectReportDiff,
8118
+ printResult: printReportDiffResult
8119
+ },
8120
+ resolveContainerConfig: resolveReportContainerConfig,
8121
+ resolveAppContainerConfig: resolveReportAppContainerConfig
8122
+ }),
8123
+ capture: createCaptureCommand({
8124
+ description: "Capture current report settings from kintone app to file",
8125
+ args: reportArgs,
8126
+ spinnerMessage: "Capturing report settings...",
8127
+ spinnerStopMessage: "Report settings captured.",
8128
+ domainLabel: "Reports",
8129
+ multiAppSuccessMessage: "All report captures completed successfully.",
8130
+ createContainer: createReportCliContainer,
8131
+ captureFn: captureReport,
8132
+ saveFn: saveReport,
8133
+ getConfigFilePath: (config) => config.reportFilePath,
8134
+ resolveContainerConfig: resolveReportContainerConfig,
8135
+ resolveAppContainerConfig: resolveReportAppContainerConfig
8136
+ }),
7947
8137
  diff: createDiffCommand({
7948
8138
  description: "Compare local report config with remote kintone app",
7949
8139
  args: reportArgs,
@@ -9824,56 +10014,6 @@ async function applyGeneralSettings({ container }) {
9824
10014
  });
9825
10015
  }
9826
10016
  //#endregion
9827
- //#region src/cli/settingsConfig.ts
9828
- const settingsArgs = {
9829
- ...kintoneArgs,
9830
- ...multiAppArgs,
9831
- "settings-file": {
9832
- type: "string",
9833
- description: "General settings file path (default: settings.yaml)"
9834
- }
9835
- };
9836
- const { resolveFilePath: resolveSettingsFilePath, resolveContainerConfig: resolveSettingsContainerConfig, resolveAppContainerConfig: resolveSettingsAppContainerConfig } = createDomainConfigResolver({
9837
- fileArgKey: "settings-file",
9838
- envVar: () => process.env.SETTINGS_FILE_PATH,
9839
- appFileField: (a) => a.settingsFile,
9840
- defaultDir: "settings",
9841
- defaultFileName: "settings.yaml",
9842
- buildConfig: (base, filePath) => ({
9843
- ...base,
9844
- settingsFilePath: filePath
9845
- })
9846
- });
9847
- //#endregion
9848
- //#region src/cli/commands/settings/apply.ts
9849
- var apply_default$1 = createApplyCommand({
9850
- description: "Apply general settings from YAML to kintone app",
9851
- args: settingsArgs,
9852
- spinnerMessage: "Applying general settings...",
9853
- spinnerStopMessage: "General settings applied.",
9854
- successMessage: "General settings applied successfully.",
9855
- createContainer: createGeneralSettingsCliContainer,
9856
- applyFn: applyGeneralSettings,
9857
- resolveContainerConfig: resolveSettingsContainerConfig,
9858
- resolveAppContainerConfig: resolveSettingsAppContainerConfig
9859
- });
9860
- //#endregion
9861
- //#region src/cli/commands/settings/capture.ts
9862
- var capture_default$1 = createCaptureCommand({
9863
- description: "Capture current general settings from kintone app to file",
9864
- args: settingsArgs,
9865
- spinnerMessage: "Capturing general settings...",
9866
- spinnerStopMessage: "General settings captured.",
9867
- domainLabel: "General settings",
9868
- multiAppSuccessMessage: "All general settings captures completed successfully.",
9869
- createContainer: createGeneralSettingsCliContainer,
9870
- captureFn: captureGeneralSettings,
9871
- saveFn: saveGeneralSettings,
9872
- getConfigFilePath: (config) => config.settingsFilePath,
9873
- resolveContainerConfig: resolveSettingsContainerConfig,
9874
- resolveAppContainerConfig: resolveSettingsAppContainerConfig
9875
- });
9876
- //#endregion
9877
10017
  //#region src/core/domain/generalSettings/services/diffDetector.ts
9878
10018
  const DEFAULT_STRING = "";
9879
10019
  const DEFAULT_BOOLEAN = false;
@@ -9946,13 +10086,61 @@ async function detectGeneralSettingsDiff({ container }) {
9946
10086
  });
9947
10087
  }
9948
10088
  //#endregion
10089
+ //#region src/cli/settingsConfig.ts
10090
+ const settingsArgs = {
10091
+ ...kintoneArgs,
10092
+ ...multiAppArgs,
10093
+ "settings-file": {
10094
+ type: "string",
10095
+ description: "General settings file path (default: settings.yaml)"
10096
+ }
10097
+ };
10098
+ const { resolveFilePath: resolveSettingsFilePath, resolveContainerConfig: resolveSettingsContainerConfig, resolveAppContainerConfig: resolveSettingsAppContainerConfig } = createDomainConfigResolver({
10099
+ fileArgKey: "settings-file",
10100
+ envVar: () => process.env.SETTINGS_FILE_PATH,
10101
+ appFileField: (a) => a.settingsFile,
10102
+ defaultDir: "settings",
10103
+ defaultFileName: "settings.yaml",
10104
+ buildConfig: (base, filePath) => ({
10105
+ ...base,
10106
+ settingsFilePath: filePath
10107
+ })
10108
+ });
10109
+ //#endregion
9949
10110
  //#region src/cli/commands/settings/index.ts
9950
10111
  var settings_default = define({
9951
10112
  name: "settings",
9952
10113
  description: "Manage kintone general settings",
9953
10114
  subCommands: {
9954
- apply: apply_default$1,
9955
- capture: capture_default$1,
10115
+ apply: createApplyCommand({
10116
+ description: "Apply general settings from YAML to kintone app",
10117
+ args: settingsArgs,
10118
+ spinnerMessage: "Applying general settings...",
10119
+ spinnerStopMessage: "General settings applied.",
10120
+ successMessage: "General settings applied successfully.",
10121
+ createContainer: createGeneralSettingsCliContainer,
10122
+ applyFn: applyGeneralSettings,
10123
+ diffPreview: {
10124
+ detectDiff: detectGeneralSettingsDiff,
10125
+ printResult: printGeneralSettingsDiffResult
10126
+ },
10127
+ resolveContainerConfig: resolveSettingsContainerConfig,
10128
+ resolveAppContainerConfig: resolveSettingsAppContainerConfig
10129
+ }),
10130
+ capture: createCaptureCommand({
10131
+ description: "Capture current general settings from kintone app to file",
10132
+ args: settingsArgs,
10133
+ spinnerMessage: "Capturing general settings...",
10134
+ spinnerStopMessage: "General settings captured.",
10135
+ domainLabel: "General settings",
10136
+ multiAppSuccessMessage: "All general settings captures completed successfully.",
10137
+ createContainer: createGeneralSettingsCliContainer,
10138
+ captureFn: captureGeneralSettings,
10139
+ saveFn: saveGeneralSettings,
10140
+ getConfigFilePath: (config) => config.settingsFilePath,
10141
+ resolveContainerConfig: resolveSettingsContainerConfig,
10142
+ resolveAppContainerConfig: resolveSettingsAppContainerConfig
10143
+ }),
9956
10144
  diff: createDiffCommand({
9957
10145
  description: "Compare local general settings config with remote kintone app",
9958
10146
  args: settingsArgs,
@@ -10036,59 +10224,6 @@ async function applyView({ container }) {
10036
10224
  return { skippedBuiltinViews };
10037
10225
  }
10038
10226
  //#endregion
10039
- //#region src/cli/viewConfig.ts
10040
- const viewArgs = {
10041
- ...kintoneArgs,
10042
- ...multiAppArgs,
10043
- "view-file": {
10044
- type: "string",
10045
- description: "View file path (default: views.yaml)"
10046
- }
10047
- };
10048
- const { resolveFilePath: resolveViewFilePath, resolveContainerConfig: resolveViewContainerConfig, resolveAppContainerConfig: resolveViewAppContainerConfig } = createDomainConfigResolver({
10049
- fileArgKey: "view-file",
10050
- envVar: () => process.env.VIEW_FILE_PATH,
10051
- appFileField: (a) => a.viewFile,
10052
- defaultDir: "view",
10053
- defaultFileName: "views.yaml",
10054
- buildConfig: (base, filePath) => ({
10055
- ...base,
10056
- viewFilePath: filePath
10057
- })
10058
- });
10059
- //#endregion
10060
- //#region src/cli/commands/view/apply.ts
10061
- var apply_default = createApplyCommand({
10062
- description: "Apply view settings from YAML to kintone app",
10063
- args: viewArgs,
10064
- spinnerMessage: "Applying views...",
10065
- spinnerStopMessage: "Views applied.",
10066
- successMessage: "Views applied successfully.",
10067
- createContainer: createViewCliContainer,
10068
- applyFn: applyView,
10069
- onResult: (result) => {
10070
- if (result.skippedBuiltinViews.length > 0) p.log.warn(`Skipped built-in views: ${result.skippedBuiltinViews.join(", ")}`);
10071
- },
10072
- resolveContainerConfig: resolveViewContainerConfig,
10073
- resolveAppContainerConfig: resolveViewAppContainerConfig
10074
- });
10075
- //#endregion
10076
- //#region src/cli/commands/view/capture.ts
10077
- var capture_default = createCaptureCommand({
10078
- description: "Capture current view settings from kintone app to file",
10079
- args: viewArgs,
10080
- spinnerMessage: "Capturing views...",
10081
- spinnerStopMessage: "Views captured.",
10082
- domainLabel: "Views",
10083
- multiAppSuccessMessage: "All view captures completed successfully.",
10084
- createContainer: createViewCliContainer,
10085
- captureFn: captureView,
10086
- saveFn: saveView,
10087
- getConfigFilePath: (config) => config.viewFilePath,
10088
- resolveContainerConfig: resolveViewContainerConfig,
10089
- resolveAppContainerConfig: resolveViewAppContainerConfig
10090
- });
10091
- //#endregion
10092
10227
  //#region src/core/domain/view/services/diffDetector.ts
10093
10228
  function checkOptionalStringChange(changes, field, localVal, remoteVal) {
10094
10229
  if ((localVal ?? "") !== (remoteVal ?? "")) changes.push(`${field} changed`);
@@ -10110,10 +10245,10 @@ function describeChanges(local, remote) {
10110
10245
  }
10111
10246
  const ViewDiffDetector = { detect: (localViews, remoteViews) => {
10112
10247
  return buildDiffResult(detectRecordDiff(localViews, remoteViews, {
10113
- onAdded: (name) => ({
10248
+ onAdded: (name, localView) => ({
10114
10249
  type: "added",
10115
10250
  viewName: name,
10116
- details: "new view"
10251
+ details: `new ${localView.type} view`
10117
10252
  }),
10118
10253
  onModified: (name, localView, remoteView) => {
10119
10254
  const changes = describeChanges(localView, remoteView);
@@ -10123,10 +10258,10 @@ const ViewDiffDetector = { detect: (localViews, remoteViews) => {
10123
10258
  details: changes.join(", ")
10124
10259
  };
10125
10260
  },
10126
- onDeleted: (name) => ({
10261
+ onDeleted: (name, remoteView) => ({
10127
10262
  type: "deleted",
10128
10263
  viewName: name,
10129
- details: "removed"
10264
+ details: `removed ${remoteView.type} view`
10130
10265
  })
10131
10266
  }));
10132
10267
  } };
@@ -10142,13 +10277,64 @@ async function detectViewDiff({ container }) {
10142
10277
  });
10143
10278
  }
10144
10279
  //#endregion
10280
+ //#region src/cli/viewConfig.ts
10281
+ const viewArgs = {
10282
+ ...kintoneArgs,
10283
+ ...multiAppArgs,
10284
+ "view-file": {
10285
+ type: "string",
10286
+ description: "View file path (default: views.yaml)"
10287
+ }
10288
+ };
10289
+ const { resolveFilePath: resolveViewFilePath, resolveContainerConfig: resolveViewContainerConfig, resolveAppContainerConfig: resolveViewAppContainerConfig } = createDomainConfigResolver({
10290
+ fileArgKey: "view-file",
10291
+ envVar: () => process.env.VIEW_FILE_PATH,
10292
+ appFileField: (a) => a.viewFile,
10293
+ defaultDir: "view",
10294
+ defaultFileName: "views.yaml",
10295
+ buildConfig: (base, filePath) => ({
10296
+ ...base,
10297
+ viewFilePath: filePath
10298
+ })
10299
+ });
10300
+ //#endregion
10145
10301
  //#region src/cli/commands/view/index.ts
10146
10302
  var view_default = define({
10147
10303
  name: "view",
10148
10304
  description: "Manage kintone view settings",
10149
10305
  subCommands: {
10150
- apply: apply_default,
10151
- capture: capture_default,
10306
+ apply: createApplyCommand({
10307
+ description: "Apply view settings from YAML to kintone app",
10308
+ args: viewArgs,
10309
+ spinnerMessage: "Applying views...",
10310
+ spinnerStopMessage: "Views applied.",
10311
+ successMessage: "Views applied successfully.",
10312
+ createContainer: createViewCliContainer,
10313
+ applyFn: applyView,
10314
+ onResult: (result) => {
10315
+ if (result.skippedBuiltinViews.length > 0) p.log.warn(`Skipped built-in views: ${result.skippedBuiltinViews.join(", ")}`);
10316
+ },
10317
+ diffPreview: {
10318
+ detectDiff: detectViewDiff,
10319
+ printResult: printViewDiffResult
10320
+ },
10321
+ resolveContainerConfig: resolveViewContainerConfig,
10322
+ resolveAppContainerConfig: resolveViewAppContainerConfig
10323
+ }),
10324
+ capture: createCaptureCommand({
10325
+ description: "Capture current view settings from kintone app to file",
10326
+ args: viewArgs,
10327
+ spinnerMessage: "Capturing views...",
10328
+ spinnerStopMessage: "Views captured.",
10329
+ domainLabel: "Views",
10330
+ multiAppSuccessMessage: "All view captures completed successfully.",
10331
+ createContainer: createViewCliContainer,
10332
+ captureFn: captureView,
10333
+ saveFn: saveView,
10334
+ getConfigFilePath: (config) => config.viewFilePath,
10335
+ resolveContainerConfig: resolveViewContainerConfig,
10336
+ resolveAppContainerConfig: resolveViewAppContainerConfig
10337
+ }),
10152
10338
  diff: createDiffCommand({
10153
10339
  description: "Compare local view config with remote kintone app",
10154
10340
  args: viewArgs,