kintone-migrator 0.24.7 → 0.24.8

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
@@ -2,13 +2,13 @@
2
2
  import { createRequire } from "node:module";
3
3
  import "dotenv/config";
4
4
  import { cli, define } from "gunshi";
5
- import * as p from "@clack/prompts";
6
- import { parse, stringify } from "yaml";
7
5
  import { KintoneRestAPIClient, KintoneRestAPIError } from "@kintone/rest-api-client";
8
6
  import { access, mkdir, readFile, writeFile } from "node:fs/promises";
9
7
  import { basename, dirname, extname, join, resolve } from "node:path";
8
+ import { parse, stringify } from "yaml";
10
9
  import { realpathSync } from "node:fs";
11
10
  import * as v from "valibot";
11
+ import * as p from "@clack/prompts";
12
12
  import pc from "picocolors";
13
13
  //#region src/lib/error.ts
14
14
  var AnyError = class extends Error {
@@ -20,8 +20,6 @@ var AnyError = class extends Error {
20
20
  //#endregion
21
21
  //#region src/core/domain/action/errorCode.ts
22
22
  const ActionErrorCode = {
23
- AcEmptyConfigText: "AC_EMPTY_CONFIG_TEXT",
24
- AcInvalidConfigYaml: "AC_INVALID_CONFIG_YAML",
25
23
  AcInvalidConfigStructure: "AC_INVALID_CONFIG_STRUCTURE",
26
24
  AcInvalidSrcType: "AC_INVALID_SRC_TYPE",
27
25
  AcInvalidEntityType: "AC_INVALID_ENTITY_TYPE",
@@ -30,16 +28,10 @@ const ActionErrorCode = {
30
28
  };
31
29
  //#endregion
32
30
  //#region src/core/domain/adminNotes/errorCode.ts
33
- const AdminNotesErrorCode = {
34
- AnEmptyConfigText: "AN_EMPTY_CONFIG_TEXT",
35
- AnInvalidConfigYaml: "AN_INVALID_CONFIG_YAML",
36
- AnInvalidConfigStructure: "AN_INVALID_CONFIG_STRUCTURE"
37
- };
31
+ const AdminNotesErrorCode = { AnInvalidConfigStructure: "AN_INVALID_CONFIG_STRUCTURE" };
38
32
  //#endregion
39
33
  //#region src/core/domain/appPermission/errorCode.ts
40
34
  const AppPermissionErrorCode = {
41
- ApEmptyConfigText: "AP_EMPTY_CONFIG_TEXT",
42
- ApInvalidConfigYaml: "AP_INVALID_CONFIG_YAML",
43
35
  ApInvalidConfigStructure: "AP_INVALID_CONFIG_STRUCTURE",
44
36
  ApInvalidEntityType: "AP_INVALID_ENTITY_TYPE",
45
37
  ApInvalidBooleanField: "AP_INVALID_BOOLEAN_FIELD",
@@ -49,8 +41,6 @@ const AppPermissionErrorCode = {
49
41
  //#endregion
50
42
  //#region src/core/domain/customization/errorCode.ts
51
43
  const CustomizationErrorCode = {
52
- CzEmptyConfigText: "CZ_EMPTY_CONFIG_TEXT",
53
- CzInvalidConfigYaml: "CZ_INVALID_CONFIG_YAML",
54
44
  CzInvalidConfigStructure: "CZ_INVALID_CONFIG_STRUCTURE",
55
45
  CzInvalidScope: "CZ_INVALID_SCOPE",
56
46
  CzInvalidResourceType: "CZ_INVALID_RESOURCE_TYPE",
@@ -59,8 +49,6 @@ const CustomizationErrorCode = {
59
49
  //#endregion
60
50
  //#region src/core/domain/fieldPermission/errorCode.ts
61
51
  const FieldPermissionErrorCode = {
62
- FpEmptyConfigText: "FP_EMPTY_CONFIG_TEXT",
63
- FpInvalidConfigYaml: "FP_INVALID_CONFIG_YAML",
64
52
  FpInvalidConfigStructure: "FP_INVALID_CONFIG_STRUCTURE",
65
53
  FpInvalidAccessibility: "FP_INVALID_ACCESSIBILITY",
66
54
  FpInvalidEntityType: "FP_INVALID_ENTITY_TYPE",
@@ -74,8 +62,6 @@ const FieldPermissionErrorCode = {
74
62
  const FormSchemaErrorCode = {
75
63
  FsEmptyFieldCode: "FS_EMPTY_FIELD_CODE",
76
64
  FsInvalidFieldCode: "FS_INVALID_FIELD_CODE",
77
- FsEmptySchemaText: "FS_EMPTY_SCHEMA_TEXT",
78
- FsInvalidSchemaFormat: "FS_INVALID_SCHEMA_FORMAT",
79
65
  FsInvalidSchemaStructure: "FS_INVALID_SCHEMA_STRUCTURE",
80
66
  FsDuplicateFieldCode: "FS_DUPLICATE_FIELD_CODE",
81
67
  FsInvalidFieldType: "FS_INVALID_FIELD_TYPE",
@@ -86,8 +72,6 @@ const FormSchemaErrorCode = {
86
72
  //#endregion
87
73
  //#region src/core/domain/generalSettings/errorCode.ts
88
74
  const GeneralSettingsErrorCode = {
89
- GsEmptyConfigText: "GS_EMPTY_CONFIG_TEXT",
90
- GsInvalidConfigYaml: "GS_INVALID_CONFIG_YAML",
91
75
  GsInvalidConfigStructure: "GS_INVALID_CONFIG_STRUCTURE",
92
76
  GsInvalidTheme: "GS_INVALID_THEME",
93
77
  GsInvalidIconType: "GS_INVALID_ICON_TYPE",
@@ -97,8 +81,6 @@ const GeneralSettingsErrorCode = {
97
81
  //#endregion
98
82
  //#region src/core/domain/notification/errorCode.ts
99
83
  const NotificationErrorCode = {
100
- NtEmptyConfigText: "NT_EMPTY_CONFIG_TEXT",
101
- NtInvalidConfigYaml: "NT_INVALID_CONFIG_YAML",
102
84
  NtInvalidConfigStructure: "NT_INVALID_CONFIG_STRUCTURE",
103
85
  NtInvalidEntityType: "NT_INVALID_ENTITY_TYPE",
104
86
  NtEmptyEntityCode: "NT_EMPTY_ENTITY_CODE",
@@ -110,8 +92,6 @@ const NotificationErrorCode = {
110
92
  //#endregion
111
93
  //#region src/core/domain/plugin/errorCode.ts
112
94
  const PluginErrorCode = {
113
- PlEmptyConfigText: "PL_EMPTY_CONFIG_TEXT",
114
- PlInvalidConfigYaml: "PL_INVALID_CONFIG_YAML",
115
95
  PlInvalidConfigStructure: "PL_INVALID_CONFIG_STRUCTURE",
116
96
  PlEmptyPluginId: "PL_EMPTY_PLUGIN_ID",
117
97
  PlDuplicatePluginId: "PL_DUPLICATE_PLUGIN_ID"
@@ -119,8 +99,6 @@ const PluginErrorCode = {
119
99
  //#endregion
120
100
  //#region src/core/domain/processManagement/errorCode.ts
121
101
  const ProcessManagementErrorCode = {
122
- PmEmptyConfigText: "PM_EMPTY_CONFIG_TEXT",
123
- PmInvalidConfigYaml: "PM_INVALID_CONFIG_YAML",
124
102
  PmInvalidConfigStructure: "PM_INVALID_CONFIG_STRUCTURE",
125
103
  PmInvalidAssigneeType: "PM_INVALID_ASSIGNEE_TYPE",
126
104
  PmInvalidEntityType: "PM_INVALID_ENTITY_TYPE",
@@ -143,8 +121,6 @@ const ProjectConfigErrorCode = {
143
121
  //#endregion
144
122
  //#region src/core/domain/recordPermission/errorCode.ts
145
123
  const RecordPermissionErrorCode = {
146
- RpEmptyConfigText: "RP_EMPTY_CONFIG_TEXT",
147
- RpInvalidConfigYaml: "RP_INVALID_CONFIG_YAML",
148
124
  RpInvalidConfigStructure: "RP_INVALID_CONFIG_STRUCTURE",
149
125
  RpInvalidEntityType: "RP_INVALID_ENTITY_TYPE",
150
126
  RpEmptyEntityCode: "RP_EMPTY_ENTITY_CODE",
@@ -154,8 +130,6 @@ const RecordPermissionErrorCode = {
154
130
  //#endregion
155
131
  //#region src/core/domain/report/errorCode.ts
156
132
  const ReportErrorCode = {
157
- RtEmptyConfigText: "RT_EMPTY_CONFIG_TEXT",
158
- RtInvalidConfigYaml: "RT_INVALID_CONFIG_YAML",
159
133
  RtInvalidConfigStructure: "RT_INVALID_CONFIG_STRUCTURE",
160
134
  RtInvalidChartType: "RT_INVALID_CHART_TYPE",
161
135
  RtInvalidChartMode: "RT_INVALID_CHART_MODE",
@@ -165,21 +139,14 @@ const ReportErrorCode = {
165
139
  //#region src/core/domain/seedData/errorCode.ts
166
140
  const SeedDataErrorCode = {
167
141
  SdEmptyUpsertKey: "SD_EMPTY_UPSERT_KEY",
168
- SdEmptySeedText: "SD_EMPTY_SEED_TEXT",
169
- SdInvalidSeedYaml: "SD_INVALID_SEED_YAML",
170
142
  SdInvalidSeedStructure: "SD_INVALID_SEED_STRUCTURE",
171
143
  SdDuplicateKeyValue: "SD_DUPLICATE_KEY_VALUE",
172
144
  SdMissingKeyField: "SD_MISSING_KEY_FIELD",
173
145
  SdInvalidKeyFieldValue: "SD_INVALID_KEY_FIELD_VALUE"
174
146
  };
175
147
  //#endregion
176
- //#region src/core/domain/services/errorCode.ts
177
- const DomainServiceErrorCode = { YamlSerializationFailed: "DS_YAML_SERIALIZATION_FAILED" };
178
- //#endregion
179
148
  //#region src/core/domain/view/errorCode.ts
180
149
  const ViewErrorCode = {
181
- VwEmptyConfigText: "VW_EMPTY_CONFIG_TEXT",
182
- VwInvalidConfigYaml: "VW_INVALID_CONFIG_YAML",
183
150
  VwInvalidConfigStructure: "VW_INVALID_CONFIG_STRUCTURE",
184
151
  VwInvalidViewType: "VW_INVALID_VIEW_TYPE",
185
152
  VwInvalidDeviceType: "VW_INVALID_DEVICE_TYPE",
@@ -201,7 +168,6 @@ const ViewErrorCode = {
201
168
  ...RecordPermissionErrorCode,
202
169
  ...ReportErrorCode,
203
170
  ...SeedDataErrorCode,
204
- ...DomainServiceErrorCode,
205
171
  ...ViewErrorCode
206
172
  });
207
173
  /**
@@ -414,16 +380,9 @@ function parseEntityBase(raw, index, validTypes, errorCodes, options) {
414
380
  };
415
381
  }
416
382
  //#endregion
417
- //#region src/core/domain/services/yamlConfigParser.ts
418
- function parseYamlConfig(rawText, errorCodes, domainLabel) {
419
- if (rawText.trim().length === 0) throw new BusinessRuleError(errorCodes.emptyConfigText, `${domainLabel} config text is empty`);
420
- let parsed;
421
- try {
422
- parsed = parse(rawText);
423
- } catch (error) {
424
- throw new BusinessRuleError(errorCodes.invalidConfigYaml, `Failed to parse ${domainLabel} YAML: ${error instanceof Error ? error.message : String(error)}`, error);
425
- }
426
- if (!isRecord(parsed)) throw new BusinessRuleError(errorCodes.invalidConfigStructure, `${domainLabel} config must be a YAML object`);
383
+ //#region src/core/domain/services/configValidator.ts
384
+ function validateParsedConfig(parsed, invalidConfigStructure, domainLabel) {
385
+ if (!isRecord(parsed)) throw new BusinessRuleError(invalidConfigStructure, `${domainLabel} config must be an object`);
427
386
  return parsed;
428
387
  }
429
388
  const VALID_SRC_TYPES = new Set(["FIELD", "RECORD_URL"]);
@@ -489,12 +448,8 @@ function parseActionConfig(raw, actionName) {
489
448
  filterCond
490
449
  };
491
450
  }
492
- const ActionConfigParser = { parse: (rawText) => {
493
- const obj = parseYamlConfig(rawText, {
494
- emptyConfigText: ActionErrorCode.AcEmptyConfigText,
495
- invalidConfigYaml: ActionErrorCode.AcInvalidConfigYaml,
496
- invalidConfigStructure: ActionErrorCode.AcInvalidConfigStructure
497
- }, "Action");
451
+ const ActionConfigParser = { parse: (parsed) => {
452
+ const obj = validateParsedConfig(parsed, ActionErrorCode.AcInvalidConfigStructure, "Action");
498
453
  if (!isRecord(obj.actions)) throw new BusinessRuleError(ActionErrorCode.AcInvalidConfigStructure, "Config must have an \"actions\" object");
499
454
  const rawActions = obj.actions;
500
455
  const actions = {};
@@ -507,16 +462,30 @@ const ActionConfigParser = { parse: (rawText) => {
507
462
  return { actions };
508
463
  } };
509
464
  //#endregion
465
+ //#region src/core/application/parseConfigText.ts
466
+ function parseConfigText(codec, rawText, domainLabel) {
467
+ if (rawText.trim().length === 0) throw new ValidationError(ValidationErrorCode.InvalidInput, `${domainLabel} config text is empty`);
468
+ let result;
469
+ try {
470
+ result = codec.parse(rawText);
471
+ } catch (cause) {
472
+ throw new ValidationError(ValidationErrorCode.InvalidInput, `Failed to parse ${domainLabel}: ${cause instanceof Error ? cause.message : String(cause)}`, cause);
473
+ }
474
+ if (result == null) throw new ValidationError(ValidationErrorCode.InvalidInput, `${domainLabel} config is empty (no data after parsing)`);
475
+ return result;
476
+ }
477
+ //#endregion
510
478
  //#region src/core/application/action/parseConfig.ts
511
- function parseActionConfigText(rawText) {
512
- return wrapBusinessRuleError(() => ActionConfigParser.parse(rawText));
479
+ function parseActionConfigText(codec, rawText) {
480
+ const parsed = parseConfigText(codec, rawText, "Action");
481
+ return wrapBusinessRuleError(() => ActionConfigParser.parse(parsed));
513
482
  }
514
483
  //#endregion
515
484
  //#region src/core/application/action/applyAction.ts
516
485
  async function applyAction({ container }) {
517
486
  await applyFromConfig({
518
487
  getStorage: () => container.actionStorage.get(),
519
- parseConfig: parseActionConfigText,
488
+ parseConfig: (content) => parseActionConfigText(container.configCodec, content),
520
489
  fetchRemote: () => container.actionConfigurator.getActions(),
521
490
  update: async (config, current) => {
522
491
  await container.actionConfigurator.updateActions({
@@ -783,6 +752,16 @@ function createLocalFileStorage(filePath, label = "file") {
783
752
  function createLocalFileActionStorage(filePath) {
784
753
  return createLocalFileStorage(filePath, "action file");
785
754
  }
755
+ //#endregion
756
+ //#region src/core/adapters/yaml/configCodec.ts
757
+ const configCodec = {
758
+ parse: (text) => parse(text),
759
+ stringify: (data) => stringify(data, {
760
+ lineWidth: 0,
761
+ defaultKeyType: "PLAIN",
762
+ defaultStringType: "PLAIN"
763
+ })
764
+ };
786
765
  const VALID_SCOPES = new Set([
787
766
  "ALL",
788
767
  "ADMIN",
@@ -1067,6 +1046,16 @@ function assertStringArray(value, fieldPath) {
1067
1046
  function assertString(value, fieldPath) {
1068
1047
  if (typeof value !== "string") throw new SystemError(SystemErrorCode.ExternalApiError, `Expected string at ${fieldPath}, got ${typeof value}`);
1069
1048
  }
1049
+ function normalizeDefaultValue(type, rest) {
1050
+ if (type === "RADIO_BUTTON" || type === "DROP_DOWN") {
1051
+ if (rest.defaultValue !== void 0 && Array.isArray(rest.defaultValue)) {
1052
+ const arr = rest.defaultValue;
1053
+ rest.defaultValue = arr.length > 0 ? String(arr[0]) : "";
1054
+ }
1055
+ } else if (type === "CHECK_BOX" || type === "MULTI_SELECT") {
1056
+ if (rest.defaultValue !== void 0 && typeof rest.defaultValue === "string") rest.defaultValue = rest.defaultValue === "" ? [] : [rest.defaultValue];
1057
+ }
1058
+ }
1070
1059
  function fromKintoneProperty(prop) {
1071
1060
  const { type, code, label, noLabel, ...rest } = prop;
1072
1061
  const base = {
@@ -1116,14 +1105,7 @@ function fromKintoneProperty(prop) {
1116
1105
  };
1117
1106
  }
1118
1107
  if (!KNOWN_FIELD_TYPES.has(type)) throw new SystemError(SystemErrorCode.ExternalApiError, `Unknown field type: ${type}`);
1119
- if (type === "RADIO_BUTTON" || type === "DROP_DOWN") {
1120
- if (rest.defaultValue !== void 0 && Array.isArray(rest.defaultValue)) {
1121
- const arr = rest.defaultValue;
1122
- rest.defaultValue = arr.length > 0 ? String(arr[0]) : "";
1123
- }
1124
- } else if (type === "CHECK_BOX" || type === "MULTI_SELECT") {
1125
- if (rest.defaultValue !== void 0 && typeof rest.defaultValue === "string") rest.defaultValue = rest.defaultValue === "" ? [] : [rest.defaultValue];
1126
- }
1108
+ normalizeDefaultValue(type, rest);
1127
1109
  return {
1128
1110
  ...base,
1129
1111
  type,
@@ -1453,6 +1435,16 @@ function toKintoneFieldValue(value) {
1453
1435
  }
1454
1436
  return { value };
1455
1437
  }
1438
+ function flattenSubtableRow(row) {
1439
+ const cells = row.value;
1440
+ const flat = {};
1441
+ for (const [k, cell] of Object.entries(cells)) {
1442
+ if (SYSTEM_FIELDS.has(k)) continue;
1443
+ if (Array.isArray(cell.value)) flat[k] = cell.value.map(String);
1444
+ else flat[k] = cell.value === null || cell.value === void 0 ? "" : String(cell.value);
1445
+ }
1446
+ return flat;
1447
+ }
1456
1448
  function fromKintoneFieldValue(value) {
1457
1449
  if (value === null || value === void 0) return "";
1458
1450
  if (typeof value === "string") return value;
@@ -1463,16 +1455,7 @@ function fromKintoneFieldValue(value) {
1463
1455
  if (typeof first === "string") return value;
1464
1456
  if (typeof first === "object" && first !== null) {
1465
1457
  if ("code" in first && !("value" in first)) return value.filter(hasCode).map((u) => ({ code: u.code }));
1466
- if ("value" in first) return value.filter(isKintoneSubtableRow).map((row) => {
1467
- const cells = row.value;
1468
- const flat = {};
1469
- for (const [k, cell] of Object.entries(cells)) {
1470
- if (SYSTEM_FIELDS.has(k)) continue;
1471
- if (Array.isArray(cell.value)) flat[k] = cell.value.map(String);
1472
- else flat[k] = cell.value === null || cell.value === void 0 ? "" : String(cell.value);
1473
- }
1474
- return flat;
1475
- });
1458
+ if ("value" in first) return value.filter(isKintoneSubtableRow).map(flattenSubtableRow);
1476
1459
  }
1477
1460
  return value.map(String);
1478
1461
  }
@@ -1611,6 +1594,7 @@ function buildKintoneAuth(auth) {
1611
1594
  function createCliContainer(config) {
1612
1595
  const client = config.client ?? createKintoneClient(config);
1613
1596
  return {
1597
+ configCodec,
1614
1598
  formConfigurator: new KintoneFormConfigurator(client, config.appId),
1615
1599
  schemaStorage: createLocalFileSchemaStorage(config.schemaFilePath),
1616
1600
  appDeployer: new KintoneAppDeployer(client, config.appId)
@@ -1618,6 +1602,7 @@ function createCliContainer(config) {
1618
1602
  }
1619
1603
  function createSeedCliContainer(config) {
1620
1604
  return {
1605
+ configCodec,
1621
1606
  recordManager: new KintoneRecordManager(config.client ?? createKintoneClient(config), config.appId),
1622
1607
  seedStorage: createLocalFileSeedStorage(config.seedFilePath)
1623
1608
  };
@@ -1625,6 +1610,7 @@ function createSeedCliContainer(config) {
1625
1610
  function createCustomizationCliContainer(config) {
1626
1611
  const client = config.client ?? createKintoneClient(config);
1627
1612
  return {
1613
+ configCodec,
1628
1614
  customizationConfigurator: new KintoneCustomizationConfigurator(client, config.appId),
1629
1615
  customizationStorage: createLocalFileCustomizationStorage(config.customizeFilePath),
1630
1616
  fileUploader: new KintoneFileUploader(client, process.cwd()),
@@ -1647,6 +1633,7 @@ function createKintoneClient(config) {
1647
1633
  function createActionCliContainer(config) {
1648
1634
  const client = config.client ?? createKintoneClient(config);
1649
1635
  return {
1636
+ configCodec,
1650
1637
  actionConfigurator: new KintoneActionConfigurator(client, config.appId),
1651
1638
  actionStorage: createLocalFileActionStorage(config.actionFilePath),
1652
1639
  appDeployer: new KintoneAppDeployer(client, config.appId)
@@ -1929,18 +1916,13 @@ const ConfigParser = { parse: parseProjectConfig };
1929
1916
  //#endregion
1930
1917
  //#region src/core/application/projectConfig/loadProjectConfig.ts
1931
1918
  /**
1932
- * Pure function that parses YAML text into a ProjectConfig.
1933
- * Intentionally does not use the container/context object pattern
1934
- * because it has no external dependencies (no I/O, no ports).
1919
+ * Pure function that parses config text into a ProjectConfig.
1920
+ * Intentionally does not use the container/context object pattern because
1921
+ * it has no I/O ports; the ConfigCodec is a lightweight serialization port.
1935
1922
  */
1936
- function loadProjectConfig(input) {
1937
- let raw;
1938
- try {
1939
- raw = parse(input.content);
1940
- } catch (cause) {
1941
- throw new ValidationError(ValidationErrorCode.InvalidInput, "Invalid YAML syntax in config file", cause);
1942
- }
1943
- return ConfigParser.parse(raw);
1923
+ function loadProjectConfig(input, codec) {
1924
+ const raw = parseConfigText(codec, input.content, "Project config");
1925
+ return wrapBusinessRuleError(() => ConfigParser.parse(raw));
1944
1926
  }
1945
1927
  //#endregion
1946
1928
  //#region src/core/domain/projectConfig/services/dependencyResolver.ts
@@ -2317,7 +2299,7 @@ async function resolveTarget(values) {
2317
2299
  if (values["app-id"]) return { mode: "single-legacy" };
2318
2300
  const configPath = values.config ?? "kintone-migrator.yaml";
2319
2301
  if (values.app || values.all) {
2320
- const config = loadProjectConfig({ content: await readConfigFile(configPath) });
2302
+ const config = loadProjectConfig({ content: await readConfigFile(configPath) }, configCodec);
2321
2303
  const plan = resolveExecutionPlan({
2322
2304
  config,
2323
2305
  appName: values.app,
@@ -2336,7 +2318,7 @@ async function resolveTarget(values) {
2336
2318
  }
2337
2319
  if (await configFileExists(configPath)) return {
2338
2320
  mode: "list-apps",
2339
- config: loadProjectConfig({ content: await readConfigFile(configPath) })
2321
+ config: loadProjectConfig({ content: await readConfigFile(configPath) }, configCodec)
2340
2322
  };
2341
2323
  return { mode: "single-legacy" };
2342
2324
  }
@@ -2524,65 +2506,74 @@ const { resolveFilePath: resolveActionFilePath, resolveContainerConfig: resolveA
2524
2506
  })
2525
2507
  });
2526
2508
  //#endregion
2527
- //#region src/cli/commands/action/apply.ts
2528
- async function runAction(config) {
2529
- const container = createActionCliContainer(config);
2530
- const s = p.spinner();
2531
- s.start("Applying action settings...");
2532
- await applyAction({ container });
2533
- s.stop("Action settings applied.");
2534
- p.log.success("Action settings applied successfully.");
2535
- return container;
2536
- }
2537
- var apply_default$12 = define({
2538
- name: "apply",
2539
- description: "Apply action settings from YAML to kintone app",
2540
- args: {
2541
- ...actionArgs,
2542
- ...confirmArgs
2543
- },
2544
- run: async (ctx) => {
2509
+ //#region src/cli/commands/applyCommandFactory.ts
2510
+ function createApplyCommand(config) {
2511
+ async function runApply(containerConfig) {
2512
+ const container = config.createContainer(containerConfig);
2513
+ const s = p.spinner();
2514
+ s.start(config.spinnerMessage);
2515
+ let result;
2545
2516
  try {
2546
- const values = ctx.values;
2547
- const skipConfirm = values.yes === true;
2548
- await routeMultiApp(values, {
2549
- singleLegacy: async () => {
2550
- await confirmAndDeploy([await runAction(resolveActionContainerConfig(values))], skipConfirm);
2551
- },
2552
- singleApp: async (app, projectConfig) => {
2553
- await confirmAndDeploy([await runAction(resolveActionAppContainerConfig(app, projectConfig, values))], skipConfirm);
2554
- },
2555
- multiApp: async (plan, projectConfig) => {
2556
- const containers = [];
2557
- await runMultiAppWithHeaders(plan, async (app) => {
2558
- const container = await runAction(resolveActionAppContainerConfig(app, projectConfig, values));
2559
- containers.push({
2560
- appDeployer: container.appDeployer,
2561
- appName: app.name
2562
- });
2563
- });
2564
- await confirmAndDeploy(containers, skipConfirm);
2565
- }
2566
- });
2517
+ result = await config.applyFn({ container });
2567
2518
  } catch (error) {
2568
- handleCliError(error);
2519
+ s.stop("Apply failed.");
2520
+ throw error;
2569
2521
  }
2522
+ s.stop(config.spinnerStopMessage);
2523
+ config.onResult?.(result);
2524
+ p.log.success(config.successMessage);
2525
+ return container;
2570
2526
  }
2571
- });
2572
- //#endregion
2573
- //#region src/core/domain/services/yamlConfigSerializer.ts
2574
- function serializeToYaml(data) {
2575
- try {
2576
- return stringify(data, {
2577
- lineWidth: 0,
2578
- defaultKeyType: "PLAIN",
2579
- defaultStringType: "PLAIN"
2580
- });
2581
- } catch (error) {
2582
- throw new BusinessRuleError(DomainServiceErrorCode.YamlSerializationFailed, `Failed to serialize config to YAML: ${error instanceof Error ? error.message : String(error)}`, error);
2583
- }
2527
+ return define({
2528
+ name: "apply",
2529
+ description: config.description,
2530
+ args: {
2531
+ ...config.args,
2532
+ ...confirmArgs
2533
+ },
2534
+ run: async (ctx) => {
2535
+ try {
2536
+ const values = ctx.values;
2537
+ const skipConfirm = ctx.values.yes === true;
2538
+ await routeMultiApp(values, {
2539
+ singleLegacy: async () => {
2540
+ await confirmAndDeploy([await runApply(config.resolveContainerConfig(values))], skipConfirm);
2541
+ },
2542
+ singleApp: async (app, projectConfig) => {
2543
+ await confirmAndDeploy([await runApply(config.resolveAppContainerConfig(app, projectConfig, values))], skipConfirm);
2544
+ },
2545
+ 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
2552
+ });
2553
+ });
2554
+ await confirmAndDeploy(containers, skipConfirm);
2555
+ }
2556
+ });
2557
+ } catch (error) {
2558
+ handleCliError(error);
2559
+ }
2560
+ }
2561
+ });
2584
2562
  }
2585
2563
  //#endregion
2564
+ //#region src/cli/commands/action/apply.ts
2565
+ var apply_default$12 = createApplyCommand({
2566
+ description: "Apply action settings from YAML to kintone app",
2567
+ args: actionArgs,
2568
+ spinnerMessage: "Applying action settings...",
2569
+ spinnerStopMessage: "Action settings applied.",
2570
+ successMessage: "Action settings applied successfully.",
2571
+ createContainer: createActionCliContainer,
2572
+ applyFn: applyAction,
2573
+ resolveContainerConfig: resolveActionContainerConfig,
2574
+ resolveAppContainerConfig: resolveActionAppContainerConfig
2575
+ });
2576
+ //#endregion
2586
2577
  //#region src/core/domain/action/services/configSerializer.ts
2587
2578
  function serializeDestApp(destApp) {
2588
2579
  const result = {};
@@ -2618,7 +2609,7 @@ const ActionConfigSerializer = { serialize: (config) => {
2618
2609
  const actions = {};
2619
2610
  for (const [key, value] of Object.entries(config.actions)) actions[key] = serializeActionConfig(value);
2620
2611
  serialized.actions = actions;
2621
- return serializeToYaml(serialized);
2612
+ return serialized;
2622
2613
  } };
2623
2614
  //#endregion
2624
2615
  //#region src/core/application/captureFromConfigBase.ts
@@ -2630,11 +2621,20 @@ async function captureFromConfig(config) {
2630
2621
  };
2631
2622
  }
2632
2623
  //#endregion
2624
+ //#region src/core/application/stringifyConfig.ts
2625
+ function stringifyConfig(codec, data) {
2626
+ try {
2627
+ return codec.stringify(data);
2628
+ } catch (cause) {
2629
+ throw new SystemError(SystemErrorCode.InternalServerError, `Failed to serialize config: ${cause instanceof Error ? cause.message : String(cause)}`, cause);
2630
+ }
2631
+ }
2632
+ //#endregion
2633
2633
  //#region src/core/application/action/captureAction.ts
2634
2634
  async function captureAction({ container }) {
2635
2635
  return captureFromConfig({
2636
2636
  fetchRemote: () => container.actionConfigurator.getActions(),
2637
- serialize: ({ actions }) => ActionConfigSerializer.serialize({ actions }),
2637
+ serialize: ({ actions }) => stringifyConfig(container.configCodec, ActionConfigSerializer.serialize({ actions })),
2638
2638
  getStorage: () => container.actionStorage.get()
2639
2639
  });
2640
2640
  }
@@ -2644,46 +2644,70 @@ async function saveAction({ container, input }) {
2644
2644
  await container.actionStorage.update(input.configText);
2645
2645
  }
2646
2646
  //#endregion
2647
- //#region src/cli/commands/action/capture.ts
2648
- async function runCaptureAction(config) {
2649
- const container = createActionCliContainer(config);
2650
- const s = p.spinner();
2651
- s.start("Capturing action settings...");
2652
- const result = await captureAction({ container });
2653
- s.stop("Action settings captured.");
2654
- await saveAction({
2655
- container,
2656
- input: { configText: result.configText }
2657
- });
2658
- p.log.success(`Action settings saved to: ${pc.cyan(config.actionFilePath)}`);
2659
- if (result.hasExistingConfig) p.log.warn("Existing action file was overwritten.");
2660
- }
2661
- var capture_default$13 = define({
2662
- name: "capture",
2663
- description: "Capture current action settings from kintone app to file",
2664
- args: actionArgs,
2665
- run: async (ctx) => {
2647
+ //#region src/cli/commands/captureCommandFactory.ts
2648
+ function createCaptureCommand(config) {
2649
+ async function runCapture(containerConfig) {
2650
+ const container = config.createContainer(containerConfig);
2651
+ const s = p.spinner();
2652
+ s.start(config.spinnerMessage);
2653
+ let result;
2666
2654
  try {
2667
- const values = ctx.values;
2668
- await routeMultiApp(values, {
2669
- singleLegacy: async () => {
2670
- await runCaptureAction(resolveActionContainerConfig(values));
2671
- },
2672
- singleApp: async (app, projectConfig) => {
2673
- await runCaptureAction(resolveActionAppContainerConfig(app, projectConfig, values));
2674
- },
2675
- multiApp: async (plan, projectConfig) => {
2676
- await runMultiAppWithFailCheck(plan, async (app) => {
2677
- const config = resolveActionAppContainerConfig(app, projectConfig, values);
2678
- printAppHeader(app.name, app.appId);
2679
- await runCaptureAction(config);
2680
- }, "All action captures completed successfully.");
2681
- }
2682
- });
2655
+ result = await config.captureFn({ container });
2683
2656
  } catch (error) {
2684
- handleCliError(error);
2657
+ s.stop("Capture failed.");
2658
+ throw error;
2685
2659
  }
2660
+ s.stop(config.spinnerStopMessage);
2661
+ await config.saveFn({
2662
+ container,
2663
+ input: { configText: result.configText }
2664
+ });
2665
+ if (result.hasExistingConfig) p.log.warn(`Existing ${config.domainLabel.toLowerCase()} file was overwritten.`);
2666
+ p.log.success(`${config.domainLabel} saved to: ${pc.cyan(config.getConfigFilePath(containerConfig))}`);
2686
2667
  }
2668
+ return define({
2669
+ name: "capture",
2670
+ description: config.description,
2671
+ args: config.args,
2672
+ run: async (ctx) => {
2673
+ try {
2674
+ const values = ctx.values;
2675
+ await routeMultiApp(values, {
2676
+ singleLegacy: async () => {
2677
+ await runCapture(config.resolveContainerConfig(values));
2678
+ },
2679
+ singleApp: async (app, projectConfig) => {
2680
+ await runCapture(config.resolveAppContainerConfig(app, projectConfig, values));
2681
+ },
2682
+ multiApp: async (plan, projectConfig) => {
2683
+ await runMultiAppWithFailCheck(plan, async (app) => {
2684
+ const containerConfig = config.resolveAppContainerConfig(app, projectConfig, values);
2685
+ printAppHeader(app.name, app.appId);
2686
+ await runCapture(containerConfig);
2687
+ }, config.multiAppSuccessMessage);
2688
+ }
2689
+ });
2690
+ } catch (error) {
2691
+ handleCliError(error);
2692
+ }
2693
+ }
2694
+ });
2695
+ }
2696
+ //#endregion
2697
+ //#region src/cli/commands/action/capture.ts
2698
+ var capture_default$13 = createCaptureCommand({
2699
+ description: "Capture current action settings from kintone app to file",
2700
+ args: actionArgs,
2701
+ spinnerMessage: "Capturing action settings...",
2702
+ spinnerStopMessage: "Action settings captured.",
2703
+ domainLabel: "Action settings",
2704
+ multiAppSuccessMessage: "All action captures completed successfully.",
2705
+ createContainer: createActionCliContainer,
2706
+ captureFn: captureAction,
2707
+ saveFn: saveAction,
2708
+ getConfigFilePath: (config) => config.actionFilePath,
2709
+ resolveContainerConfig: resolveActionContainerConfig,
2710
+ resolveAppContainerConfig: resolveActionAppContainerConfig
2687
2711
  });
2688
2712
  //#endregion
2689
2713
  //#region src/lib/deepEqual.ts
@@ -2753,14 +2777,7 @@ function isSetEqual(a, b, stack) {
2753
2777
  }
2754
2778
  return true;
2755
2779
  }
2756
- function deepEqualInner(a, b, stack) {
2757
- if (a === b) return true;
2758
- if (typeof a === "number" && typeof b === "number" && Number.isNaN(a) && Number.isNaN(b)) return true;
2759
- if (a === null || b === null) return a === b;
2760
- if (typeof a !== typeof b) return false;
2761
- if (typeof a !== "object") return false;
2762
- const objA = a;
2763
- const objB = b;
2780
+ function compareDateOrRegExp(objA, objB) {
2764
2781
  if (objA instanceof Date && objB instanceof Date) {
2765
2782
  const ta = objA.getTime();
2766
2783
  const tb = objB.getTime();
@@ -2769,16 +2786,30 @@ function deepEqualInner(a, b, stack) {
2769
2786
  if (objA instanceof Date || objB instanceof Date) return false;
2770
2787
  if (objA instanceof RegExp && objB instanceof RegExp) return String(objA) === String(objB);
2771
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;
2772
2809
  for (const [sa, sb] of stack) if (sa === objA && sb === objB) return true;
2773
2810
  stack.push([objA, objB]);
2774
2811
  try {
2775
- if (Array.isArray(objA)) return isArrayEqual(objA, objB, stack);
2776
- if (objA instanceof Map) return isMapEqual$1(objA, objB, stack);
2777
- if (objB instanceof Map) return false;
2778
- if (objA instanceof Set) return isSetEqual(objA, objB, stack);
2779
- if (objB instanceof Set) return false;
2780
- if (isRecord(objA)) return isRecordEqual(objA, objB, stack);
2781
- return false;
2812
+ return compareCollectionOrRecord(objA, objB, stack);
2782
2813
  } finally {
2783
2814
  stack.pop();
2784
2815
  }
@@ -2903,7 +2934,7 @@ async function detectActionDiff({ container }) {
2903
2934
  return detectDiffFromConfig({
2904
2935
  getStorage: () => container.actionStorage.get(),
2905
2936
  fetchRemote: () => container.actionConfigurator.getActions(),
2906
- parseConfig: parseActionConfigText,
2937
+ parseConfig: (content) => parseActionConfigText(container.configCodec, content),
2907
2938
  detect: (local, remote) => ActionDiffDetector.detect(local, { actions: remote.actions }),
2908
2939
  notFoundMessage: "Action config file not found"
2909
2940
  });
@@ -2977,30 +3008,27 @@ var action_default = define({
2977
3008
  });
2978
3009
  //#endregion
2979
3010
  //#region src/core/domain/adminNotes/services/configParser.ts
2980
- const AdminNotesConfigParser = { parse: (rawText) => {
2981
- const parsed = parseYamlConfig(rawText, {
2982
- emptyConfigText: AdminNotesErrorCode.AnEmptyConfigText,
2983
- invalidConfigYaml: AdminNotesErrorCode.AnInvalidConfigYaml,
2984
- invalidConfigStructure: AdminNotesErrorCode.AnInvalidConfigStructure
2985
- }, "Admin notes");
2986
- if (typeof parsed.content !== "string") throw new BusinessRuleError(AdminNotesErrorCode.AnInvalidConfigStructure, "Config must have a \"content\" string property");
2987
- if (typeof parsed.includeInTemplateAndDuplicates !== "boolean") throw new BusinessRuleError(AdminNotesErrorCode.AnInvalidConfigStructure, "Config must have an \"includeInTemplateAndDuplicates\" boolean property");
2988
- return {
2989
- content: parsed.content,
2990
- includeInTemplateAndDuplicates: parsed.includeInTemplateAndDuplicates
3011
+ const AdminNotesConfigParser = { parse: (parsed) => {
3012
+ const obj = validateParsedConfig(parsed, AdminNotesErrorCode.AnInvalidConfigStructure, "Admin notes");
3013
+ if (typeof obj.content !== "string") throw new BusinessRuleError(AdminNotesErrorCode.AnInvalidConfigStructure, "Config must have a \"content\" string property");
3014
+ if (typeof obj.includeInTemplateAndDuplicates !== "boolean") throw new BusinessRuleError(AdminNotesErrorCode.AnInvalidConfigStructure, "Config must have an \"includeInTemplateAndDuplicates\" boolean property");
3015
+ return {
3016
+ content: obj.content,
3017
+ includeInTemplateAndDuplicates: obj.includeInTemplateAndDuplicates
2991
3018
  };
2992
3019
  } };
2993
3020
  //#endregion
2994
3021
  //#region src/core/application/adminNotes/parseConfig.ts
2995
- function parseAdminNotesConfigText(rawText) {
2996
- return wrapBusinessRuleError(() => AdminNotesConfigParser.parse(rawText));
3022
+ function parseAdminNotesConfigText(codec, rawText) {
3023
+ const parsed = parseConfigText(codec, rawText, "Admin notes");
3024
+ return wrapBusinessRuleError(() => AdminNotesConfigParser.parse(parsed));
2997
3025
  }
2998
3026
  //#endregion
2999
3027
  //#region src/core/application/adminNotes/applyAdminNotes.ts
3000
3028
  async function applyAdminNotes({ container }) {
3001
3029
  await applyFromConfig({
3002
3030
  getStorage: () => container.adminNotesStorage.get(),
3003
- parseConfig: parseAdminNotesConfigText,
3031
+ parseConfig: (content) => parseAdminNotesConfigText(container.configCodec, content),
3004
3032
  fetchRemote: () => container.adminNotesConfigurator.getAdminNotes(),
3005
3033
  update: async (config, current) => {
3006
3034
  await container.adminNotesConfigurator.updateAdminNotes({
@@ -3059,6 +3087,7 @@ function createLocalFileAdminNotesStorage(filePath) {
3059
3087
  function createAdminNotesCliContainer(config) {
3060
3088
  const client = config.client ?? createKintoneClient(config);
3061
3089
  return {
3090
+ configCodec,
3062
3091
  adminNotesConfigurator: new KintoneAdminNotesConfigurator(client, config.appId),
3063
3092
  adminNotesStorage: createLocalFileAdminNotesStorage(config.adminNotesFilePath),
3064
3093
  appDeployer: new KintoneAppDeployer(client, config.appId)
@@ -3087,64 +3116,31 @@ const { resolveFilePath: resolveAdminNotesFilePath, resolveContainerConfig: reso
3087
3116
  });
3088
3117
  //#endregion
3089
3118
  //#region src/cli/commands/admin-notes/apply.ts
3090
- async function runAdminNotes(config) {
3091
- const container = createAdminNotesCliContainer(config);
3092
- const s = p.spinner();
3093
- s.start("Applying admin notes...");
3094
- await applyAdminNotes({ container });
3095
- s.stop("Admin notes applied.");
3096
- p.log.success("Admin notes applied successfully.");
3097
- return container;
3098
- }
3099
- var apply_default$11 = define({
3100
- name: "apply",
3119
+ var apply_default$11 = createApplyCommand({
3101
3120
  description: "Apply admin notes from YAML to kintone app",
3102
- args: {
3103
- ...adminNotesArgs,
3104
- ...confirmArgs
3105
- },
3106
- run: async (ctx) => {
3107
- try {
3108
- const values = ctx.values;
3109
- const skipConfirm = values.yes === true;
3110
- await routeMultiApp(values, {
3111
- singleLegacy: async () => {
3112
- await confirmAndDeploy([await runAdminNotes(resolveAdminNotesContainerConfig(values))], skipConfirm);
3113
- },
3114
- singleApp: async (app, projectConfig) => {
3115
- await confirmAndDeploy([await runAdminNotes(resolveAdminNotesAppContainerConfig(app, projectConfig, values))], skipConfirm);
3116
- },
3117
- multiApp: async (plan, projectConfig) => {
3118
- const containers = [];
3119
- await runMultiAppWithHeaders(plan, async (app) => {
3120
- const container = await runAdminNotes(resolveAdminNotesAppContainerConfig(app, projectConfig, values));
3121
- containers.push({
3122
- appDeployer: container.appDeployer,
3123
- appName: app.name
3124
- });
3125
- });
3126
- await confirmAndDeploy(containers, skipConfirm);
3127
- }
3128
- });
3129
- } catch (error) {
3130
- handleCliError(error);
3131
- }
3132
- }
3121
+ args: adminNotesArgs,
3122
+ spinnerMessage: "Applying admin notes...",
3123
+ spinnerStopMessage: "Admin notes applied.",
3124
+ successMessage: "Admin notes applied successfully.",
3125
+ createContainer: createAdminNotesCliContainer,
3126
+ applyFn: applyAdminNotes,
3127
+ resolveContainerConfig: resolveAdminNotesContainerConfig,
3128
+ resolveAppContainerConfig: resolveAdminNotesAppContainerConfig
3133
3129
  });
3134
3130
  //#endregion
3135
3131
  //#region src/core/domain/adminNotes/services/configSerializer.ts
3136
3132
  const AdminNotesConfigSerializer = { serialize: (config) => {
3137
- return serializeToYaml({
3133
+ return {
3138
3134
  content: config.content,
3139
3135
  includeInTemplateAndDuplicates: config.includeInTemplateAndDuplicates
3140
- });
3136
+ };
3141
3137
  } };
3142
3138
  //#endregion
3143
3139
  //#region src/core/application/adminNotes/captureAdminNotes.ts
3144
3140
  async function captureAdminNotes({ container }) {
3145
3141
  return captureFromConfig({
3146
3142
  fetchRemote: () => container.adminNotesConfigurator.getAdminNotes(),
3147
- serialize: ({ config }) => AdminNotesConfigSerializer.serialize(config),
3143
+ serialize: ({ config }) => stringifyConfig(container.configCodec, AdminNotesConfigSerializer.serialize(config)),
3148
3144
  getStorage: () => container.adminNotesStorage.get()
3149
3145
  });
3150
3146
  }
@@ -3155,45 +3151,19 @@ async function saveAdminNotes({ container, input }) {
3155
3151
  }
3156
3152
  //#endregion
3157
3153
  //#region src/cli/commands/admin-notes/capture.ts
3158
- async function runCaptureAdminNotes(config) {
3159
- const container = createAdminNotesCliContainer(config);
3160
- const s = p.spinner();
3161
- s.start("Capturing admin notes...");
3162
- const result = await captureAdminNotes({ container });
3163
- s.stop("Admin notes captured.");
3164
- await saveAdminNotes({
3165
- container,
3166
- input: { configText: result.configText }
3167
- });
3168
- p.log.success(`Admin notes saved to: ${pc.cyan(config.adminNotesFilePath)}`);
3169
- if (result.hasExistingConfig) p.log.warn("Existing admin notes file was overwritten.");
3170
- }
3171
- var capture_default$12 = define({
3172
- name: "capture",
3154
+ var capture_default$12 = createCaptureCommand({
3173
3155
  description: "Capture current admin notes from kintone app to file",
3174
3156
  args: adminNotesArgs,
3175
- run: async (ctx) => {
3176
- try {
3177
- const values = ctx.values;
3178
- await routeMultiApp(values, {
3179
- singleLegacy: async () => {
3180
- await runCaptureAdminNotes(resolveAdminNotesContainerConfig(values));
3181
- },
3182
- singleApp: async (app, projectConfig) => {
3183
- await runCaptureAdminNotes(resolveAdminNotesAppContainerConfig(app, projectConfig, values));
3184
- },
3185
- multiApp: async (plan, projectConfig) => {
3186
- await runMultiAppWithFailCheck(plan, async (app) => {
3187
- const config = resolveAdminNotesAppContainerConfig(app, projectConfig, values);
3188
- printAppHeader(app.name, app.appId);
3189
- await runCaptureAdminNotes(config);
3190
- }, "All admin notes captures completed successfully.");
3191
- }
3192
- });
3193
- } catch (error) {
3194
- handleCliError(error);
3195
- }
3196
- }
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
3197
3167
  });
3198
3168
  //#endregion
3199
3169
  //#region src/core/domain/adminNotes/services/diffDetector.ts
@@ -3220,7 +3190,7 @@ async function detectAdminNotesDiff({ container }) {
3220
3190
  return detectDiffFromConfig({
3221
3191
  getStorage: () => container.adminNotesStorage.get(),
3222
3192
  fetchRemote: () => container.adminNotesConfigurator.getAdminNotes(),
3223
- parseConfig: parseAdminNotesConfigText,
3193
+ parseConfig: (content) => parseAdminNotesConfigText(container.configCodec, content),
3224
3194
  detect: (local, remote) => AdminNotesDiffDetector.detect(local, remote.config),
3225
3195
  notFoundMessage: "Admin notes config file not found"
3226
3196
  });
@@ -3276,14 +3246,10 @@ function parseAppRight(raw, index) {
3276
3246
  recordExportable: parseStrictBoolean(raw.recordExportable, "recordExportable", `App right at index ${index}`, AppPermissionErrorCode.ApInvalidBooleanField)
3277
3247
  };
3278
3248
  }
3279
- const AppPermissionConfigParser = { parse: (rawText) => {
3280
- const parsed = parseYamlConfig(rawText, {
3281
- emptyConfigText: AppPermissionErrorCode.ApEmptyConfigText,
3282
- invalidConfigYaml: AppPermissionErrorCode.ApInvalidConfigYaml,
3283
- invalidConfigStructure: AppPermissionErrorCode.ApInvalidConfigStructure
3284
- }, "App permission");
3285
- if (!Array.isArray(parsed.rights)) throw new BusinessRuleError(AppPermissionErrorCode.ApInvalidConfigStructure, "Config must have a \"rights\" array");
3286
- const rights = parsed.rights.map((item, i) => parseAppRight(item, i));
3249
+ const AppPermissionConfigParser = { parse: (parsed) => {
3250
+ const obj = validateParsedConfig(parsed, AppPermissionErrorCode.ApInvalidConfigStructure, "App permission");
3251
+ if (!Array.isArray(obj.rights)) throw new BusinessRuleError(AppPermissionErrorCode.ApInvalidConfigStructure, "Config must have a \"rights\" array");
3252
+ const rights = obj.rights.map((item, i) => parseAppRight(item, i));
3287
3253
  const seenKeys = /* @__PURE__ */ new Set();
3288
3254
  for (const right of rights) {
3289
3255
  const key = `${right.entity.type}:${right.entity.code}`;
@@ -3294,15 +3260,16 @@ const AppPermissionConfigParser = { parse: (rawText) => {
3294
3260
  } };
3295
3261
  //#endregion
3296
3262
  //#region src/core/application/appPermission/parseConfig.ts
3297
- function parseAppPermissionConfigText(rawText) {
3298
- return wrapBusinessRuleError(() => AppPermissionConfigParser.parse(rawText));
3263
+ function parseAppPermissionConfigText(codec, rawText) {
3264
+ const parsed = parseConfigText(codec, rawText, "App permission");
3265
+ return wrapBusinessRuleError(() => AppPermissionConfigParser.parse(parsed));
3299
3266
  }
3300
3267
  //#endregion
3301
3268
  //#region src/core/application/appPermission/applyAppPermission.ts
3302
3269
  async function applyAppPermission({ container }) {
3303
3270
  await applyFromConfig({
3304
3271
  getStorage: () => container.appPermissionStorage.get(),
3305
- parseConfig: parseAppPermissionConfigText,
3272
+ parseConfig: (content) => parseAppPermissionConfigText(container.configCodec, content),
3306
3273
  fetchRemote: () => container.appPermissionConfigurator.getAppPermissions(),
3307
3274
  update: async (config, current) => {
3308
3275
  await container.appPermissionConfigurator.updateAppPermissions({
@@ -3403,6 +3370,7 @@ function createLocalFileAppPermissionStorage(filePath) {
3403
3370
  function createAppPermissionCliContainer(config) {
3404
3371
  const client = config.client ?? createKintoneClient(config);
3405
3372
  return {
3373
+ configCodec,
3406
3374
  appPermissionConfigurator: new KintoneAppPermissionConfigurator(client, config.appId),
3407
3375
  appPermissionStorage: createLocalFileAppPermissionStorage(config.appAclFilePath),
3408
3376
  appDeployer: new KintoneAppDeployer(client, config.appId)
@@ -3431,49 +3399,16 @@ const { resolveFilePath: resolveAppAclFilePath, resolveContainerConfig: resolveA
3431
3399
  });
3432
3400
  //#endregion
3433
3401
  //#region src/cli/commands/app-acl/apply.ts
3434
- async function runAppAcl(config) {
3435
- const container = createAppPermissionCliContainer(config);
3436
- const s = p.spinner();
3437
- s.start("Applying app access permissions...");
3438
- await applyAppPermission({ container });
3439
- s.stop("App access permissions applied.");
3440
- p.log.success("App access permissions applied successfully.");
3441
- return container;
3442
- }
3443
- var apply_default$10 = define({
3444
- name: "apply",
3402
+ var apply_default$10 = createApplyCommand({
3445
3403
  description: "Apply app access permissions from YAML to kintone app",
3446
- args: {
3447
- ...appAclArgs,
3448
- ...confirmArgs
3449
- },
3450
- run: async (ctx) => {
3451
- try {
3452
- const values = ctx.values;
3453
- const skipConfirm = values.yes === true;
3454
- await routeMultiApp(values, {
3455
- singleLegacy: async () => {
3456
- await confirmAndDeploy([await runAppAcl(resolveAppAclContainerConfig(values))], skipConfirm);
3457
- },
3458
- singleApp: async (app, projectConfig) => {
3459
- await confirmAndDeploy([await runAppAcl(resolveAppAclAppContainerConfig(app, projectConfig, values))], skipConfirm);
3460
- },
3461
- multiApp: async (plan, projectConfig) => {
3462
- const containers = [];
3463
- await runMultiAppWithHeaders(plan, async (app) => {
3464
- const container = await runAppAcl(resolveAppAclAppContainerConfig(app, projectConfig, values));
3465
- containers.push({
3466
- appDeployer: container.appDeployer,
3467
- appName: app.name
3468
- });
3469
- });
3470
- await confirmAndDeploy(containers, skipConfirm);
3471
- }
3472
- });
3473
- } catch (error) {
3474
- handleCliError(error);
3475
- }
3476
- }
3404
+ args: appAclArgs,
3405
+ spinnerMessage: "Applying app access permissions...",
3406
+ spinnerStopMessage: "App access permissions applied.",
3407
+ successMessage: "App access permissions applied successfully.",
3408
+ createContainer: createAppPermissionCliContainer,
3409
+ applyFn: applyAppPermission,
3410
+ resolveContainerConfig: resolveAppAclContainerConfig,
3411
+ resolveAppContainerConfig: resolveAppAclAppContainerConfig
3477
3412
  });
3478
3413
  //#endregion
3479
3414
  //#region src/core/domain/appPermission/services/configSerializer.ts
@@ -3494,14 +3429,14 @@ function serializeAppRight(right) {
3494
3429
  };
3495
3430
  }
3496
3431
  const AppPermissionConfigSerializer = { serialize: (config) => {
3497
- return serializeToYaml({ rights: config.rights.map(serializeAppRight) });
3432
+ return { rights: config.rights.map(serializeAppRight) };
3498
3433
  } };
3499
3434
  //#endregion
3500
3435
  //#region src/core/application/appPermission/captureAppPermission.ts
3501
3436
  async function captureAppPermission({ container }) {
3502
3437
  return captureFromConfig({
3503
3438
  fetchRemote: () => container.appPermissionConfigurator.getAppPermissions(),
3504
- serialize: ({ rights }) => AppPermissionConfigSerializer.serialize({ rights }),
3439
+ serialize: ({ rights }) => stringifyConfig(container.configCodec, AppPermissionConfigSerializer.serialize({ rights })),
3505
3440
  getStorage: () => container.appPermissionStorage.get()
3506
3441
  });
3507
3442
  }
@@ -3512,45 +3447,19 @@ async function saveAppPermission({ container, input }) {
3512
3447
  }
3513
3448
  //#endregion
3514
3449
  //#region src/cli/commands/app-acl/capture.ts
3515
- async function runCaptureAppAcl(config) {
3516
- const container = createAppPermissionCliContainer(config);
3517
- const s = p.spinner();
3518
- s.start("Capturing app access permissions...");
3519
- const result = await captureAppPermission({ container });
3520
- s.stop("App access permissions captured.");
3521
- await saveAppPermission({
3522
- container,
3523
- input: { configText: result.configText }
3524
- });
3525
- p.log.success(`App ACL saved to: ${pc.cyan(config.appAclFilePath)}`);
3526
- if (result.hasExistingConfig) p.log.warn("Existing app ACL file was overwritten.");
3527
- }
3528
- var capture_default$11 = define({
3529
- name: "capture",
3450
+ var capture_default$11 = createCaptureCommand({
3530
3451
  description: "Capture current app access permissions from kintone app to file",
3531
3452
  args: appAclArgs,
3532
- run: async (ctx) => {
3533
- try {
3534
- const values = ctx.values;
3535
- await routeMultiApp(values, {
3536
- singleLegacy: async () => {
3537
- await runCaptureAppAcl(resolveAppAclContainerConfig(values));
3538
- },
3539
- singleApp: async (app, projectConfig) => {
3540
- await runCaptureAppAcl(resolveAppAclAppContainerConfig(app, projectConfig, values));
3541
- },
3542
- multiApp: async (plan, projectConfig) => {
3543
- await runMultiAppWithFailCheck(plan, async (app) => {
3544
- const config = resolveAppAclAppContainerConfig(app, projectConfig, values);
3545
- printAppHeader(app.name, app.appId);
3546
- await runCaptureAppAcl(config);
3547
- }, "All app ACL captures completed successfully.");
3548
- }
3549
- });
3550
- } catch (error) {
3551
- handleCliError(error);
3552
- }
3553
- }
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
3554
3463
  });
3555
3464
  //#endregion
3556
3465
  //#region src/core/domain/appPermission/services/diffDetector.ts
@@ -3609,7 +3518,7 @@ async function detectAppPermissionDiff({ container }) {
3609
3518
  return detectDiffFromConfig({
3610
3519
  getStorage: () => container.appPermissionStorage.get(),
3611
3520
  fetchRemote: () => container.appPermissionConfigurator.getAppPermissions(),
3612
- parseConfig: parseAppPermissionConfigText,
3521
+ parseConfig: (content) => parseAppPermissionConfigText(container.configCodec, content),
3613
3522
  detect: (local, remote) => AppPermissionDiffDetector.detect(local, { rights: remote.rights }),
3614
3523
  notFoundMessage: "App permission config file not found"
3615
3524
  });
@@ -3693,12 +3602,8 @@ function parsePlatform(raw) {
3693
3602
  css: raw.css === void 0 || raw.css === null ? [] : parseResourceList(raw.css)
3694
3603
  };
3695
3604
  }
3696
- const CustomizationConfigParser = { parse: (rawText) => {
3697
- const obj = parseYamlConfig(rawText, {
3698
- emptyConfigText: CustomizationErrorCode.CzEmptyConfigText,
3699
- invalidConfigYaml: CustomizationErrorCode.CzInvalidConfigYaml,
3700
- invalidConfigStructure: CustomizationErrorCode.CzInvalidConfigStructure
3701
- }, "Customization");
3605
+ const CustomizationConfigParser = { parse: (parsed) => {
3606
+ const obj = validateParsedConfig(parsed, CustomizationErrorCode.CzInvalidConfigStructure, "Customization");
3702
3607
  let scope;
3703
3608
  if (obj.scope !== void 0 && obj.scope !== null) {
3704
3609
  if (typeof obj.scope !== "string" || !isCustomizationScope(obj.scope)) throw new BusinessRuleError(CustomizationErrorCode.CzInvalidScope, `Invalid scope: ${String(obj.scope)}. Must be ALL, ADMIN, or NONE`);
@@ -3720,8 +3625,9 @@ const CustomizationConfigParser = { parse: (rawText) => {
3720
3625
  } };
3721
3626
  //#endregion
3722
3627
  //#region src/core/application/customization/parseConfig.ts
3723
- function parseConfigText(rawText) {
3724
- return wrapBusinessRuleError(() => CustomizationConfigParser.parse(rawText));
3628
+ function parseCustomizationConfigText(codec, rawText) {
3629
+ const parsed = parseConfigText(codec, rawText, "Customization");
3630
+ return wrapBusinessRuleError(() => CustomizationConfigParser.parse(parsed));
3725
3631
  }
3726
3632
  //#endregion
3727
3633
  //#region src/core/application/customization/applyCustomization.ts
@@ -3753,7 +3659,7 @@ function mergePlatform(current, incoming) {
3753
3659
  async function applyCustomization({ container, input }) {
3754
3660
  const result = await container.customizationStorage.get();
3755
3661
  if (!result.exists) throw new ValidationError(ValidationErrorCode.InvalidInput, "Customization config file not found");
3756
- const config = parseConfigText(result.content);
3662
+ const config = parseCustomizationConfigText(container.configCodec, result.content);
3757
3663
  const currentCustomization = await container.customizationConfigurator.getCustomization();
3758
3664
  const [resolvedDesktop, resolvedMobile] = await Promise.all([resolveResources(config.desktop, input.basePath, container.fileUploader), resolveResources(config.mobile, input.basePath, container.fileUploader)]);
3759
3665
  const mergedDesktop = mergePlatform(currentCustomization.desktop, resolvedDesktop);
@@ -3821,11 +3727,11 @@ function serializePlatform(platform) {
3821
3727
  };
3822
3728
  }
3823
3729
  const CustomizationConfigSerializer = { serialize: (config) => {
3824
- return serializeToYaml({
3730
+ return {
3825
3731
  ...config.scope !== void 0 ? { scope: config.scope } : {},
3826
3732
  ...hasPlatformResources(config.desktop) ? { desktop: serializePlatform(config.desktop) } : {},
3827
3733
  ...hasPlatformResources(config.mobile) ? { mobile: serializePlatform(config.mobile) } : {}
3828
- });
3734
+ };
3829
3735
  } };
3830
3736
  //#endregion
3831
3737
  //#region src/lib/deduplicateName.ts
@@ -3967,7 +3873,7 @@ async function captureCustomization({ container, input }) {
3967
3873
  desktop: desktopPlan.platform,
3968
3874
  mobile: mobilePlan.platform
3969
3875
  };
3970
- const configText = CustomizationConfigSerializer.serialize(config);
3876
+ const configText = stringifyConfig(container.configCodec, CustomizationConfigSerializer.serialize(config));
3971
3877
  await downloadFiles([...desktopPlan.filesToDownload, ...mobilePlan.filesToDownload], container);
3972
3878
  return {
3973
3879
  configText,
@@ -4161,7 +4067,7 @@ async function detectCustomizationDiff({ container }) {
4161
4067
  return detectDiffFromConfig({
4162
4068
  getStorage: () => container.customizationStorage.get(),
4163
4069
  fetchRemote: () => container.customizationConfigurator.getCustomization(),
4164
- parseConfig: (content) => parseConfigText(content),
4070
+ parseConfig: (content) => parseCustomizationConfigText(container.configCodec, content),
4165
4071
  detect: (local, remote) => CustomizationDiffDetector.detect(local, remote),
4166
4072
  notFoundMessage: "Customization config file not found"
4167
4073
  });
@@ -4282,6 +4188,7 @@ function createLocalFileFieldPermissionStorage(filePath) {
4282
4188
  function createFieldPermissionCliContainer(config) {
4283
4189
  const client = config.client ?? createKintoneClient(config);
4284
4190
  return {
4191
+ configCodec,
4285
4192
  fieldPermissionConfigurator: new KintoneFieldPermissionConfigurator(client, config.appId),
4286
4193
  fieldPermissionStorage: createLocalFileFieldPermissionStorage(config.fieldAclFilePath),
4287
4194
  appDeployer: new KintoneAppDeployer(client, config.appId)
@@ -4329,14 +4236,10 @@ function parseFieldRight(raw, index) {
4329
4236
  entities
4330
4237
  };
4331
4238
  }
4332
- const FieldPermissionConfigParser = { parse: (rawText) => {
4333
- const parsed = parseYamlConfig(rawText, {
4334
- emptyConfigText: FieldPermissionErrorCode.FpEmptyConfigText,
4335
- invalidConfigYaml: FieldPermissionErrorCode.FpInvalidConfigYaml,
4336
- invalidConfigStructure: FieldPermissionErrorCode.FpInvalidConfigStructure
4337
- }, "Field permission");
4338
- if (!Array.isArray(parsed.rights)) throw new BusinessRuleError(FieldPermissionErrorCode.FpInvalidConfigStructure, "Config must have a \"rights\" array");
4339
- const rights = parsed.rights.map((item, i) => parseFieldRight(item, i));
4239
+ const FieldPermissionConfigParser = { parse: (parsed) => {
4240
+ const obj = validateParsedConfig(parsed, FieldPermissionErrorCode.FpInvalidConfigStructure, "Field permission");
4241
+ if (!Array.isArray(obj.rights)) throw new BusinessRuleError(FieldPermissionErrorCode.FpInvalidConfigStructure, "Config must have a \"rights\" array");
4242
+ const rights = obj.rights.map((item, i) => parseFieldRight(item, i));
4340
4243
  const seenCodes = /* @__PURE__ */ new Set();
4341
4244
  for (const right of rights) {
4342
4245
  if (seenCodes.has(right.code)) throw new BusinessRuleError(FieldPermissionErrorCode.FpDuplicateFieldCode, `Duplicate field code: ${right.code}`);
@@ -4346,15 +4249,16 @@ const FieldPermissionConfigParser = { parse: (rawText) => {
4346
4249
  } };
4347
4250
  //#endregion
4348
4251
  //#region src/core/application/fieldPermission/parseConfig.ts
4349
- function parseFieldPermissionConfigText(rawText) {
4350
- return wrapBusinessRuleError(() => FieldPermissionConfigParser.parse(rawText));
4252
+ function parseFieldPermissionConfigText(codec, rawText) {
4253
+ const parsed = parseConfigText(codec, rawText, "Field permission");
4254
+ return wrapBusinessRuleError(() => FieldPermissionConfigParser.parse(parsed));
4351
4255
  }
4352
4256
  //#endregion
4353
4257
  //#region src/core/application/fieldPermission/applyFieldPermission.ts
4354
4258
  async function applyFieldPermission({ container }) {
4355
4259
  await applyFromConfig({
4356
4260
  getStorage: () => container.fieldPermissionStorage.get(),
4357
- parseConfig: parseFieldPermissionConfigText,
4261
+ parseConfig: (content) => parseFieldPermissionConfigText(container.configCodec, content),
4358
4262
  fetchRemote: () => container.fieldPermissionConfigurator.getFieldPermissions(),
4359
4263
  update: async (config, current) => {
4360
4264
  await container.fieldPermissionConfigurator.updateFieldPermissions({
@@ -4388,49 +4292,16 @@ const { resolveFilePath: resolveFieldAclFilePath, resolveContainerConfig: resolv
4388
4292
  });
4389
4293
  //#endregion
4390
4294
  //#region src/cli/commands/field-acl/apply.ts
4391
- async function runFieldAcl(config) {
4392
- const container = createFieldPermissionCliContainer(config);
4393
- const s = p.spinner();
4394
- s.start("Applying field access permissions...");
4395
- await applyFieldPermission({ container });
4396
- s.stop("Field access permissions applied.");
4397
- p.log.success("Field access permissions applied successfully.");
4398
- return container;
4399
- }
4400
- var apply_default$8 = define({
4401
- name: "apply",
4295
+ var apply_default$8 = createApplyCommand({
4402
4296
  description: "Apply field access permissions from YAML to kintone app",
4403
- args: {
4404
- ...fieldAclArgs,
4405
- ...confirmArgs
4406
- },
4407
- run: async (ctx) => {
4408
- try {
4409
- const values = ctx.values;
4410
- const skipConfirm = values.yes === true;
4411
- await routeMultiApp(values, {
4412
- singleLegacy: async () => {
4413
- await confirmAndDeploy([await runFieldAcl(resolveFieldAclContainerConfig(values))], skipConfirm);
4414
- },
4415
- singleApp: async (app, projectConfig) => {
4416
- await confirmAndDeploy([await runFieldAcl(resolveFieldAclAppContainerConfig(app, projectConfig, values))], skipConfirm);
4417
- },
4418
- multiApp: async (plan, projectConfig) => {
4419
- const containers = [];
4420
- await runMultiAppWithHeaders(plan, async (app) => {
4421
- const container = await runFieldAcl(resolveFieldAclAppContainerConfig(app, projectConfig, values));
4422
- containers.push({
4423
- appDeployer: container.appDeployer,
4424
- appName: app.name
4425
- });
4426
- });
4427
- await confirmAndDeploy(containers, skipConfirm);
4428
- }
4429
- });
4430
- } catch (error) {
4431
- handleCliError(error);
4432
- }
4433
- }
4297
+ args: fieldAclArgs,
4298
+ spinnerMessage: "Applying field access permissions...",
4299
+ spinnerStopMessage: "Field access permissions applied.",
4300
+ successMessage: "Field access permissions applied successfully.",
4301
+ createContainer: createFieldPermissionCliContainer,
4302
+ applyFn: applyFieldPermission,
4303
+ resolveContainerConfig: resolveFieldAclContainerConfig,
4304
+ resolveAppContainerConfig: resolveFieldAclAppContainerConfig
4434
4305
  });
4435
4306
  //#endregion
4436
4307
  //#region src/core/domain/fieldPermission/services/configSerializer.ts
@@ -4446,17 +4317,17 @@ function serializeFieldRightEntity(entity) {
4446
4317
  return result;
4447
4318
  }
4448
4319
  const FieldPermissionConfigSerializer = { serialize: (config) => {
4449
- return serializeToYaml({ rights: config.rights.map((right) => ({
4320
+ return { rights: config.rights.map((right) => ({
4450
4321
  code: right.code,
4451
4322
  entities: right.entities.map(serializeFieldRightEntity)
4452
- })) });
4323
+ })) };
4453
4324
  } };
4454
4325
  //#endregion
4455
4326
  //#region src/core/application/fieldPermission/captureFieldPermission.ts
4456
4327
  async function captureFieldPermission({ container }) {
4457
4328
  return captureFromConfig({
4458
4329
  fetchRemote: () => container.fieldPermissionConfigurator.getFieldPermissions(),
4459
- serialize: ({ rights }) => FieldPermissionConfigSerializer.serialize({ rights }),
4330
+ serialize: ({ rights }) => stringifyConfig(container.configCodec, FieldPermissionConfigSerializer.serialize({ rights })),
4460
4331
  getStorage: () => container.fieldPermissionStorage.get()
4461
4332
  });
4462
4333
  }
@@ -4467,45 +4338,19 @@ async function saveFieldPermission({ container, input }) {
4467
4338
  }
4468
4339
  //#endregion
4469
4340
  //#region src/cli/commands/field-acl/capture.ts
4470
- async function runCaptureFieldAcl(config) {
4471
- const container = createFieldPermissionCliContainer(config);
4472
- const s = p.spinner();
4473
- s.start("Capturing field access permissions...");
4474
- const result = await captureFieldPermission({ container });
4475
- s.stop("Field access permissions captured.");
4476
- await saveFieldPermission({
4477
- container,
4478
- input: { configText: result.configText }
4479
- });
4480
- p.log.success(`Field ACL saved to: ${pc.cyan(config.fieldAclFilePath)}`);
4481
- if (result.hasExistingConfig) p.log.warn("Existing field ACL file was overwritten.");
4482
- }
4483
- var capture_default$9 = define({
4484
- name: "capture",
4341
+ var capture_default$9 = createCaptureCommand({
4485
4342
  description: "Capture current field access permissions from kintone app to file",
4486
4343
  args: fieldAclArgs,
4487
- run: async (ctx) => {
4488
- try {
4489
- const values = ctx.values;
4490
- await routeMultiApp(values, {
4491
- singleLegacy: async () => {
4492
- await runCaptureFieldAcl(resolveFieldAclContainerConfig(values));
4493
- },
4494
- singleApp: async (app, projectConfig) => {
4495
- await runCaptureFieldAcl(resolveFieldAclAppContainerConfig(app, projectConfig, values));
4496
- },
4497
- multiApp: async (plan, projectConfig) => {
4498
- await runMultiAppWithFailCheck(plan, async (app) => {
4499
- const config = resolveFieldAclAppContainerConfig(app, projectConfig, values);
4500
- printAppHeader(app.name, app.appId);
4501
- await runCaptureFieldAcl(config);
4502
- }, "All field ACL captures completed successfully.");
4503
- }
4504
- });
4505
- } catch (error) {
4506
- handleCliError(error);
4507
- }
4508
- }
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
4509
4354
  });
4510
4355
  //#endregion
4511
4356
  //#region src/core/domain/fieldPermission/services/diffDetector.ts
@@ -4552,7 +4397,7 @@ async function detectFieldPermissionDiff({ container }) {
4552
4397
  return detectDiffFromConfig({
4553
4398
  getStorage: () => container.fieldPermissionStorage.get(),
4554
4399
  fetchRemote: () => container.fieldPermissionConfigurator.getFieldPermissions(),
4555
- parseConfig: parseFieldPermissionConfigText,
4400
+ parseConfig: (content) => parseFieldPermissionConfigText(container.configCodec, content),
4556
4401
  detect: (local, remote) => FieldPermissionDiffDetector.detect(local, { rights: remote.rights }),
4557
4402
  notFoundMessage: "Field permission config file not found"
4558
4403
  });
@@ -4728,6 +4573,7 @@ function createLocalFileGeneralSettingsStorage(filePath) {
4728
4573
  function createGeneralSettingsCliContainer(config) {
4729
4574
  const client = config.client ?? createKintoneClient(config);
4730
4575
  return {
4576
+ configCodec,
4731
4577
  generalSettingsConfigurator: new KintoneGeneralSettingsConfigurator(client, config.appId),
4732
4578
  generalSettingsStorage: createLocalFileGeneralSettingsStorage(config.settingsFilePath),
4733
4579
  appDeployer: new KintoneAppDeployer(client, config.appId)
@@ -4948,6 +4794,7 @@ function createLocalFileNotificationStorage(filePath) {
4948
4794
  function createNotificationCliContainer(config) {
4949
4795
  const client = config.client ?? createKintoneClient(config);
4950
4796
  return {
4797
+ configCodec,
4951
4798
  notificationConfigurator: new KintoneNotificationConfigurator(client, config.appId),
4952
4799
  notificationStorage: createLocalFileNotificationStorage(config.notificationFilePath),
4953
4800
  appDeployer: new KintoneAppDeployer(client, config.appId)
@@ -5000,6 +4847,7 @@ function createLocalFilePluginStorage(filePath) {
5000
4847
  function createPluginCliContainer(config) {
5001
4848
  const client = config.client ?? createKintoneClient(config);
5002
4849
  return {
4850
+ configCodec,
5003
4851
  pluginConfigurator: new KintonePluginConfigurator(client, config.appId),
5004
4852
  pluginStorage: createLocalFilePluginStorage(config.pluginFilePath),
5005
4853
  appDeployer: new KintoneAppDeployer(client, config.appId)
@@ -5146,6 +4994,7 @@ function createLocalFileProcessManagementStorage(filePath) {
5146
4994
  function createProcessManagementCliContainer(config) {
5147
4995
  const client = config.client ?? createKintoneClient(config);
5148
4996
  return {
4997
+ configCodec,
5149
4998
  processManagementConfigurator: new KintoneProcessManagementConfigurator(client, config.appId),
5150
4999
  processManagementStorage: createLocalFileProcessManagementStorage(config.processFilePath),
5151
5000
  appDeployer: new KintoneAppDeployer(client, config.appId)
@@ -5238,6 +5087,7 @@ function createLocalFileRecordPermissionStorage(filePath) {
5238
5087
  function createRecordPermissionCliContainer(config) {
5239
5088
  const client = config.client ?? createKintoneClient(config);
5240
5089
  return {
5090
+ configCodec,
5241
5091
  recordPermissionConfigurator: new KintoneRecordPermissionConfigurator(client, config.appId),
5242
5092
  recordPermissionStorage: createLocalFileRecordPermissionStorage(config.recordAclFilePath),
5243
5093
  appDeployer: new KintoneAppDeployer(client, config.appId)
@@ -5510,6 +5360,7 @@ function createLocalFileReportStorage(filePath) {
5510
5360
  function createReportCliContainer(config) {
5511
5361
  const client = config.client ?? createKintoneClient(config);
5512
5362
  return {
5363
+ configCodec,
5513
5364
  reportConfigurator: new KintoneReportConfigurator(client, config.appId),
5514
5365
  reportStorage: createLocalFileReportStorage(config.reportFilePath),
5515
5366
  appDeployer: new KintoneAppDeployer(client, config.appId)
@@ -5616,6 +5467,7 @@ function createLocalFileViewStorage(filePath) {
5616
5467
  function createViewCliContainer(config) {
5617
5468
  const client = config.client ?? createKintoneClient(config);
5618
5469
  return {
5470
+ configCodec,
5619
5471
  viewConfigurator: new KintoneViewConfigurator(client, config.appId),
5620
5472
  viewStorage: createLocalFileViewStorage(config.viewFilePath),
5621
5473
  appDeployer: new KintoneAppDeployer(client, config.appId)
@@ -5904,11 +5756,7 @@ function serializeLayoutItem(item, fields) {
5904
5756
  }
5905
5757
  }
5906
5758
  const SchemaSerializer = { serialize: (layout, fields) => {
5907
- return stringify({ layout: layout.map((item) => serializeLayoutItem(item, fields)) }, {
5908
- lineWidth: 0,
5909
- defaultKeyType: "PLAIN",
5910
- defaultStringType: "PLAIN"
5911
- });
5759
+ return { layout: layout.map((item) => serializeLayoutItem(item, fields)) };
5912
5760
  } };
5913
5761
  //#endregion
5914
5762
  //#region src/core/application/formSchema/captureSchema.ts
@@ -5916,7 +5764,7 @@ async function captureSchema({ container }) {
5916
5764
  const [currentFields, currentLayout] = await Promise.all([container.formConfigurator.getFields(), container.formConfigurator.getLayout()]);
5917
5765
  const enrichedLayout = enrichLayoutWithFields(currentLayout, currentFields);
5918
5766
  return {
5919
- schemaText: SchemaSerializer.serialize(enrichedLayout, currentFields),
5767
+ schemaText: stringifyConfig(container.configCodec, SchemaSerializer.serialize(enrichedLayout, currentFields)),
5920
5768
  hasExistingSchema: (await container.schemaStorage.get()).exists
5921
5769
  };
5922
5770
  }
@@ -5955,14 +5803,14 @@ function serializeConfig(config) {
5955
5803
  return result;
5956
5804
  }
5957
5805
  const GeneralSettingsConfigSerializer = { serialize: (config) => {
5958
- return serializeToYaml(serializeConfig(config));
5806
+ return serializeConfig(config);
5959
5807
  } };
5960
5808
  //#endregion
5961
5809
  //#region src/core/application/generalSettings/captureGeneralSettings.ts
5962
5810
  async function captureGeneralSettings({ container }) {
5963
5811
  return captureFromConfig({
5964
5812
  fetchRemote: () => container.generalSettingsConfigurator.getGeneralSettings(),
5965
- serialize: ({ config }) => GeneralSettingsConfigSerializer.serialize(config),
5813
+ serialize: ({ config }) => stringifyConfig(container.configCodec, GeneralSettingsConfigSerializer.serialize(config)),
5966
5814
  getStorage: () => container.generalSettingsStorage.get()
5967
5815
  });
5968
5816
  }
@@ -6026,7 +5874,7 @@ const NotificationConfigSerializer = { serialize: (config) => {
6026
5874
  timezone: config.reminder.timezone,
6027
5875
  notifications: config.reminder.notifications.map(serializeReminderNotification)
6028
5876
  };
6029
- return serializeToYaml(serialized);
5877
+ return serialized;
6030
5878
  } };
6031
5879
  //#endregion
6032
5880
  //#region src/core/application/notification/captureNotification.ts
@@ -6048,7 +5896,7 @@ async function captureNotification({ container }) {
6048
5896
  }
6049
5897
  };
6050
5898
  return {
6051
- configText: NotificationConfigSerializer.serialize(config),
5899
+ configText: stringifyConfig(container.configCodec, NotificationConfigSerializer.serialize(config)),
6052
5900
  hasExistingConfig: (await container.notificationStorage.get()).exists
6053
5901
  };
6054
5902
  }
@@ -6060,18 +5908,18 @@ async function saveNotification({ container, input }) {
6060
5908
  //#endregion
6061
5909
  //#region src/core/domain/plugin/services/configSerializer.ts
6062
5910
  const PluginConfigSerializer = { serialize: (config) => {
6063
- return serializeToYaml({ plugins: config.plugins.map((plugin) => ({
5911
+ return { plugins: config.plugins.map((plugin) => ({
6064
5912
  id: plugin.id,
6065
5913
  name: plugin.name,
6066
5914
  enabled: plugin.enabled
6067
- })) });
5915
+ })) };
6068
5916
  } };
6069
5917
  //#endregion
6070
5918
  //#region src/core/application/plugin/capturePlugin.ts
6071
5919
  async function capturePlugin({ container }) {
6072
5920
  return captureFromConfig({
6073
5921
  fetchRemote: () => container.pluginConfigurator.getPlugins(),
6074
- serialize: ({ plugins }) => PluginConfigSerializer.serialize({ plugins }),
5922
+ serialize: ({ plugins }) => stringifyConfig(container.configCodec, PluginConfigSerializer.serialize({ plugins })),
6075
5923
  getStorage: () => container.pluginStorage.get()
6076
5924
  });
6077
5925
  }
@@ -6097,7 +5945,7 @@ const ProcessManagementConfigSerializer = { serialize: (config) => {
6097
5945
  entities: state.assignee.entities.map(serializeProcessEntity)
6098
5946
  }
6099
5947
  };
6100
- return serializeToYaml({
5948
+ return {
6101
5949
  enable: config.enable,
6102
5950
  states: serializedStates,
6103
5951
  actions: config.actions.map((action) => {
@@ -6111,14 +5959,14 @@ const ProcessManagementConfigSerializer = { serialize: (config) => {
6111
5959
  if (action.executableUser !== void 0) serializedAction.executableUser = { entities: action.executableUser.entities.map(serializeProcessEntity) };
6112
5960
  return serializedAction;
6113
5961
  })
6114
- });
5962
+ };
6115
5963
  } };
6116
5964
  //#endregion
6117
5965
  //#region src/core/application/processManagement/captureProcessManagement.ts
6118
5966
  async function captureProcessManagement({ container }) {
6119
5967
  return captureFromConfig({
6120
5968
  fetchRemote: () => container.processManagementConfigurator.getProcessManagement(),
6121
- serialize: ({ config }) => ProcessManagementConfigSerializer.serialize(config),
5969
+ serialize: ({ config }) => stringifyConfig(container.configCodec, ProcessManagementConfigSerializer.serialize(config)),
6122
5970
  getStorage: () => container.processManagementStorage.get()
6123
5971
  });
6124
5972
  }
@@ -6142,17 +5990,17 @@ function serializeRecordRightEntity(entity) {
6142
5990
  };
6143
5991
  }
6144
5992
  const RecordPermissionConfigSerializer = { serialize: (config) => {
6145
- return serializeToYaml({ rights: config.rights.map((right) => ({
5993
+ return { rights: config.rights.map((right) => ({
6146
5994
  filterCond: right.filterCond,
6147
5995
  entities: right.entities.map(serializeRecordRightEntity)
6148
- })) });
5996
+ })) };
6149
5997
  } };
6150
5998
  //#endregion
6151
5999
  //#region src/core/application/recordPermission/captureRecordPermission.ts
6152
6000
  async function captureRecordPermission({ container }) {
6153
6001
  return captureFromConfig({
6154
6002
  fetchRemote: () => container.recordPermissionConfigurator.getRecordPermissions(),
6155
- serialize: ({ rights }) => RecordPermissionConfigSerializer.serialize({ rights }),
6003
+ serialize: ({ rights }) => stringifyConfig(container.configCodec, RecordPermissionConfigSerializer.serialize({ rights })),
6156
6004
  getStorage: () => container.recordPermissionStorage.get()
6157
6005
  });
6158
6006
  }
@@ -6213,14 +6061,14 @@ const ReportConfigSerializer = { serialize: (config) => {
6213
6061
  const reports = {};
6214
6062
  for (const [name, reportConfig] of Object.entries(config.reports)) reports[name] = serializeReportConfig(reportConfig);
6215
6063
  serialized.reports = reports;
6216
- return serializeToYaml(serialized);
6064
+ return serialized;
6217
6065
  } };
6218
6066
  //#endregion
6219
6067
  //#region src/core/application/report/captureReport.ts
6220
6068
  async function captureReport({ container }) {
6221
6069
  return captureFromConfig({
6222
6070
  fetchRemote: () => container.reportConfigurator.getReports(),
6223
- serialize: ({ reports }) => ReportConfigSerializer.serialize({ reports }),
6071
+ serialize: ({ reports }) => stringifyConfig(container.configCodec, ReportConfigSerializer.serialize({ reports })),
6224
6072
  getStorage: () => container.reportStorage.get()
6225
6073
  });
6226
6074
  }
@@ -6233,14 +6081,10 @@ async function saveReport({ container, input }) {
6233
6081
  //#region src/core/domain/seedData/services/seedSerializer.ts
6234
6082
  const SeedSerializer = { serialize: (seedData) => {
6235
6083
  const records = seedData.records.map((record) => ({ ...record }));
6236
- return stringify(seedData.key !== null ? {
6084
+ return seedData.key !== null ? {
6237
6085
  key: seedData.key,
6238
6086
  records
6239
- } : { records }, {
6240
- lineWidth: 0,
6241
- defaultKeyType: "PLAIN",
6242
- defaultStringType: "PLAIN"
6243
- });
6087
+ } : { records };
6244
6088
  } };
6245
6089
  //#endregion
6246
6090
  //#region src/core/domain/seedData/valueObject.ts
@@ -6253,10 +6097,10 @@ const UpsertKey = { create: (key) => {
6253
6097
  async function captureSeed({ container, input }) {
6254
6098
  const key = input.keyField ? UpsertKey.create(input.keyField) : null;
6255
6099
  const records = (await container.recordManager.getAllRecords()).map(({ record }) => record);
6256
- const seedText = SeedSerializer.serialize({
6100
+ const seedText = stringifyConfig(container.configCodec, SeedSerializer.serialize({
6257
6101
  key,
6258
6102
  records
6259
- });
6103
+ }));
6260
6104
  const existing = await container.seedStorage.get();
6261
6105
  return {
6262
6106
  seedText,
@@ -6290,14 +6134,14 @@ function serializeViewConfig(config) {
6290
6134
  const ViewConfigSerializer = { serialize: (config) => {
6291
6135
  const serialized = {};
6292
6136
  for (const [name, viewConfig] of Object.entries(config.views)) serialized[name] = serializeViewConfig(viewConfig);
6293
- return serializeToYaml({ views: serialized });
6137
+ return { views: serialized };
6294
6138
  } };
6295
6139
  //#endregion
6296
6140
  //#region src/core/application/view/captureView.ts
6297
6141
  async function captureView({ container }) {
6298
6142
  return captureFromConfig({
6299
6143
  fetchRemote: () => container.viewConfigurator.getViews(),
6300
- serialize: ({ views }) => ViewConfigSerializer.serialize({ views }),
6144
+ serialize: ({ views }) => stringifyConfig(container.configCodec, ViewConfigSerializer.serialize({ views })),
6301
6145
  getStorage: () => container.viewStorage.get()
6302
6146
  });
6303
6147
  }
@@ -6481,7 +6325,7 @@ function deduplicateAppName(baseName, usedNames) {
6481
6325
  startCounter: 2
6482
6326
  }));
6483
6327
  }
6484
- function generateProjectConfig(input) {
6328
+ function generateProjectConfig(input, codec) {
6485
6329
  const apps = {};
6486
6330
  const usedNames = /* @__PURE__ */ new Set();
6487
6331
  for (const app of input.apps) {
@@ -6491,11 +6335,11 @@ function generateProjectConfig(input) {
6491
6335
  files: buildAppFilePaths(name, input.baseDir)
6492
6336
  };
6493
6337
  }
6494
- return stringify({
6338
+ return stringifyConfig(codec, {
6495
6339
  domain: input.domain,
6496
6340
  ...input.guestSpaceId !== void 0 ? { guestSpaceId: input.guestSpaceId } : {},
6497
6341
  apps
6498
- }, { lineWidth: 0 });
6342
+ });
6499
6343
  }
6500
6344
  //#endregion
6501
6345
  //#region src/cli/commands/init.ts
@@ -6578,7 +6422,7 @@ var init_default = define({
6578
6422
  domain: kintoneDomain,
6579
6423
  guestSpaceId,
6580
6424
  baseDir: output
6581
- });
6425
+ }, configCodec);
6582
6426
  if (dryRun) {
6583
6427
  p.log.info(pc.dim("(dry-run mode - no files will be written)"));
6584
6428
  p.log.step(`\nConfig file: ${pc.cyan(configPath)}`);
@@ -6732,12 +6576,8 @@ function parseReminderConfig(raw) {
6732
6576
  notifications
6733
6577
  };
6734
6578
  }
6735
- const NotificationConfigParser = { parse: (rawText) => {
6736
- const obj = parseYamlConfig(rawText, {
6737
- emptyConfigText: NotificationErrorCode.NtEmptyConfigText,
6738
- invalidConfigYaml: NotificationErrorCode.NtInvalidConfigYaml,
6739
- invalidConfigStructure: NotificationErrorCode.NtInvalidConfigStructure
6740
- }, "Notification");
6579
+ const NotificationConfigParser = { parse: (parsed) => {
6580
+ const obj = validateParsedConfig(parsed, NotificationErrorCode.NtInvalidConfigStructure, "Notification");
6741
6581
  const config = {};
6742
6582
  if (obj.general !== void 0) config.general = parseGeneralConfig(obj.general);
6743
6583
  if (obj.perRecord !== void 0) config.perRecord = parsePerRecordConfig(obj.perRecord);
@@ -6746,8 +6586,9 @@ const NotificationConfigParser = { parse: (rawText) => {
6746
6586
  } };
6747
6587
  //#endregion
6748
6588
  //#region src/core/application/notification/parseConfig.ts
6749
- function parseNotificationConfigText(rawText) {
6750
- return wrapBusinessRuleError(() => NotificationConfigParser.parse(rawText));
6589
+ function parseNotificationConfigText(codec, rawText) {
6590
+ const parsed = parseConfigText(codec, rawText, "Notification");
6591
+ return wrapBusinessRuleError(() => NotificationConfigParser.parse(parsed));
6751
6592
  }
6752
6593
  //#endregion
6753
6594
  //#region src/core/application/notification/applyNotification.ts
@@ -6763,7 +6604,7 @@ function parseNotificationConfigText(rawText) {
6763
6604
  async function applyNotification({ container }) {
6764
6605
  const result = await container.notificationStorage.get();
6765
6606
  if (!result.exists) throw new ValidationError(ValidationErrorCode.InvalidInput, "Notification config file not found");
6766
- const config = parseNotificationConfigText(result.content);
6607
+ const config = parseNotificationConfigText(container.configCodec, result.content);
6767
6608
  if (config.general !== void 0) {
6768
6609
  const currentGeneral = await container.notificationConfigurator.getGeneralNotifications();
6769
6610
  await container.notificationConfigurator.updateGeneralNotifications({
@@ -6811,91 +6652,32 @@ const { resolveFilePath: resolveNotificationFilePath, resolveContainerConfig: re
6811
6652
  });
6812
6653
  //#endregion
6813
6654
  //#region src/cli/commands/notification/apply.ts
6814
- async function runNotification(config) {
6815
- const container = createNotificationCliContainer(config);
6816
- const s = p.spinner();
6817
- s.start("Applying notification settings...");
6818
- await applyNotification({ container });
6819
- s.stop("Notification settings applied.");
6820
- p.log.success("Notification settings applied successfully.");
6821
- return container;
6822
- }
6823
- var apply_default$7 = define({
6824
- name: "apply",
6655
+ var apply_default$7 = createApplyCommand({
6825
6656
  description: "Apply notification settings from YAML to kintone app",
6826
- args: {
6827
- ...notificationArgs,
6828
- ...confirmArgs
6829
- },
6830
- run: async (ctx) => {
6831
- try {
6832
- const values = ctx.values;
6833
- const skipConfirm = values.yes === true;
6834
- await routeMultiApp(values, {
6835
- singleLegacy: async () => {
6836
- await confirmAndDeploy([await runNotification(resolveNotificationContainerConfig(values))], skipConfirm);
6837
- },
6838
- singleApp: async (app, projectConfig) => {
6839
- await confirmAndDeploy([await runNotification(resolveNotificationAppContainerConfig(app, projectConfig, values))], skipConfirm);
6840
- },
6841
- multiApp: async (plan, projectConfig) => {
6842
- const containers = [];
6843
- await runMultiAppWithHeaders(plan, async (app) => {
6844
- const container = await runNotification(resolveNotificationAppContainerConfig(app, projectConfig, values));
6845
- containers.push({
6846
- appDeployer: container.appDeployer,
6847
- appName: app.name
6848
- });
6849
- });
6850
- await confirmAndDeploy(containers, skipConfirm);
6851
- }
6852
- });
6853
- } catch (error) {
6854
- handleCliError(error);
6855
- }
6856
- }
6657
+ args: notificationArgs,
6658
+ spinnerMessage: "Applying notification settings...",
6659
+ spinnerStopMessage: "Notification settings applied.",
6660
+ successMessage: "Notification settings applied successfully.",
6661
+ createContainer: createNotificationCliContainer,
6662
+ applyFn: applyNotification,
6663
+ resolveContainerConfig: resolveNotificationContainerConfig,
6664
+ resolveAppContainerConfig: resolveNotificationAppContainerConfig
6857
6665
  });
6858
6666
  //#endregion
6859
6667
  //#region src/cli/commands/notification/capture.ts
6860
- async function runCaptureNotification(config) {
6861
- const container = createNotificationCliContainer(config);
6862
- const s = p.spinner();
6863
- s.start("Capturing notification settings...");
6864
- const result = await captureNotification({ container });
6865
- s.stop("Notification settings captured.");
6866
- await saveNotification({
6867
- container,
6868
- input: { configText: result.configText }
6869
- });
6870
- p.log.success(`Notification settings saved to: ${pc.cyan(config.notificationFilePath)}`);
6871
- if (result.hasExistingConfig) p.log.warn("Existing notification file was overwritten.");
6872
- }
6873
- var capture_default$8 = define({
6874
- name: "capture",
6668
+ var capture_default$8 = createCaptureCommand({
6875
6669
  description: "Capture current notification settings from kintone app to file",
6876
6670
  args: notificationArgs,
6877
- run: async (ctx) => {
6878
- try {
6879
- const values = ctx.values;
6880
- await routeMultiApp(values, {
6881
- singleLegacy: async () => {
6882
- await runCaptureNotification(resolveNotificationContainerConfig(values));
6883
- },
6884
- singleApp: async (app, projectConfig) => {
6885
- await runCaptureNotification(resolveNotificationAppContainerConfig(app, projectConfig, values));
6886
- },
6887
- multiApp: async (plan, projectConfig) => {
6888
- await runMultiAppWithFailCheck(plan, async (app) => {
6889
- const config = resolveNotificationAppContainerConfig(app, projectConfig, values);
6890
- printAppHeader(app.name, app.appId);
6891
- await runCaptureNotification(config);
6892
- }, "All notification captures completed successfully.");
6893
- }
6894
- });
6895
- } catch (error) {
6896
- handleCliError(error);
6897
- }
6898
- }
6671
+ spinnerMessage: "Capturing notification settings...",
6672
+ spinnerStopMessage: "Notification settings captured.",
6673
+ domainLabel: "Notification settings",
6674
+ multiAppSuccessMessage: "All notification captures completed successfully.",
6675
+ createContainer: createNotificationCliContainer,
6676
+ captureFn: captureNotification,
6677
+ saveFn: saveNotification,
6678
+ getConfigFilePath: (config) => config.notificationFilePath,
6679
+ resolveContainerConfig: resolveNotificationContainerConfig,
6680
+ resolveAppContainerConfig: resolveNotificationAppContainerConfig
6899
6681
  });
6900
6682
  //#endregion
6901
6683
  //#region src/lib/groupByKey.ts
@@ -7112,7 +6894,7 @@ async function detectNotificationDiff({ container }) {
7112
6894
  container.notificationConfigurator.getReminderNotifications()
7113
6895
  ]);
7114
6896
  if (!storageResult.exists) throw new ValidationError(ValidationErrorCode.InvalidInput, "Notification config file not found");
7115
- const localConfig = parseNotificationConfigText(storageResult.content);
6897
+ const localConfig = parseNotificationConfigText(container.configCodec, storageResult.content);
7116
6898
  const remoteConfig = {
7117
6899
  general: {
7118
6900
  notifyToCommenter: generalResult.notifyToCommenter,
@@ -7160,12 +6942,8 @@ function parsePluginEntry(raw, index) {
7160
6942
  enabled: typeof raw.enabled === "boolean" ? raw.enabled : true
7161
6943
  };
7162
6944
  }
7163
- const PluginConfigParser = { parse: (rawText) => {
7164
- const obj = parseYamlConfig(rawText, {
7165
- emptyConfigText: PluginErrorCode.PlEmptyConfigText,
7166
- invalidConfigYaml: PluginErrorCode.PlInvalidConfigYaml,
7167
- invalidConfigStructure: PluginErrorCode.PlInvalidConfigStructure
7168
- }, "Plugin");
6945
+ const PluginConfigParser = { parse: (parsed) => {
6946
+ const obj = validateParsedConfig(parsed, PluginErrorCode.PlInvalidConfigStructure, "Plugin");
7169
6947
  if (!Array.isArray(obj.plugins)) throw new BusinessRuleError(PluginErrorCode.PlInvalidConfigStructure, "Config must have a \"plugins\" array");
7170
6948
  const plugins = obj.plugins.map((item, i) => parsePluginEntry(item, i));
7171
6949
  const seenIds = /* @__PURE__ */ new Set();
@@ -7177,15 +6955,16 @@ const PluginConfigParser = { parse: (rawText) => {
7177
6955
  } };
7178
6956
  //#endregion
7179
6957
  //#region src/core/application/plugin/parseConfig.ts
7180
- function parsePluginConfigText(rawText) {
7181
- return wrapBusinessRuleError(() => PluginConfigParser.parse(rawText));
6958
+ function parsePluginConfigText(codec, rawText) {
6959
+ const parsed = parseConfigText(codec, rawText, "Plugin");
6960
+ return wrapBusinessRuleError(() => PluginConfigParser.parse(parsed));
7182
6961
  }
7183
6962
  //#endregion
7184
6963
  //#region src/core/application/plugin/applyPlugin.ts
7185
6964
  async function applyPlugin({ container }) {
7186
6965
  const result = await container.pluginStorage.get();
7187
6966
  if (!result.exists) throw new ValidationError(ValidationErrorCode.InvalidInput, "Plugin config file not found");
7188
- const config = parsePluginConfigText(result.content);
6967
+ const config = parsePluginConfigText(container.configCodec, result.content);
7189
6968
  const current = await container.pluginConfigurator.getPlugins();
7190
6969
  const currentIds = new Set(current.plugins.map((p) => p.id));
7191
6970
  const missingIds = config.plugins.map((p) => p.id).filter((id) => !currentIds.has(id));
@@ -7217,91 +6996,32 @@ const { resolveFilePath: resolvePluginFilePath, resolveContainerConfig: resolveP
7217
6996
  });
7218
6997
  //#endregion
7219
6998
  //#region src/cli/commands/plugin/apply.ts
7220
- async function runPlugin(config) {
7221
- const container = createPluginCliContainer(config);
7222
- const s = p.spinner();
7223
- s.start("Applying plugins...");
7224
- await applyPlugin({ container });
7225
- s.stop("Plugins applied.");
7226
- p.log.success("Plugins applied successfully.");
7227
- return container;
7228
- }
7229
- var apply_default$6 = define({
7230
- name: "apply",
6999
+ var apply_default$6 = createApplyCommand({
7231
7000
  description: "Apply plugins from YAML to kintone app",
7232
- args: {
7233
- ...pluginArgs,
7234
- ...confirmArgs
7235
- },
7236
- run: async (ctx) => {
7237
- try {
7238
- const values = ctx.values;
7239
- const skipConfirm = values.yes === true;
7240
- await routeMultiApp(values, {
7241
- singleLegacy: async () => {
7242
- await confirmAndDeploy([await runPlugin(resolvePluginContainerConfig(values))], skipConfirm);
7243
- },
7244
- singleApp: async (app, projectConfig) => {
7245
- await confirmAndDeploy([await runPlugin(resolvePluginAppContainerConfig(app, projectConfig, values))], skipConfirm);
7246
- },
7247
- multiApp: async (plan, projectConfig) => {
7248
- const containers = [];
7249
- await runMultiAppWithHeaders(plan, async (app) => {
7250
- const container = await runPlugin(resolvePluginAppContainerConfig(app, projectConfig, values));
7251
- containers.push({
7252
- appDeployer: container.appDeployer,
7253
- appName: app.name
7254
- });
7255
- });
7256
- await confirmAndDeploy(containers, skipConfirm);
7257
- }
7258
- });
7259
- } catch (error) {
7260
- handleCliError(error);
7261
- }
7262
- }
7001
+ args: pluginArgs,
7002
+ spinnerMessage: "Applying plugins...",
7003
+ spinnerStopMessage: "Plugins applied.",
7004
+ successMessage: "Plugins applied successfully.",
7005
+ createContainer: createPluginCliContainer,
7006
+ applyFn: applyPlugin,
7007
+ resolveContainerConfig: resolvePluginContainerConfig,
7008
+ resolveAppContainerConfig: resolvePluginAppContainerConfig
7263
7009
  });
7264
7010
  //#endregion
7265
7011
  //#region src/cli/commands/plugin/capture.ts
7266
- async function runCapturePlugin(config) {
7267
- const container = createPluginCliContainer(config);
7268
- const s = p.spinner();
7269
- s.start("Capturing plugins...");
7270
- const result = await capturePlugin({ container });
7271
- s.stop("Plugins captured.");
7272
- await savePlugin({
7273
- container,
7274
- input: { configText: result.configText }
7275
- });
7276
- p.log.success(`Plugins saved to: ${pc.cyan(config.pluginFilePath)}`);
7277
- if (result.hasExistingConfig) p.log.warn("Existing plugin file was overwritten.");
7278
- }
7279
- var capture_default$7 = define({
7280
- name: "capture",
7012
+ var capture_default$7 = createCaptureCommand({
7281
7013
  description: "Capture current plugins from kintone app to file",
7282
7014
  args: pluginArgs,
7283
- run: async (ctx) => {
7284
- try {
7285
- const values = ctx.values;
7286
- await routeMultiApp(values, {
7287
- singleLegacy: async () => {
7288
- await runCapturePlugin(resolvePluginContainerConfig(values));
7289
- },
7290
- singleApp: async (app, projectConfig) => {
7291
- await runCapturePlugin(resolvePluginAppContainerConfig(app, projectConfig, values));
7292
- },
7293
- multiApp: async (plan, projectConfig) => {
7294
- await runMultiAppWithFailCheck(plan, async (app) => {
7295
- const config = resolvePluginAppContainerConfig(app, projectConfig, values);
7296
- printAppHeader(app.name, app.appId);
7297
- await runCapturePlugin(config);
7298
- }, "All plugin captures completed successfully.");
7299
- }
7300
- });
7301
- } catch (error) {
7302
- handleCliError(error);
7303
- }
7304
- }
7015
+ spinnerMessage: "Capturing plugins...",
7016
+ spinnerStopMessage: "Plugins captured.",
7017
+ domainLabel: "Plugins",
7018
+ multiAppSuccessMessage: "All plugin captures completed successfully.",
7019
+ createContainer: createPluginCliContainer,
7020
+ captureFn: capturePlugin,
7021
+ saveFn: savePlugin,
7022
+ getConfigFilePath: (config) => config.pluginFilePath,
7023
+ resolveContainerConfig: resolvePluginContainerConfig,
7024
+ resolveAppContainerConfig: resolvePluginAppContainerConfig
7305
7025
  });
7306
7026
  //#endregion
7307
7027
  //#region src/core/domain/plugin/services/diffDetector.ts
@@ -7340,7 +7060,7 @@ async function detectPluginDiff({ container }) {
7340
7060
  return detectDiffFromConfig({
7341
7061
  getStorage: () => container.pluginStorage.get(),
7342
7062
  fetchRemote: () => container.pluginConfigurator.getPlugins(),
7343
- parseConfig: parsePluginConfigText,
7063
+ parseConfig: (content) => parsePluginConfigText(container.configCodec, content),
7344
7064
  detect: (local, remote) => PluginDiffDetector.detect(local, { plugins: remote.plugins }),
7345
7065
  notFoundMessage: "Plugin config file not found"
7346
7066
  });
@@ -7438,19 +7158,15 @@ function parseAction(raw, index) {
7438
7158
  };
7439
7159
  return result;
7440
7160
  }
7441
- const ProcessManagementConfigParser = { parse: (rawText) => {
7442
- const parsed = parseYamlConfig(rawText, {
7443
- emptyConfigText: ProcessManagementErrorCode.PmEmptyConfigText,
7444
- invalidConfigYaml: ProcessManagementErrorCode.PmInvalidConfigYaml,
7445
- invalidConfigStructure: ProcessManagementErrorCode.PmInvalidConfigStructure
7446
- }, "Process management");
7447
- const enable = parsed.enable !== void 0 && parsed.enable !== null ? parseStrictBoolean(parsed.enable, "enable", "Config", ProcessManagementErrorCode.PmInvalidBooleanField) : false;
7448
- if (parsed.states !== void 0 && parsed.states !== null && !isRecord(parsed.states)) throw new BusinessRuleError(ProcessManagementErrorCode.PmInvalidConfigStructure, "Config \"states\" must be an object (map of state name to state definition)");
7449
- const rawStates = isRecord(parsed.states) ? parsed.states : {};
7161
+ const ProcessManagementConfigParser = { parse: (parsed) => {
7162
+ const obj = validateParsedConfig(parsed, ProcessManagementErrorCode.PmInvalidConfigStructure, "Process management");
7163
+ const enable = obj.enable !== void 0 && obj.enable !== null ? parseStrictBoolean(obj.enable, "enable", "Config", ProcessManagementErrorCode.PmInvalidBooleanField) : false;
7164
+ if (obj.states !== void 0 && obj.states !== null && !isRecord(obj.states)) throw new BusinessRuleError(ProcessManagementErrorCode.PmInvalidConfigStructure, "Config \"states\" must be an object (map of state name to state definition)");
7165
+ const rawStates = isRecord(obj.states) ? obj.states : {};
7450
7166
  const states = {};
7451
7167
  for (const [name, value] of Object.entries(rawStates)) states[name] = parseState(value, name);
7452
- if (!Array.isArray(parsed.actions) && parsed.actions !== void 0) throw new BusinessRuleError(ProcessManagementErrorCode.PmInvalidConfigStructure, "Config \"actions\" must be an array");
7453
- const actions = (Array.isArray(parsed.actions) ? parsed.actions : []).map((item, i) => parseAction(item, i));
7168
+ if (!Array.isArray(obj.actions) && obj.actions !== void 0) throw new BusinessRuleError(ProcessManagementErrorCode.PmInvalidConfigStructure, "Config \"actions\" must be an array");
7169
+ const actions = (Array.isArray(obj.actions) ? obj.actions : []).map((item, i) => parseAction(item, i));
7454
7170
  const actionNames = /* @__PURE__ */ new Set();
7455
7171
  for (const action of actions) {
7456
7172
  if (actionNames.has(action.name)) throw new BusinessRuleError(ProcessManagementErrorCode.PmDuplicateActionName, `Duplicate action name: "${action.name}"`);
@@ -7469,15 +7185,16 @@ const ProcessManagementConfigParser = { parse: (rawText) => {
7469
7185
  } };
7470
7186
  //#endregion
7471
7187
  //#region src/core/application/processManagement/parseConfig.ts
7472
- function parseProcessManagementConfigText(rawText) {
7473
- return wrapBusinessRuleError(() => ProcessManagementConfigParser.parse(rawText));
7188
+ function parseProcessManagementConfigText(codec, rawText) {
7189
+ const parsed = parseConfigText(codec, rawText, "Process management");
7190
+ return wrapBusinessRuleError(() => ProcessManagementConfigParser.parse(parsed));
7474
7191
  }
7475
7192
  //#endregion
7476
7193
  //#region src/core/application/processManagement/applyProcessManagement.ts
7477
7194
  async function applyProcessManagement({ container }) {
7478
7195
  const result = await container.processManagementStorage.get();
7479
7196
  if (!result.exists) throw new ValidationError(ValidationErrorCode.InvalidInput, "Process management config file not found");
7480
- const config = parseProcessManagementConfigText(result.content);
7197
+ const config = parseProcessManagementConfigText(container.configCodec, result.content);
7481
7198
  const current = await container.processManagementConfigurator.getProcessManagement();
7482
7199
  const enableChanged = current.config.enable !== config.enable;
7483
7200
  await container.processManagementConfigurator.updateProcessManagement({
@@ -7512,92 +7229,35 @@ const { resolveFilePath: resolveProcessFilePath, resolveContainerConfig: resolve
7512
7229
  });
7513
7230
  //#endregion
7514
7231
  //#region src/cli/commands/process/apply.ts
7515
- async function runProcessApply(config) {
7516
- const container = createProcessManagementCliContainer(config);
7517
- const s = p.spinner();
7518
- s.start("Applying process management settings...");
7519
- const result = await applyProcessManagement({ container });
7520
- s.stop("Process management settings applied.");
7521
- 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.");
7522
- p.log.success("Process management settings applied successfully.");
7523
- return container;
7524
- }
7525
- var apply_default$5 = define({
7526
- name: "apply",
7232
+ var apply_default$5 = createApplyCommand({
7527
7233
  description: "Apply process management settings from YAML to kintone app",
7528
- args: {
7529
- ...processArgs,
7530
- ...confirmArgs
7234
+ args: processArgs,
7235
+ spinnerMessage: "Applying process management settings...",
7236
+ spinnerStopMessage: "Process management settings applied.",
7237
+ successMessage: "Process management settings applied successfully.",
7238
+ createContainer: createProcessManagementCliContainer,
7239
+ applyFn: applyProcessManagement,
7240
+ onResult: (result) => {
7241
+ 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.");
7531
7242
  },
7532
- run: async (ctx) => {
7533
- try {
7534
- const values = ctx.values;
7535
- const skipConfirm = values.yes === true;
7536
- await routeMultiApp(values, {
7537
- singleLegacy: async () => {
7538
- await confirmAndDeploy([await runProcessApply(resolveProcessContainerConfig(values))], skipConfirm);
7539
- },
7540
- singleApp: async (app, projectConfig) => {
7541
- await confirmAndDeploy([await runProcessApply(resolveProcessAppContainerConfig(app, projectConfig, values))], skipConfirm);
7542
- },
7543
- multiApp: async (plan, projectConfig) => {
7544
- const containers = [];
7545
- await runMultiAppWithHeaders(plan, async (app) => {
7546
- const container = await runProcessApply(resolveProcessAppContainerConfig(app, projectConfig, values));
7547
- containers.push({
7548
- appDeployer: container.appDeployer,
7549
- appName: app.name
7550
- });
7551
- });
7552
- await confirmAndDeploy(containers, skipConfirm);
7553
- }
7554
- });
7555
- } catch (error) {
7556
- handleCliError(error);
7557
- }
7558
- }
7243
+ resolveContainerConfig: resolveProcessContainerConfig,
7244
+ resolveAppContainerConfig: resolveProcessAppContainerConfig
7559
7245
  });
7560
7246
  //#endregion
7561
7247
  //#region src/cli/commands/process/capture.ts
7562
- async function runCaptureProcess(config) {
7563
- const container = createProcessManagementCliContainer(config);
7564
- const s = p.spinner();
7565
- s.start("Capturing process management settings...");
7566
- const result = await captureProcessManagement({ container });
7567
- s.stop("Process management settings captured.");
7568
- await saveProcessManagement({
7569
- container,
7570
- input: { configText: result.configText }
7571
- });
7572
- p.log.success(`Process management settings saved to: ${pc.cyan(config.processFilePath)}`);
7573
- if (result.hasExistingConfig) p.log.warn("Existing process management file was overwritten.");
7574
- }
7575
- var capture_default$6 = define({
7576
- name: "capture",
7248
+ var capture_default$6 = createCaptureCommand({
7577
7249
  description: "Capture current process management settings from kintone app to file",
7578
7250
  args: processArgs,
7579
- run: async (ctx) => {
7580
- try {
7581
- const values = ctx.values;
7582
- await routeMultiApp(values, {
7583
- singleLegacy: async () => {
7584
- await runCaptureProcess(resolveProcessContainerConfig(values));
7585
- },
7586
- singleApp: async (app, projectConfig) => {
7587
- await runCaptureProcess(resolveProcessAppContainerConfig(app, projectConfig, values));
7588
- },
7589
- multiApp: async (plan, projectConfig) => {
7590
- await runMultiAppWithFailCheck(plan, async (app) => {
7591
- const config = resolveProcessAppContainerConfig(app, projectConfig, values);
7592
- printAppHeader(app.name, app.appId);
7593
- await runCaptureProcess(config);
7594
- }, "All process management captures completed successfully.");
7595
- }
7596
- });
7597
- } catch (error) {
7598
- handleCliError(error);
7599
- }
7600
- }
7251
+ spinnerMessage: "Capturing process management settings...",
7252
+ spinnerStopMessage: "Process management settings captured.",
7253
+ domainLabel: "Process management settings",
7254
+ multiAppSuccessMessage: "All process management captures completed successfully.",
7255
+ createContainer: createProcessManagementCliContainer,
7256
+ captureFn: captureProcessManagement,
7257
+ saveFn: saveProcessManagement,
7258
+ getConfigFilePath: (config) => config.processFilePath,
7259
+ resolveContainerConfig: resolveProcessContainerConfig,
7260
+ resolveAppContainerConfig: resolveProcessAppContainerConfig
7601
7261
  });
7602
7262
  //#endregion
7603
7263
  //#region src/core/domain/processManagement/services/diffDetector.ts
@@ -7626,14 +7286,7 @@ function compareActions(localAction, remoteAction) {
7626
7286
  if (!isExecutableUserEqual(localAction.executableUser, remoteAction.executableUser)) diffs.push("executableUser changed");
7627
7287
  return diffs;
7628
7288
  }
7629
- function compareConfigs$1(local, remote) {
7630
- const entries = [];
7631
- if (local.enable !== remote.enable) entries.push({
7632
- type: "modified",
7633
- category: "enable",
7634
- name: "enable",
7635
- details: `${String(remote.enable)} -> ${String(local.enable)}`
7636
- });
7289
+ function compareStates(local, remote, entries) {
7637
7290
  const localStateNames = new Set(Object.keys(local.states));
7638
7291
  const remoteStateNames = new Set(Object.keys(remote.states));
7639
7292
  for (const name of localStateNames) if (!remoteStateNames.has(name)) entries.push({
@@ -7662,6 +7315,8 @@ function compareConfigs$1(local, remote) {
7662
7315
  name,
7663
7316
  details: `assignee: ${remote.states[name].assignee.type}`
7664
7317
  });
7318
+ }
7319
+ function compareActionEntries(local, remote, entries) {
7665
7320
  const localActionMap = new Map(local.actions.map((a) => [a.name, a]));
7666
7321
  const remoteActionMap = new Map(remote.actions.map((a) => [a.name, a]));
7667
7322
  for (const [name, localAction] of localActionMap) {
@@ -7688,6 +7343,17 @@ function compareConfigs$1(local, remote) {
7688
7343
  name,
7689
7344
  details: `${remoteAction.from} -> ${remoteAction.to}`
7690
7345
  });
7346
+ }
7347
+ function compareConfigs$1(local, remote) {
7348
+ const entries = [];
7349
+ if (local.enable !== remote.enable) entries.push({
7350
+ type: "modified",
7351
+ category: "enable",
7352
+ name: "enable",
7353
+ details: `${String(remote.enable)} -> ${String(local.enable)}`
7354
+ });
7355
+ compareStates(local, remote, entries);
7356
+ compareActionEntries(local, remote, entries);
7691
7357
  return entries;
7692
7358
  }
7693
7359
  const ProcessManagementDiffDetector = { detect: (local, remote) => {
@@ -7699,7 +7365,7 @@ async function detectProcessManagementDiff({ container }) {
7699
7365
  return detectDiffFromConfig({
7700
7366
  getStorage: () => container.processManagementStorage.get(),
7701
7367
  fetchRemote: () => container.processManagementConfigurator.getProcessManagement(),
7702
- parseConfig: parseProcessManagementConfigText,
7368
+ parseConfig: (content) => parseProcessManagementConfigText(container.configCodec, content),
7703
7369
  detect: (local, remote) => ProcessManagementDiffDetector.detect(local, remote.config),
7704
7370
  notFoundMessage: "Process management config file not found"
7705
7371
  });
@@ -7762,14 +7428,10 @@ function parseRecordRight(raw, index) {
7762
7428
  entities
7763
7429
  };
7764
7430
  }
7765
- const RecordPermissionConfigParser = { parse: (rawText) => {
7766
- const parsed = parseYamlConfig(rawText, {
7767
- emptyConfigText: RecordPermissionErrorCode.RpEmptyConfigText,
7768
- invalidConfigYaml: RecordPermissionErrorCode.RpInvalidConfigYaml,
7769
- invalidConfigStructure: RecordPermissionErrorCode.RpInvalidConfigStructure
7770
- }, "Record permission");
7771
- if (!Array.isArray(parsed.rights)) throw new BusinessRuleError(RecordPermissionErrorCode.RpInvalidConfigStructure, "Config must have a \"rights\" array");
7772
- const rights = parsed.rights.map((item, i) => parseRecordRight(item, i));
7431
+ const RecordPermissionConfigParser = { parse: (parsed) => {
7432
+ const obj = validateParsedConfig(parsed, RecordPermissionErrorCode.RpInvalidConfigStructure, "Record permission");
7433
+ if (!Array.isArray(obj.rights)) throw new BusinessRuleError(RecordPermissionErrorCode.RpInvalidConfigStructure, "Config must have a \"rights\" array");
7434
+ const rights = obj.rights.map((item, i) => parseRecordRight(item, i));
7773
7435
  for (const right of rights) {
7774
7436
  const seenKeys = /* @__PURE__ */ new Set();
7775
7437
  for (const re of right.entities) {
@@ -7782,15 +7444,16 @@ const RecordPermissionConfigParser = { parse: (rawText) => {
7782
7444
  } };
7783
7445
  //#endregion
7784
7446
  //#region src/core/application/recordPermission/parseConfig.ts
7785
- function parseRecordPermissionConfigText(rawText) {
7786
- return wrapBusinessRuleError(() => RecordPermissionConfigParser.parse(rawText));
7447
+ function parseRecordPermissionConfigText(codec, rawText) {
7448
+ const parsed = parseConfigText(codec, rawText, "Record permission");
7449
+ return wrapBusinessRuleError(() => RecordPermissionConfigParser.parse(parsed));
7787
7450
  }
7788
7451
  //#endregion
7789
7452
  //#region src/core/application/recordPermission/applyRecordPermission.ts
7790
7453
  async function applyRecordPermission({ container }) {
7791
7454
  await applyFromConfig({
7792
7455
  getStorage: () => container.recordPermissionStorage.get(),
7793
- parseConfig: parseRecordPermissionConfigText,
7456
+ parseConfig: (content) => parseRecordPermissionConfigText(container.configCodec, content),
7794
7457
  fetchRemote: () => container.recordPermissionConfigurator.getRecordPermissions(),
7795
7458
  update: async (config, current) => {
7796
7459
  await container.recordPermissionConfigurator.updateRecordPermissions({
@@ -7824,91 +7487,32 @@ const { resolveFilePath: resolveRecordAclFilePath, resolveContainerConfig: resol
7824
7487
  });
7825
7488
  //#endregion
7826
7489
  //#region src/cli/commands/record-acl/apply.ts
7827
- async function runRecordAcl(config) {
7828
- const container = createRecordPermissionCliContainer(config);
7829
- const s = p.spinner();
7830
- s.start("Applying record access permissions...");
7831
- await applyRecordPermission({ container });
7832
- s.stop("Record access permissions applied.");
7833
- p.log.success("Record access permissions applied successfully.");
7834
- return container;
7835
- }
7836
- var apply_default$4 = define({
7837
- name: "apply",
7490
+ var apply_default$4 = createApplyCommand({
7838
7491
  description: "Apply record access permissions from YAML to kintone app",
7839
- args: {
7840
- ...recordAclArgs,
7841
- ...confirmArgs
7842
- },
7843
- run: async (ctx) => {
7844
- try {
7845
- const values = ctx.values;
7846
- const skipConfirm = values.yes === true;
7847
- await routeMultiApp(values, {
7848
- singleLegacy: async () => {
7849
- await confirmAndDeploy([await runRecordAcl(resolveRecordAclContainerConfig(values))], skipConfirm);
7850
- },
7851
- singleApp: async (app, projectConfig) => {
7852
- await confirmAndDeploy([await runRecordAcl(resolveRecordAclAppContainerConfig(app, projectConfig, values))], skipConfirm);
7853
- },
7854
- multiApp: async (plan, projectConfig) => {
7855
- const containers = [];
7856
- await runMultiAppWithHeaders(plan, async (app) => {
7857
- const container = await runRecordAcl(resolveRecordAclAppContainerConfig(app, projectConfig, values));
7858
- containers.push({
7859
- appDeployer: container.appDeployer,
7860
- appName: app.name
7861
- });
7862
- });
7863
- await confirmAndDeploy(containers, skipConfirm);
7864
- }
7865
- });
7866
- } catch (error) {
7867
- handleCliError(error);
7868
- }
7869
- }
7492
+ args: recordAclArgs,
7493
+ spinnerMessage: "Applying record access permissions...",
7494
+ spinnerStopMessage: "Record access permissions applied.",
7495
+ successMessage: "Record access permissions applied successfully.",
7496
+ createContainer: createRecordPermissionCliContainer,
7497
+ applyFn: applyRecordPermission,
7498
+ resolveContainerConfig: resolveRecordAclContainerConfig,
7499
+ resolveAppContainerConfig: resolveRecordAclAppContainerConfig
7870
7500
  });
7871
7501
  //#endregion
7872
7502
  //#region src/cli/commands/record-acl/capture.ts
7873
- async function runCaptureRecordAcl(config) {
7874
- const container = createRecordPermissionCliContainer(config);
7875
- const s = p.spinner();
7876
- s.start("Capturing record access permissions...");
7877
- const result = await captureRecordPermission({ container });
7878
- s.stop("Record access permissions captured.");
7879
- await saveRecordPermission({
7880
- container,
7881
- input: { configText: result.configText }
7882
- });
7883
- p.log.success(`Record ACL saved to: ${pc.cyan(config.recordAclFilePath)}`);
7884
- if (result.hasExistingConfig) p.log.warn("Existing record ACL file was overwritten.");
7885
- }
7886
- var capture_default$5 = define({
7887
- name: "capture",
7503
+ var capture_default$5 = createCaptureCommand({
7888
7504
  description: "Capture current record access permissions from kintone app to file",
7889
7505
  args: recordAclArgs,
7890
- run: async (ctx) => {
7891
- try {
7892
- const values = ctx.values;
7893
- await routeMultiApp(values, {
7894
- singleLegacy: async () => {
7895
- await runCaptureRecordAcl(resolveRecordAclContainerConfig(values));
7896
- },
7897
- singleApp: async (app, projectConfig) => {
7898
- await runCaptureRecordAcl(resolveRecordAclAppContainerConfig(app, projectConfig, values));
7899
- },
7900
- multiApp: async (plan, projectConfig) => {
7901
- await runMultiAppWithFailCheck(plan, async (app) => {
7902
- const config = resolveRecordAclAppContainerConfig(app, projectConfig, values);
7903
- printAppHeader(app.name, app.appId);
7904
- await runCaptureRecordAcl(config);
7905
- }, "All record ACL captures completed successfully.");
7906
- }
7907
- });
7908
- } catch (error) {
7909
- handleCliError(error);
7910
- }
7911
- }
7506
+ spinnerMessage: "Capturing record access permissions...",
7507
+ spinnerStopMessage: "Record access permissions captured.",
7508
+ domainLabel: "Record ACL",
7509
+ multiAppSuccessMessage: "All record ACL captures completed successfully.",
7510
+ createContainer: createRecordPermissionCliContainer,
7511
+ captureFn: captureRecordPermission,
7512
+ saveFn: saveRecordPermission,
7513
+ getConfigFilePath: (config) => config.recordAclFilePath,
7514
+ resolveContainerConfig: resolveRecordAclContainerConfig,
7515
+ resolveAppContainerConfig: resolveRecordAclAppContainerConfig
7912
7516
  });
7913
7517
  //#endregion
7914
7518
  //#region src/core/domain/recordPermission/services/diffDetector.ts
@@ -7940,40 +7544,38 @@ function describeRight(right) {
7940
7544
  });
7941
7545
  return perEntity.length > 0 ? perEntity.join(", ") : "no entities";
7942
7546
  }
7547
+ function compareRightsForFilter(filterCond, localRights, remoteRights, entries) {
7548
+ const maxLen = Math.max(localRights.length, remoteRights.length);
7549
+ for (let i = 0; i < maxLen; i++) {
7550
+ const localRight = localRights[i];
7551
+ const remoteRight = remoteRights[i];
7552
+ if (localRight && !remoteRight) entries.push({
7553
+ type: "added",
7554
+ filterCond,
7555
+ details: describeRight(localRight)
7556
+ });
7557
+ else if (!localRight && remoteRight) entries.push({
7558
+ type: "deleted",
7559
+ filterCond,
7560
+ details: describeRight(remoteRight)
7561
+ });
7562
+ else if (localRight && remoteRight) {
7563
+ if (!isRightEqual(localRight, remoteRight)) {
7564
+ const detail = localRight.entities.length !== remoteRight.entities.length ? `entities: ${remoteRight.entities.length} -> ${localRight.entities.length}` : "entities changed";
7565
+ entries.push({
7566
+ type: "modified",
7567
+ filterCond,
7568
+ details: detail
7569
+ });
7570
+ }
7571
+ }
7572
+ }
7573
+ }
7943
7574
  const RecordPermissionDiffDetector = { detect: (local, remote) => {
7944
7575
  const entries = [];
7945
7576
  const localMulti = groupByKey(local.rights, (r) => r.filterCond);
7946
7577
  const remoteMulti = groupByKey(remote.rights, (r) => r.filterCond);
7947
- for (const [filterCond, localRights] of localMulti) {
7948
- const remoteRights = remoteMulti.get(filterCond) ?? [];
7949
- const maxLen = Math.max(localRights.length, remoteRights.length);
7950
- for (let i = 0; i < maxLen; i++) {
7951
- const localRight = localRights[i];
7952
- const remoteRight = remoteRights[i];
7953
- if (localRight && !remoteRight) entries.push({
7954
- type: "added",
7955
- filterCond,
7956
- details: describeRight(localRight)
7957
- });
7958
- else if (!localRight && remoteRight) entries.push({
7959
- type: "deleted",
7960
- filterCond,
7961
- details: describeRight(remoteRight)
7962
- });
7963
- else if (localRight && remoteRight) {
7964
- if (!isRightEqual(localRight, remoteRight)) {
7965
- const diffs = [];
7966
- if (localRight.entities.length !== remoteRight.entities.length) diffs.push(`entities: ${remoteRight.entities.length} -> ${localRight.entities.length}`);
7967
- else diffs.push("entities changed");
7968
- entries.push({
7969
- type: "modified",
7970
- filterCond,
7971
- details: diffs.join(", ")
7972
- });
7973
- }
7974
- }
7975
- }
7976
- }
7578
+ for (const [filterCond, localRights] of localMulti) compareRightsForFilter(filterCond, localRights, remoteMulti.get(filterCond) ?? [], entries);
7977
7579
  for (const [filterCond, remoteRights] of remoteMulti) if (!localMulti.has(filterCond)) for (const remoteRight of remoteRights) entries.push({
7978
7580
  type: "deleted",
7979
7581
  filterCond,
@@ -7987,7 +7589,7 @@ async function detectRecordPermissionDiff({ container }) {
7987
7589
  return detectDiffFromConfig({
7988
7590
  getStorage: () => container.recordPermissionStorage.get(),
7989
7591
  fetchRemote: () => container.recordPermissionConfigurator.getRecordPermissions(),
7990
- parseConfig: parseRecordPermissionConfigText,
7592
+ parseConfig: (content) => parseRecordPermissionConfigText(container.configCodec, content),
7991
7593
  detect: (local, remote) => RecordPermissionDiffDetector.detect(local, { rights: remote.rights }),
7992
7594
  notFoundMessage: "Record permission config file not found"
7993
7595
  });
@@ -8051,45 +7653,48 @@ function parseSort(raw, index) {
8051
7653
  order: raw.order
8052
7654
  };
8053
7655
  }
7656
+ function parsePeriodMonth(raw) {
7657
+ if (raw.month === void 0 || raw.month === null) return void 0;
7658
+ const parsed = Number(raw.month);
7659
+ if (!Number.isInteger(parsed) || parsed < 1 || parsed > 12) throw new BusinessRuleError(ReportErrorCode.RtInvalidConfigStructure, `periodicReport.period has invalid month: ${String(raw.month)}. Must be an integer between 1 and 12`);
7660
+ return parsed;
7661
+ }
7662
+ function parsePeriodPattern(raw) {
7663
+ if (raw.pattern === void 0 || raw.pattern === null) return void 0;
7664
+ if (typeof raw.pattern !== "string" || !isPeriodicReportPattern(raw.pattern)) throw new BusinessRuleError(ReportErrorCode.RtInvalidConfigStructure, `periodicReport.period has invalid pattern: ${String(raw.pattern)}. Must be JAN_APR_JUL_OCT, FEB_MAY_AUG_NOV, or MAR_JUN_SEP_DEC`);
7665
+ return raw.pattern;
7666
+ }
7667
+ function parsePeriodDayOfMonth(raw) {
7668
+ if (raw.dayOfMonth === void 0 || raw.dayOfMonth === null) return void 0;
7669
+ if (raw.dayOfMonth === "END_OF_MONTH") return "END_OF_MONTH";
7670
+ const parsed = Number(raw.dayOfMonth);
7671
+ if (!Number.isInteger(parsed)) throw new BusinessRuleError(ReportErrorCode.RtInvalidConfigStructure, `periodicReport.period has invalid dayOfMonth: ${String(raw.dayOfMonth)}. Must be an integer or "END_OF_MONTH"`);
7672
+ if (parsed < 1 || parsed > 31) throw new BusinessRuleError(ReportErrorCode.RtInvalidConfigStructure, `periodicReport.period has out-of-range dayOfMonth: ${parsed}. Must be 1-31`);
7673
+ return parsed;
7674
+ }
7675
+ function parsePeriodDayOfWeek(raw) {
7676
+ if (raw.dayOfWeek === void 0 || raw.dayOfWeek === null) return void 0;
7677
+ const dayStr = String(raw.dayOfWeek);
7678
+ if (!isDayOfWeek(dayStr)) throw new BusinessRuleError(ReportErrorCode.RtInvalidConfigStructure, `periodicReport.period has invalid dayOfWeek: ${dayStr}. Must be SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, or SATURDAY`);
7679
+ return dayStr;
7680
+ }
7681
+ function parsePeriodMinute(raw) {
7682
+ if (raw.minute === void 0 || raw.minute === null) return void 0;
7683
+ const parsed = Number(raw.minute);
7684
+ if (!Number.isInteger(parsed) || parsed < 0 || parsed > 50 || parsed % 10 !== 0) throw new BusinessRuleError(ReportErrorCode.RtInvalidConfigStructure, `periodicReport.period has invalid minute: ${String(raw.minute)}. Must be a multiple of 10 (0, 10, 20, 30, 40, 50)`);
7685
+ return parsed;
7686
+ }
8054
7687
  function parsePeriodicReportPeriod(raw) {
8055
7688
  if (!isRecord(raw)) throw new BusinessRuleError(ReportErrorCode.RtInvalidConfigStructure, "periodicReport.period must be an object");
8056
7689
  if (typeof raw.every !== "string" || !isPeriodicReportEvery(raw.every)) throw new BusinessRuleError(ReportErrorCode.RtInvalidConfigStructure, `periodicReport.period has invalid every: ${String(raw.every)}. Must be YEAR, QUARTER, MONTH, WEEK, DAY, or HOUR`);
8057
- const every = raw.every;
8058
- let month;
8059
- if (raw.month !== void 0 && raw.month !== null) {
8060
- const parsed = Number(raw.month);
8061
- if (!Number.isInteger(parsed) || parsed < 1 || parsed > 12) throw new BusinessRuleError(ReportErrorCode.RtInvalidConfigStructure, `periodicReport.period has invalid month: ${String(raw.month)}. Must be an integer between 1 and 12`);
8062
- month = parsed;
8063
- }
8064
- let pattern;
8065
- if (raw.pattern !== void 0 && raw.pattern !== null) {
8066
- if (typeof raw.pattern !== "string" || !isPeriodicReportPattern(raw.pattern)) throw new BusinessRuleError(ReportErrorCode.RtInvalidConfigStructure, `periodicReport.period has invalid pattern: ${String(raw.pattern)}. Must be JAN_APR_JUL_OCT, FEB_MAY_AUG_NOV, or MAR_JUN_SEP_DEC`);
8067
- pattern = raw.pattern;
8068
- }
8069
- let dayOfMonth;
8070
- if (raw.dayOfMonth !== void 0 && raw.dayOfMonth !== null) if (raw.dayOfMonth === "END_OF_MONTH") dayOfMonth = "END_OF_MONTH";
8071
- else {
8072
- const parsed = Number(raw.dayOfMonth);
8073
- if (!Number.isInteger(parsed)) throw new BusinessRuleError(ReportErrorCode.RtInvalidConfigStructure, `periodicReport.period has invalid dayOfMonth: ${String(raw.dayOfMonth)}. Must be an integer or "END_OF_MONTH"`);
8074
- if (parsed < 1 || parsed > 31) throw new BusinessRuleError(ReportErrorCode.RtInvalidConfigStructure, `periodicReport.period has out-of-range dayOfMonth: ${parsed}. Must be 1-31`);
8075
- dayOfMonth = parsed;
8076
- }
8077
- let time;
8078
- if (raw.time !== void 0 && raw.time !== null) time = String(raw.time);
8079
- let dayOfWeek;
8080
- if (raw.dayOfWeek !== void 0 && raw.dayOfWeek !== null) {
8081
- const dayStr = String(raw.dayOfWeek);
8082
- if (!isDayOfWeek(dayStr)) throw new BusinessRuleError(ReportErrorCode.RtInvalidConfigStructure, `periodicReport.period has invalid dayOfWeek: ${dayStr}. Must be SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, or SATURDAY`);
8083
- dayOfWeek = dayStr;
8084
- }
8085
- let minute;
8086
- if (raw.minute !== void 0 && raw.minute !== null) {
8087
- const parsed = Number(raw.minute);
8088
- if (!Number.isInteger(parsed) || parsed < 0 || parsed > 50 || parsed % 10 !== 0) throw new BusinessRuleError(ReportErrorCode.RtInvalidConfigStructure, `periodicReport.period has invalid minute: ${String(raw.minute)}. Must be a multiple of 10 (0, 10, 20, 30, 40, 50)`);
8089
- minute = parsed;
8090
- }
7690
+ const month = parsePeriodMonth(raw);
7691
+ const pattern = parsePeriodPattern(raw);
7692
+ const dayOfMonth = parsePeriodDayOfMonth(raw);
7693
+ const time = raw.time !== void 0 && raw.time !== null ? String(raw.time) : void 0;
7694
+ const dayOfWeek = parsePeriodDayOfWeek(raw);
7695
+ const minute = parsePeriodMinute(raw);
8091
7696
  return {
8092
- every,
7697
+ every: raw.every,
8093
7698
  ...month !== void 0 ? { month } : {},
8094
7699
  ...pattern !== void 0 ? { pattern } : {},
8095
7700
  ...dayOfMonth !== void 0 ? { dayOfMonth } : {},
@@ -8107,6 +7712,11 @@ function parsePeriodicReport(raw) {
8107
7712
  period: parsePeriodicReportPeriod(raw.period)
8108
7713
  };
8109
7714
  }
7715
+ function parseOptionalArrayField(raw, fieldName, reportName, parseFn) {
7716
+ const value = raw[fieldName];
7717
+ if (value !== void 0 && !Array.isArray(value)) throw new BusinessRuleError(ReportErrorCode.RtInvalidConfigStructure, `Report "${reportName}" has invalid ${fieldName}: must be an array`);
7718
+ return Array.isArray(value) ? value.map((item, i) => parseFn(item, i)) : [];
7719
+ }
8110
7720
  function parseReportConfig(raw, reportName) {
8111
7721
  if (reportName.length === 0) throw new BusinessRuleError(ReportErrorCode.RtEmptyReportName, "Report name (key) must not be empty");
8112
7722
  if (!isRecord(raw)) throw new BusinessRuleError(ReportErrorCode.RtInvalidConfigStructure, `Report "${reportName}" must be an object`);
@@ -8119,13 +7729,10 @@ function parseReportConfig(raw, reportName) {
8119
7729
  const name = typeof raw.name === "string" && raw.name.length > 0 ? raw.name : reportName;
8120
7730
  if (raw.index !== void 0 && raw.index !== null && (typeof raw.index !== "number" || !Number.isInteger(raw.index) || raw.index < 0)) throw new BusinessRuleError(ReportErrorCode.RtInvalidConfigStructure, `Report "${reportName}" has invalid index: ${String(raw.index)}. Must be a non-negative integer`);
8121
7731
  const index = typeof raw.index === "number" ? raw.index : 0;
8122
- if (raw.groups !== void 0 && !Array.isArray(raw.groups)) throw new BusinessRuleError(ReportErrorCode.RtInvalidConfigStructure, `Report "${reportName}" has invalid groups: must be an array`);
8123
- const groups = Array.isArray(raw.groups) ? raw.groups.map((item, i) => parseGroup(item, i)) : [];
8124
- if (raw.aggregations !== void 0 && !Array.isArray(raw.aggregations)) throw new BusinessRuleError(ReportErrorCode.RtInvalidConfigStructure, `Report "${reportName}" has invalid aggregations: must be an array`);
8125
- const aggregations = Array.isArray(raw.aggregations) ? raw.aggregations.map((item, i) => parseAggregation(item, i)) : [];
7732
+ const groups = parseOptionalArrayField(raw, "groups", reportName, parseGroup);
7733
+ const aggregations = parseOptionalArrayField(raw, "aggregations", reportName, parseAggregation);
8126
7734
  const filterCond = typeof raw.filterCond === "string" ? raw.filterCond : "";
8127
- if (raw.sorts !== void 0 && !Array.isArray(raw.sorts)) throw new BusinessRuleError(ReportErrorCode.RtInvalidConfigStructure, `Report "${reportName}" has invalid sorts: must be an array`);
8128
- const sorts = Array.isArray(raw.sorts) ? raw.sorts.map((item, i) => parseSort(item, i)) : [];
7735
+ const sorts = parseOptionalArrayField(raw, "sorts", reportName, parseSort);
8129
7736
  const result = {
8130
7737
  chartType: raw.chartType,
8131
7738
  ...chartMode !== void 0 ? { chartMode } : {},
@@ -8142,12 +7749,8 @@ function parseReportConfig(raw, reportName) {
8142
7749
  };
8143
7750
  return result;
8144
7751
  }
8145
- const ReportConfigParser = { parse: (rawText) => {
8146
- const obj = parseYamlConfig(rawText, {
8147
- emptyConfigText: ReportErrorCode.RtEmptyConfigText,
8148
- invalidConfigYaml: ReportErrorCode.RtInvalidConfigYaml,
8149
- invalidConfigStructure: ReportErrorCode.RtInvalidConfigStructure
8150
- }, "Report");
7752
+ const ReportConfigParser = { parse: (parsed) => {
7753
+ const obj = validateParsedConfig(parsed, ReportErrorCode.RtInvalidConfigStructure, "Report");
8151
7754
  if (!isRecord(obj.reports)) throw new BusinessRuleError(ReportErrorCode.RtInvalidConfigStructure, "Config must have a \"reports\" object");
8152
7755
  const rawReports = obj.reports;
8153
7756
  const reports = {};
@@ -8156,15 +7759,16 @@ const ReportConfigParser = { parse: (rawText) => {
8156
7759
  } };
8157
7760
  //#endregion
8158
7761
  //#region src/core/application/report/parseConfig.ts
8159
- function parseReportConfigText(rawText) {
8160
- return wrapBusinessRuleError(() => ReportConfigParser.parse(rawText));
7762
+ function parseReportConfigText(codec, rawText) {
7763
+ const parsed = parseConfigText(codec, rawText, "Report");
7764
+ return wrapBusinessRuleError(() => ReportConfigParser.parse(parsed));
8161
7765
  }
8162
7766
  //#endregion
8163
7767
  //#region src/core/application/report/applyReport.ts
8164
7768
  async function applyReport({ container }) {
8165
7769
  await applyFromConfig({
8166
7770
  getStorage: () => container.reportStorage.get(),
8167
- parseConfig: parseReportConfigText,
7771
+ parseConfig: (content) => parseReportConfigText(container.configCodec, content),
8168
7772
  fetchRemote: () => container.reportConfigurator.getReports(),
8169
7773
  update: async (config, current) => {
8170
7774
  await container.reportConfigurator.updateReports({
@@ -8198,91 +7802,32 @@ const { resolveFilePath: resolveReportFilePath, resolveContainerConfig: resolveR
8198
7802
  });
8199
7803
  //#endregion
8200
7804
  //#region src/cli/commands/report/apply.ts
8201
- async function runReport(config) {
8202
- const container = createReportCliContainer(config);
8203
- const s = p.spinner();
8204
- s.start("Applying report settings...");
8205
- await applyReport({ container });
8206
- s.stop("Report settings applied.");
8207
- p.log.success("Report settings applied successfully.");
8208
- return container;
8209
- }
8210
- var apply_default$3 = define({
8211
- name: "apply",
7805
+ var apply_default$3 = createApplyCommand({
8212
7806
  description: "Apply report settings from YAML to kintone app",
8213
- args: {
8214
- ...reportArgs,
8215
- ...confirmArgs
8216
- },
8217
- run: async (ctx) => {
8218
- try {
8219
- const values = ctx.values;
8220
- const skipConfirm = values.yes === true;
8221
- await routeMultiApp(values, {
8222
- singleLegacy: async () => {
8223
- await confirmAndDeploy([await runReport(resolveReportContainerConfig(values))], skipConfirm);
8224
- },
8225
- singleApp: async (app, projectConfig) => {
8226
- await confirmAndDeploy([await runReport(resolveReportAppContainerConfig(app, projectConfig, values))], skipConfirm);
8227
- },
8228
- multiApp: async (plan, projectConfig) => {
8229
- const containers = [];
8230
- await runMultiAppWithHeaders(plan, async (app) => {
8231
- const container = await runReport(resolveReportAppContainerConfig(app, projectConfig, values));
8232
- containers.push({
8233
- appDeployer: container.appDeployer,
8234
- appName: app.name
8235
- });
8236
- });
8237
- await confirmAndDeploy(containers, skipConfirm);
8238
- }
8239
- });
8240
- } catch (error) {
8241
- handleCliError(error);
8242
- }
8243
- }
7807
+ args: reportArgs,
7808
+ spinnerMessage: "Applying report settings...",
7809
+ spinnerStopMessage: "Report settings applied.",
7810
+ successMessage: "Report settings applied successfully.",
7811
+ createContainer: createReportCliContainer,
7812
+ applyFn: applyReport,
7813
+ resolveContainerConfig: resolveReportContainerConfig,
7814
+ resolveAppContainerConfig: resolveReportAppContainerConfig
8244
7815
  });
8245
7816
  //#endregion
8246
7817
  //#region src/cli/commands/report/capture.ts
8247
- async function runCaptureReport(config) {
8248
- const container = createReportCliContainer(config);
8249
- const s = p.spinner();
8250
- s.start("Capturing report settings...");
8251
- const result = await captureReport({ container });
8252
- s.stop("Report settings captured.");
8253
- await saveReport({
8254
- container,
8255
- input: { configText: result.configText }
8256
- });
8257
- p.log.success(`Reports saved to: ${pc.cyan(config.reportFilePath)}`);
8258
- if (result.hasExistingConfig) p.log.warn("Existing report file was overwritten.");
8259
- }
8260
- var capture_default$4 = define({
8261
- name: "capture",
7818
+ var capture_default$4 = createCaptureCommand({
8262
7819
  description: "Capture current report settings from kintone app to file",
8263
7820
  args: reportArgs,
8264
- run: async (ctx) => {
8265
- try {
8266
- const values = ctx.values;
8267
- await routeMultiApp(values, {
8268
- singleLegacy: async () => {
8269
- await runCaptureReport(resolveReportContainerConfig(values));
8270
- },
8271
- singleApp: async (app, projectConfig) => {
8272
- await runCaptureReport(resolveReportAppContainerConfig(app, projectConfig, values));
8273
- },
8274
- multiApp: async (plan, projectConfig) => {
8275
- await runMultiAppWithFailCheck(plan, async (app) => {
8276
- const config = resolveReportAppContainerConfig(app, projectConfig, values);
8277
- printAppHeader(app.name, app.appId);
8278
- await runCaptureReport(config);
8279
- }, "All report captures completed successfully.");
8280
- }
8281
- });
8282
- } catch (error) {
8283
- handleCliError(error);
8284
- }
8285
- }
7821
+ spinnerMessage: "Capturing report settings...",
7822
+ spinnerStopMessage: "Report settings captured.",
7823
+ domainLabel: "Reports",
7824
+ multiAppSuccessMessage: "All report captures completed successfully.",
7825
+ createContainer: createReportCliContainer,
7826
+ captureFn: captureReport,
7827
+ saveFn: saveReport,
7828
+ getConfigFilePath: (config) => config.reportFilePath,
7829
+ resolveContainerConfig: resolveReportContainerConfig,
7830
+ resolveAppContainerConfig: resolveReportAppContainerConfig
8286
7831
  });
8287
7832
  //#endregion
8288
7833
  //#region src/core/domain/report/services/diffDetector.ts
@@ -8327,7 +7872,7 @@ async function detectReportDiff({ container }) {
8327
7872
  return detectDiffFromConfig({
8328
7873
  getStorage: () => container.reportStorage.get(),
8329
7874
  fetchRemote: () => container.reportConfigurator.getReports(),
8330
- parseConfig: parseReportConfigText,
7875
+ parseConfig: (content) => parseReportConfigText(container.configCodec, content),
8331
7876
  detect: (local, remote) => ReportDiffDetector.detect(local, { reports: remote.reports }),
8332
7877
  notFoundMessage: "Report config file not found"
8333
7878
  });
@@ -8700,6 +8245,35 @@ function buildFieldDefinition(base, fieldType, properties) {
8700
8245
  };
8701
8246
  }
8702
8247
  }
8248
+ function parseReferenceTableField(raw, code, base) {
8249
+ if (!isRecord(raw.referenceTable)) throw new BusinessRuleError(FormSchemaErrorCode.FsInvalidSchemaStructure, `Field "${code}" of type REFERENCE_TABLE must have a "referenceTable" property`);
8250
+ const refTable = raw.referenceTable;
8251
+ if (!isRecord(refTable.relatedApp)) throw new BusinessRuleError(FormSchemaErrorCode.FsInvalidSchemaStructure, `Field "${code}" of type REFERENCE_TABLE must have "referenceTable.relatedApp"`);
8252
+ if (!isRecord(refTable.condition)) throw new BusinessRuleError(FormSchemaErrorCode.FsInvalidSchemaStructure, `Field "${code}" of type REFERENCE_TABLE must have "referenceTable.condition"`);
8253
+ if (!Array.isArray(refTable.displayFields)) throw new BusinessRuleError(FormSchemaErrorCode.FsInvalidSchemaStructure, `Field "${code}" of type REFERENCE_TABLE must have "referenceTable.displayFields" array`);
8254
+ const condition = refTable.condition;
8255
+ const displayFields = refTable.displayFields.map((f) => FieldCode.create(f));
8256
+ const relatedApp = refTable.relatedApp;
8257
+ const conditionField = String(condition.field ?? "");
8258
+ const conditionRelatedField = String(condition.relatedField ?? "");
8259
+ if (conditionField.length === 0) throw new BusinessRuleError(FormSchemaErrorCode.FsInvalidSchemaStructure, `Field "${code}" of type REFERENCE_TABLE must have a non-empty "referenceTable.condition.field"`);
8260
+ if (conditionRelatedField.length === 0) throw new BusinessRuleError(FormSchemaErrorCode.FsInvalidSchemaStructure, `Field "${code}" of type REFERENCE_TABLE must have a non-empty "referenceTable.condition.relatedField"`);
8261
+ return {
8262
+ ...base,
8263
+ type: "REFERENCE_TABLE",
8264
+ properties: { referenceTable: {
8265
+ relatedApp: { app: String(relatedApp.app ?? "") },
8266
+ condition: {
8267
+ field: FieldCode.create(conditionField),
8268
+ relatedField: FieldCode.create(conditionRelatedField)
8269
+ },
8270
+ ...refTable.filterCond !== void 0 ? { filterCond: String(refTable.filterCond) } : {},
8271
+ displayFields,
8272
+ ...refTable.sort !== void 0 ? { sort: String(refTable.sort) } : {},
8273
+ ...refTable.size !== void 0 ? { size: String(refTable.size) } : {}
8274
+ } }
8275
+ };
8276
+ }
8703
8277
  function parseFieldDefinitionFromFlat(raw) {
8704
8278
  const code = String(raw.code);
8705
8279
  const type = String(raw.type);
@@ -8724,35 +8298,7 @@ function parseFieldDefinitionFromFlat(raw) {
8724
8298
  properties: { fields: subFields }
8725
8299
  };
8726
8300
  }
8727
- if (fieldType === "REFERENCE_TABLE") {
8728
- if (!isRecord(raw.referenceTable)) throw new BusinessRuleError(FormSchemaErrorCode.FsInvalidSchemaStructure, `Field "${code}" of type REFERENCE_TABLE must have a "referenceTable" property`);
8729
- const refTable = raw.referenceTable;
8730
- if (!isRecord(refTable.relatedApp)) throw new BusinessRuleError(FormSchemaErrorCode.FsInvalidSchemaStructure, `Field "${code}" of type REFERENCE_TABLE must have "referenceTable.relatedApp"`);
8731
- if (!isRecord(refTable.condition)) throw new BusinessRuleError(FormSchemaErrorCode.FsInvalidSchemaStructure, `Field "${code}" of type REFERENCE_TABLE must have "referenceTable.condition"`);
8732
- if (!Array.isArray(refTable.displayFields)) throw new BusinessRuleError(FormSchemaErrorCode.FsInvalidSchemaStructure, `Field "${code}" of type REFERENCE_TABLE must have "referenceTable.displayFields" array`);
8733
- const condition = refTable.condition;
8734
- const displayFields = refTable.displayFields.map((f) => FieldCode.create(f));
8735
- const relatedApp = refTable.relatedApp;
8736
- const conditionField = String(condition.field ?? "");
8737
- const conditionRelatedField = String(condition.relatedField ?? "");
8738
- if (conditionField.length === 0) throw new BusinessRuleError(FormSchemaErrorCode.FsInvalidSchemaStructure, `Field "${code}" of type REFERENCE_TABLE must have a non-empty "referenceTable.condition.field"`);
8739
- if (conditionRelatedField.length === 0) throw new BusinessRuleError(FormSchemaErrorCode.FsInvalidSchemaStructure, `Field "${code}" of type REFERENCE_TABLE must have a non-empty "referenceTable.condition.relatedField"`);
8740
- return {
8741
- ...base,
8742
- type: "REFERENCE_TABLE",
8743
- properties: { referenceTable: {
8744
- relatedApp: { app: String(relatedApp.app ?? "") },
8745
- condition: {
8746
- field: FieldCode.create(conditionField),
8747
- relatedField: FieldCode.create(conditionRelatedField)
8748
- },
8749
- ...refTable.filterCond !== void 0 ? { filterCond: String(refTable.filterCond) } : {},
8750
- displayFields,
8751
- ...refTable.sort !== void 0 ? { sort: String(refTable.sort) } : {},
8752
- ...refTable.size !== void 0 ? { size: String(refTable.size) } : {}
8753
- } }
8754
- };
8755
- }
8301
+ if (fieldType === "REFERENCE_TABLE") return parseReferenceTableField(raw, code, base);
8756
8302
  const properties = extractProperties(raw);
8757
8303
  validateFieldProperties(code, fieldType, properties);
8758
8304
  return buildFieldDefinition(base, fieldType, properties);
@@ -8828,6 +8374,68 @@ function mergeFieldMaps(target, source) {
8828
8374
  }
8829
8375
  return merged;
8830
8376
  }
8377
+ function parseGroupLayoutItem(raw) {
8378
+ const code = FieldCode.create(String(raw.code));
8379
+ const label = String(raw.label ?? "");
8380
+ const noLabel = typeof raw.noLabel === "boolean" ? raw.noLabel : void 0;
8381
+ const openGroup = typeof raw.openGroup === "boolean" ? raw.openGroup : void 0;
8382
+ const rawLayout = Array.isArray(raw.layout) ? raw.layout.filter(isRecord) : [];
8383
+ let groupFields = /* @__PURE__ */ new Map();
8384
+ const layout = [];
8385
+ for (const r of rawLayout) {
8386
+ const row = parseLayoutRow(r);
8387
+ const rowFields = collectFieldsFromElements(row.fields);
8388
+ groupFields = mergeFieldMaps(groupFields, rowFields);
8389
+ layout.push(row);
8390
+ }
8391
+ const groupDef = {
8392
+ code,
8393
+ label,
8394
+ ...noLabel !== void 0 ? { noLabel } : {},
8395
+ type: "GROUP",
8396
+ properties: { ...openGroup !== void 0 ? { openGroup } : {} }
8397
+ };
8398
+ checkDuplicateFieldCode(groupFields, code);
8399
+ groupFields.set(code, groupDef);
8400
+ return {
8401
+ item: {
8402
+ type: "GROUP",
8403
+ code,
8404
+ label,
8405
+ ...noLabel !== void 0 ? { noLabel } : {},
8406
+ ...openGroup !== void 0 ? { openGroup } : {},
8407
+ layout
8408
+ },
8409
+ fields: groupFields
8410
+ };
8411
+ }
8412
+ function parseSubtableLayoutItem(raw) {
8413
+ const code = FieldCode.create(String(raw.code));
8414
+ const label = String(raw.label ?? "");
8415
+ const noLabel = typeof raw.noLabel === "boolean" ? raw.noLabel : void 0;
8416
+ const elements = (Array.isArray(raw.fields) ? raw.fields : []).map(parseLayoutElement);
8417
+ const subFields = collectFieldsFromElements(elements);
8418
+ const subtableDef = {
8419
+ code,
8420
+ label,
8421
+ ...noLabel !== void 0 ? { noLabel } : {},
8422
+ type: "SUBTABLE",
8423
+ properties: { fields: subFields }
8424
+ };
8425
+ const allFields = new Map(subFields);
8426
+ checkDuplicateFieldCode(allFields, code);
8427
+ allFields.set(code, subtableDef);
8428
+ return {
8429
+ item: {
8430
+ type: "SUBTABLE",
8431
+ code,
8432
+ label,
8433
+ ...noLabel !== void 0 ? { noLabel } : {},
8434
+ fields: elements
8435
+ },
8436
+ fields: allFields
8437
+ };
8438
+ }
8831
8439
  function parseLayoutItem(raw) {
8832
8440
  const type = String(raw.type);
8833
8441
  switch (type) {
@@ -8838,68 +8446,8 @@ function parseLayoutItem(raw) {
8838
8446
  fields: collectFieldsFromElements(row.fields)
8839
8447
  };
8840
8448
  }
8841
- case "GROUP": {
8842
- const code = FieldCode.create(String(raw.code));
8843
- const label = String(raw.label ?? "");
8844
- const noLabel = typeof raw.noLabel === "boolean" ? raw.noLabel : void 0;
8845
- const openGroup = typeof raw.openGroup === "boolean" ? raw.openGroup : void 0;
8846
- const rawLayout = Array.isArray(raw.layout) ? raw.layout.filter(isRecord) : [];
8847
- let groupFields = /* @__PURE__ */ new Map();
8848
- const layout = [];
8849
- for (const r of rawLayout) {
8850
- const row = parseLayoutRow(r);
8851
- const rowFields = collectFieldsFromElements(row.fields);
8852
- groupFields = mergeFieldMaps(groupFields, rowFields);
8853
- layout.push(row);
8854
- }
8855
- const groupDef = {
8856
- code,
8857
- label,
8858
- ...noLabel !== void 0 ? { noLabel } : {},
8859
- type: "GROUP",
8860
- properties: { ...openGroup !== void 0 ? { openGroup } : {} }
8861
- };
8862
- checkDuplicateFieldCode(groupFields, code);
8863
- groupFields.set(code, groupDef);
8864
- return {
8865
- item: {
8866
- type: "GROUP",
8867
- code,
8868
- label,
8869
- ...noLabel !== void 0 ? { noLabel } : {},
8870
- ...openGroup !== void 0 ? { openGroup } : {},
8871
- layout
8872
- },
8873
- fields: groupFields
8874
- };
8875
- }
8876
- case "SUBTABLE": {
8877
- const code = FieldCode.create(String(raw.code));
8878
- const label = String(raw.label ?? "");
8879
- const noLabel = typeof raw.noLabel === "boolean" ? raw.noLabel : void 0;
8880
- const elements = (Array.isArray(raw.fields) ? raw.fields : []).map(parseLayoutElement);
8881
- const subFields = collectFieldsFromElements(elements);
8882
- const subtableDef = {
8883
- code,
8884
- label,
8885
- ...noLabel !== void 0 ? { noLabel } : {},
8886
- type: "SUBTABLE",
8887
- properties: { fields: subFields }
8888
- };
8889
- const allFields = new Map(subFields);
8890
- checkDuplicateFieldCode(allFields, code);
8891
- allFields.set(code, subtableDef);
8892
- return {
8893
- item: {
8894
- type: "SUBTABLE",
8895
- code,
8896
- label,
8897
- ...noLabel !== void 0 ? { noLabel } : {},
8898
- fields: elements
8899
- },
8900
- fields: allFields
8901
- };
8902
- }
8449
+ case "GROUP": return parseGroupLayoutItem(raw);
8450
+ case "SUBTABLE": return parseSubtableLayoutItem(raw);
8903
8451
  case "REFERENCE_TABLE": {
8904
8452
  const code = FieldCode.create(String(raw.code));
8905
8453
  const label = String(raw.label ?? "");
@@ -8920,14 +8468,7 @@ function parseLayoutItem(raw) {
8920
8468
  default: throw new BusinessRuleError(FormSchemaErrorCode.FsInvalidLayoutStructure, `Unknown layout item type: "${type}"`);
8921
8469
  }
8922
8470
  }
8923
- const SchemaParser = { parse: (rawText) => {
8924
- if (rawText.trim().length === 0) throw new BusinessRuleError(FormSchemaErrorCode.FsEmptySchemaText, "Schema text cannot be empty");
8925
- let parsed;
8926
- try {
8927
- parsed = parse(rawText);
8928
- } catch {
8929
- throw new BusinessRuleError(FormSchemaErrorCode.FsInvalidSchemaFormat, "Schema text is not valid YAML/JSON");
8930
- }
8471
+ const SchemaParser = { parse: (parsed) => {
8931
8472
  if (!isRecord(parsed)) throw new BusinessRuleError(FormSchemaErrorCode.FsInvalidSchemaStructure, "Schema must be an object");
8932
8473
  const obj = parsed;
8933
8474
  if ("fields" in obj && !("layout" in obj)) throw new BusinessRuleError(FormSchemaErrorCode.FsInvalidSchemaStructure, "\"fields\" key detected. Schema format has changed. Please use \"capture\" to generate a new format schema.");
@@ -8946,8 +8487,9 @@ const SchemaParser = { parse: (rawText) => {
8946
8487
  } };
8947
8488
  //#endregion
8948
8489
  //#region src/core/application/formSchema/parseSchema.ts
8949
- function parseSchemaText(rawText) {
8950
- return wrapBusinessRuleError(() => SchemaParser.parse(rawText));
8490
+ function parseSchemaText(codec, rawText) {
8491
+ const parsed = parseConfigText(codec, rawText, "Schema");
8492
+ return wrapBusinessRuleError(() => SchemaParser.parse(parsed));
8951
8493
  }
8952
8494
  //#endregion
8953
8495
  //#region src/core/application/formSchema/detectDiff.ts
@@ -8975,7 +8517,7 @@ function toFieldDto(field) {
8975
8517
  async function detectDiff({ container }) {
8976
8518
  const result = await container.schemaStorage.get();
8977
8519
  if (!result.exists) throw new ValidationError(ValidationErrorCode.InvalidInput, "Schema file not found");
8978
- const schema = parseSchemaText(result.content);
8520
+ const schema = parseSchemaText(container.configCodec, result.content);
8979
8521
  const [currentFields, currentLayout] = await Promise.all([container.formConfigurator.getFields(), container.formConfigurator.getLayout()]);
8980
8522
  const diff = DiffDetector.detect(schema, currentFields);
8981
8523
  const enrichedCurrentLayout = enrichLayoutWithFields(currentLayout, currentFields);
@@ -9340,10 +8882,22 @@ function assertSchemaValid(schema) {
9340
8882
  }
9341
8883
  //#endregion
9342
8884
  //#region src/core/application/formSchema/executeMigration.ts
8885
+ function processModifiedEntry(after, before, fieldsToUpdate, innerFieldsToDelete) {
8886
+ if (after.type === "SUBTABLE" && before !== void 0 && before.type === "SUBTABLE") {
8887
+ const { newInnerFields, existingInnerFields, deletedInnerFieldCodes } = splitSubtableInnerFields(after, before);
8888
+ if (newInnerFields.size > 0) throw new ValidationError(ValidationErrorCode.InvalidInput, `kintone REST API does not support adding fields to an existing subtable. Use the schema override command instead. Subtable: ${after.code}`);
8889
+ if (existingInnerFields.size > 0) fieldsToUpdate.push({
8890
+ ...after,
8891
+ properties: { fields: existingInnerFields }
8892
+ });
8893
+ for (const code of deletedInnerFieldCodes) innerFieldsToDelete.push(code);
8894
+ } else if (before !== void 0 && before.type !== after.type) throw new ValidationError(ValidationErrorCode.InvalidInput, `Field type change detected for "${after.code}" (${before.type} → ${after.type}). Use the schema override command instead.`);
8895
+ else fieldsToUpdate.push(after);
8896
+ }
9343
8897
  async function executeMigration({ container }) {
9344
8898
  const result = await container.schemaStorage.get();
9345
8899
  if (!result.exists) throw new ValidationError(ValidationErrorCode.InvalidInput, "Schema file not found");
9346
- const schema = parseSchemaText(result.content);
8900
+ const schema = parseSchemaText(container.configCodec, result.content);
9347
8901
  assertSchemaValid(schema);
9348
8902
  const [currentFields, currentLayout] = await Promise.all([container.formConfigurator.getFields(), container.formConfigurator.getLayout()]);
9349
8903
  const diff = DiffDetector.detect(schema, currentFields);
@@ -9365,18 +8919,7 @@ async function executeMigration({ container }) {
9365
8919
  for (const entry of modified) {
9366
8920
  if (entry.after === void 0) continue;
9367
8921
  if (subtableInnerCodes.has(entry.fieldCode)) continue;
9368
- const after = entry.after;
9369
- const before = entry.before;
9370
- if (after.type === "SUBTABLE" && before !== void 0 && before.type === "SUBTABLE") {
9371
- const { newInnerFields, existingInnerFields, deletedInnerFieldCodes } = splitSubtableInnerFields(after, before);
9372
- if (newInnerFields.size > 0) throw new ValidationError(ValidationErrorCode.InvalidInput, `kintone REST API does not support adding fields to an existing subtable. Use the schema override command instead. Subtable: ${after.code}`);
9373
- if (existingInnerFields.size > 0) fieldsToUpdate.push({
9374
- ...after,
9375
- properties: { fields: existingInnerFields }
9376
- });
9377
- for (const code of deletedInnerFieldCodes) innerFieldsToDelete.push(code);
9378
- } else if (before !== void 0 && before.type !== after.type) throw new ValidationError(ValidationErrorCode.InvalidInput, `Field type change detected for "${after.code}" (${before.type} → ${after.type}). Use the schema override command instead.`);
9379
- else fieldsToUpdate.push(after);
8922
+ processModifiedEntry(entry.after, entry.before, fieldsToUpdate, innerFieldsToDelete);
9380
8923
  }
9381
8924
  if (fieldsToAdd.length > 0) await container.formConfigurator.addFields(fieldsToAdd);
9382
8925
  if (fieldsToUpdate.length > 0) await container.formConfigurator.updateFields(fieldsToUpdate);
@@ -9485,55 +9028,62 @@ var migrate_default = define({
9485
9028
  });
9486
9029
  //#endregion
9487
9030
  //#region src/core/application/formSchema/forceOverrideForm.ts
9031
+ function classifySubtableField(fieldCode, schemaDef, currentFields, result) {
9032
+ const currentDef = currentFields.get(fieldCode);
9033
+ if (currentDef !== void 0 && currentDef.type === "SUBTABLE") {
9034
+ const { newInnerFields, existingInnerFields, deletedInnerFieldCodes } = splitSubtableInnerFields(schemaDef, currentDef);
9035
+ const allInnerFieldsRemoved = existingInnerFields.size === 0 && deletedInnerFieldCodes.length > 0;
9036
+ if (newInnerFields.size > 0 || allInnerFieldsRemoved) {
9037
+ result.toDelete.push(fieldCode);
9038
+ result.toAdd.push(schemaDef);
9039
+ } else {
9040
+ result.toUpdate.push({
9041
+ ...schemaDef,
9042
+ properties: { fields: existingInnerFields }
9043
+ });
9044
+ for (const code of deletedInnerFieldCodes) result.innerFieldsToDelete.push(code);
9045
+ }
9046
+ } else {
9047
+ result.toDelete.push(fieldCode);
9048
+ result.toAdd.push(schemaDef);
9049
+ }
9050
+ }
9051
+ function classifyExistingField(fieldCode, schemaDef, currentFields, result) {
9052
+ if (schemaDef.type === "SUBTABLE") classifySubtableField(fieldCode, schemaDef, currentFields, result);
9053
+ else {
9054
+ const currentDef = currentFields.get(fieldCode);
9055
+ if (currentDef !== void 0 && currentDef.type !== schemaDef.type) {
9056
+ result.toDelete.push(fieldCode);
9057
+ result.toAdd.push(schemaDef);
9058
+ } else result.toUpdate.push(schemaDef);
9059
+ }
9060
+ }
9488
9061
  async function forceOverrideForm({ container }) {
9489
9062
  const result = await container.schemaStorage.get();
9490
9063
  if (!result.exists) throw new ValidationError(ValidationErrorCode.InvalidInput, "Schema file not found");
9491
- const schema = parseSchemaText(result.content);
9064
+ const schema = parseSchemaText(container.configCodec, result.content);
9492
9065
  assertSchemaValid(schema);
9493
9066
  const currentFields = await container.formConfigurator.getFields();
9494
9067
  const subtableInnerCodes = collectSubtableInnerFieldCodes(schema.fields);
9495
- const toAdd = [];
9496
- const toUpdate = [];
9497
- const toDelete = [];
9498
- const innerFieldsToDelete = [];
9068
+ const classification = {
9069
+ toAdd: [],
9070
+ toUpdate: [],
9071
+ toDelete: [],
9072
+ innerFieldsToDelete: []
9073
+ };
9499
9074
  for (const [fieldCode, schemaDef] of schema.fields) {
9500
9075
  if (subtableInnerCodes.has(fieldCode)) continue;
9501
- if (currentFields.has(fieldCode)) if (schemaDef.type === "SUBTABLE") {
9502
- const currentDef = currentFields.get(fieldCode);
9503
- if (currentDef !== void 0 && currentDef.type === "SUBTABLE") {
9504
- const { newInnerFields, existingInnerFields, deletedInnerFieldCodes } = splitSubtableInnerFields(schemaDef, currentDef);
9505
- const allInnerFieldsRemoved = existingInnerFields.size === 0 && deletedInnerFieldCodes.length > 0;
9506
- if (newInnerFields.size > 0 || allInnerFieldsRemoved) {
9507
- toDelete.push(fieldCode);
9508
- toAdd.push(schemaDef);
9509
- } else {
9510
- toUpdate.push({
9511
- ...schemaDef,
9512
- properties: { fields: existingInnerFields }
9513
- });
9514
- for (const code of deletedInnerFieldCodes) innerFieldsToDelete.push(code);
9515
- }
9516
- } else {
9517
- toDelete.push(fieldCode);
9518
- toAdd.push(schemaDef);
9519
- }
9520
- } else {
9521
- const currentDef = currentFields.get(fieldCode);
9522
- if (currentDef !== void 0 && currentDef.type !== schemaDef.type) {
9523
- toDelete.push(fieldCode);
9524
- toAdd.push(schemaDef);
9525
- } else toUpdate.push(schemaDef);
9526
- }
9527
- else toAdd.push(schemaDef);
9076
+ if (currentFields.has(fieldCode)) classifyExistingField(fieldCode, schemaDef, currentFields, classification);
9077
+ else classification.toAdd.push(schemaDef);
9528
9078
  }
9529
9079
  const currentSubtableInnerCodes = collectSubtableInnerFieldCodes(currentFields);
9530
9080
  for (const fieldCode of currentFields.keys()) {
9531
9081
  if (currentSubtableInnerCodes.has(fieldCode)) continue;
9532
- if (!schema.fields.has(fieldCode)) toDelete.push(fieldCode);
9082
+ if (!schema.fields.has(fieldCode)) classification.toDelete.push(fieldCode);
9533
9083
  }
9534
- if (toDelete.length > 0 || innerFieldsToDelete.length > 0) await container.formConfigurator.deleteFields([...toDelete, ...innerFieldsToDelete]);
9535
- if (toAdd.length > 0) await container.formConfigurator.addFields(toAdd);
9536
- if (toUpdate.length > 0) await container.formConfigurator.updateFields(toUpdate);
9084
+ if (classification.toDelete.length > 0 || classification.innerFieldsToDelete.length > 0) await container.formConfigurator.deleteFields([...classification.toDelete, ...classification.innerFieldsToDelete]);
9085
+ if (classification.toAdd.length > 0) await container.formConfigurator.addFields(classification.toAdd);
9086
+ if (classification.toUpdate.length > 0) await container.formConfigurator.updateFields(classification.toUpdate);
9537
9087
  await container.formConfigurator.updateLayout(schema.layout);
9538
9088
  }
9539
9089
  //#endregion
@@ -9667,7 +9217,10 @@ var override_default = define({
9667
9217
  //#endregion
9668
9218
  //#region src/core/application/container/validateCli.ts
9669
9219
  function createValidateCliContainer(config) {
9670
- return { schemaStorage: createLocalFileSchemaStorage(config.schemaFilePath) };
9220
+ return {
9221
+ configCodec,
9222
+ schemaStorage: createLocalFileSchemaStorage(config.schemaFilePath)
9223
+ };
9671
9224
  }
9672
9225
  //#endregion
9673
9226
  //#region src/core/application/formSchema/validateSchema.ts
@@ -9676,7 +9229,7 @@ async function validateSchema({ container }) {
9676
9229
  if (!result.exists) throw new ValidationError(ValidationErrorCode.InvalidInput, "Schema file not found");
9677
9230
  let schema;
9678
9231
  try {
9679
- schema = parseSchemaText(result.content);
9232
+ schema = parseSchemaText(container.configCodec, result.content);
9680
9233
  } catch (error) {
9681
9234
  if (isValidationError(error)) return {
9682
9235
  parseError: error.message,
@@ -9854,14 +9407,7 @@ function parseRecord(raw, index) {
9854
9407
  for (const [key, value] of Object.entries(raw)) record[key] = normalizeValue(value);
9855
9408
  return record;
9856
9409
  }
9857
- const SeedParser = { parse: (rawText) => {
9858
- if (rawText.trim().length === 0) throw new BusinessRuleError(SeedDataErrorCode.SdEmptySeedText, "Seed text cannot be empty");
9859
- let parsed;
9860
- try {
9861
- parsed = parse(rawText);
9862
- } catch {
9863
- throw new BusinessRuleError(SeedDataErrorCode.SdInvalidSeedYaml, "Seed text is not valid YAML");
9864
- }
9410
+ const SeedParser = { parse: (parsed) => {
9865
9411
  if (!isRecord(parsed)) throw new BusinessRuleError(SeedDataErrorCode.SdInvalidSeedStructure, "Seed data must be an object");
9866
9412
  const obj = parsed;
9867
9413
  const key = "key" in obj && typeof obj.key === "string" ? UpsertKey.create(obj.key) : null;
@@ -9887,15 +9433,16 @@ const SeedParser = { parse: (rawText) => {
9887
9433
  } };
9888
9434
  //#endregion
9889
9435
  //#region src/core/application/seedData/parseConfig.ts
9890
- function parseSeedText(rawText) {
9891
- return wrapBusinessRuleError(() => SeedParser.parse(rawText));
9436
+ function parseSeedText(codec, rawText) {
9437
+ const parsed = parseConfigText(codec, rawText, "Seed");
9438
+ return wrapBusinessRuleError(() => SeedParser.parse(parsed));
9892
9439
  }
9893
9440
  //#endregion
9894
9441
  //#region src/core/application/seedData/upsertSeed.ts
9895
9442
  async function upsertSeed({ container, input }) {
9896
9443
  const result = await container.seedStorage.get();
9897
9444
  if (!result.exists) throw new ValidationError(ValidationErrorCode.InvalidInput, "Seed file not found");
9898
- const seedData = parseSeedText(result.content);
9445
+ const seedData = parseSeedText(container.configCodec, result.content);
9899
9446
  if (input.clean) {
9900
9447
  const { deletedCount } = await container.recordManager.deleteAllRecords();
9901
9448
  if (seedData.records.length > 0) await container.recordManager.addRecords(seedData.records);
@@ -10153,41 +9700,33 @@ function parseNumberPrecision(raw) {
10153
9700
  roundingMode: parseEnum(raw.roundingMode, VALID_ROUNDING_MODES, GeneralSettingsErrorCode.GsInvalidConfigStructure, `numberPrecision.roundingMode must be HALF_EVEN, UP, or DOWN, got: ${String(raw.roundingMode)}`)
10154
9701
  };
10155
9702
  }
10156
- const GeneralSettingsConfigParser = { parse: (rawText) => {
10157
- const parsed = parseYamlConfig(rawText, {
10158
- emptyConfigText: GeneralSettingsErrorCode.GsEmptyConfigText,
10159
- invalidConfigYaml: GeneralSettingsErrorCode.GsInvalidConfigYaml,
10160
- invalidConfigStructure: GeneralSettingsErrorCode.GsInvalidConfigStructure
10161
- }, "General settings");
10162
- let name;
10163
- if (parsed.name !== void 0 && parsed.name !== null) {
10164
- if (typeof parsed.name !== "string") throw new BusinessRuleError(GeneralSettingsErrorCode.GsInvalidConfigStructure, "name must be a string");
10165
- name = parsed.name;
10166
- }
10167
- let description;
10168
- if (parsed.description !== void 0 && parsed.description !== null) {
10169
- if (typeof parsed.description !== "string") throw new BusinessRuleError(GeneralSettingsErrorCode.GsInvalidConfigStructure, "description must be a string");
10170
- description = parsed.description;
10171
- }
10172
- let icon;
10173
- if (parsed.icon !== void 0 && parsed.icon !== null) icon = parseIcon(parsed.icon);
10174
- let theme;
10175
- if (parsed.theme !== void 0 && parsed.theme !== null) theme = parseEnum(parsed.theme, VALID_THEMES, GeneralSettingsErrorCode.GsInvalidTheme, `theme must be WHITE, RED, GREEN, BLUE, YELLOW, BLACK, CLIPBOARD, BINDER, PENCIL, or CLIPS, got: ${String(parsed.theme)}`);
10176
- let titleField;
10177
- if (parsed.titleField !== void 0 && parsed.titleField !== null) titleField = parseTitleField(parsed.titleField);
10178
- const enableThumbnails = parseOptionalBoolean(parsed, "enableThumbnails");
10179
- const enableBulkDeletion = parseOptionalBoolean(parsed, "enableBulkDeletion");
10180
- const enableComments = parseOptionalBoolean(parsed, "enableComments");
10181
- const enableDuplicateRecord = parseOptionalBoolean(parsed, "enableDuplicateRecord");
10182
- const enableInlineRecordEditing = parseOptionalBoolean(parsed, "enableInlineRecordEditing");
10183
- let numberPrecision;
10184
- if (parsed.numberPrecision !== void 0 && parsed.numberPrecision !== null) numberPrecision = parseNumberPrecision(parsed.numberPrecision);
10185
- let firstMonthOfFiscalYear;
10186
- if (parsed.firstMonthOfFiscalYear !== void 0 && parsed.firstMonthOfFiscalYear !== null) {
10187
- if (typeof parsed.firstMonthOfFiscalYear !== "number") throw new BusinessRuleError(GeneralSettingsErrorCode.GsInvalidConfigStructure, "firstMonthOfFiscalYear must be a number");
10188
- if (parsed.firstMonthOfFiscalYear < 1 || parsed.firstMonthOfFiscalYear > 12 || !Number.isInteger(parsed.firstMonthOfFiscalYear)) throw new BusinessRuleError(GeneralSettingsErrorCode.GsInvalidConfigStructure, `firstMonthOfFiscalYear must be an integer between 1 and 12, got: ${parsed.firstMonthOfFiscalYear}`);
10189
- firstMonthOfFiscalYear = parsed.firstMonthOfFiscalYear;
10190
- }
9703
+ function parseOptionalString(obj, fieldName) {
9704
+ const value = obj[fieldName];
9705
+ if (value === void 0 || value === null) return void 0;
9706
+ if (typeof value !== "string") throw new BusinessRuleError(GeneralSettingsErrorCode.GsInvalidConfigStructure, `${fieldName} must be a string`);
9707
+ return value;
9708
+ }
9709
+ function parseFirstMonthOfFiscalYear(obj) {
9710
+ const value = obj.firstMonthOfFiscalYear;
9711
+ if (value === void 0 || value === null) return void 0;
9712
+ if (typeof value !== "number") throw new BusinessRuleError(GeneralSettingsErrorCode.GsInvalidConfigStructure, "firstMonthOfFiscalYear must be a number");
9713
+ if (value < 1 || value > 12 || !Number.isInteger(value)) throw new BusinessRuleError(GeneralSettingsErrorCode.GsInvalidConfigStructure, `firstMonthOfFiscalYear must be an integer between 1 and 12, got: ${value}`);
9714
+ return value;
9715
+ }
9716
+ const GeneralSettingsConfigParser = { parse: (parsed) => {
9717
+ const obj = validateParsedConfig(parsed, GeneralSettingsErrorCode.GsInvalidConfigStructure, "General settings");
9718
+ const name = parseOptionalString(obj, "name");
9719
+ const description = parseOptionalString(obj, "description");
9720
+ const icon = obj.icon !== void 0 && obj.icon !== null ? parseIcon(obj.icon) : void 0;
9721
+ const theme = obj.theme !== void 0 && obj.theme !== null ? parseEnum(obj.theme, VALID_THEMES, GeneralSettingsErrorCode.GsInvalidTheme, `theme must be WHITE, RED, GREEN, BLUE, YELLOW, BLACK, CLIPBOARD, BINDER, PENCIL, or CLIPS, got: ${String(obj.theme)}`) : void 0;
9722
+ const titleField = obj.titleField !== void 0 && obj.titleField !== null ? parseTitleField(obj.titleField) : void 0;
9723
+ const enableThumbnails = parseOptionalBoolean(obj, "enableThumbnails");
9724
+ const enableBulkDeletion = parseOptionalBoolean(obj, "enableBulkDeletion");
9725
+ const enableComments = parseOptionalBoolean(obj, "enableComments");
9726
+ const enableDuplicateRecord = parseOptionalBoolean(obj, "enableDuplicateRecord");
9727
+ const enableInlineRecordEditing = parseOptionalBoolean(obj, "enableInlineRecordEditing");
9728
+ const numberPrecision = obj.numberPrecision !== void 0 && obj.numberPrecision !== null ? parseNumberPrecision(obj.numberPrecision) : void 0;
9729
+ const firstMonthOfFiscalYear = parseFirstMonthOfFiscalYear(obj);
10191
9730
  return {
10192
9731
  ...name !== void 0 ? { name } : {},
10193
9732
  ...description !== void 0 ? { description } : {},
@@ -10205,15 +9744,16 @@ const GeneralSettingsConfigParser = { parse: (rawText) => {
10205
9744
  } };
10206
9745
  //#endregion
10207
9746
  //#region src/core/application/generalSettings/parseConfig.ts
10208
- function parseGeneralSettingsConfigText(rawText) {
10209
- return wrapBusinessRuleError(() => GeneralSettingsConfigParser.parse(rawText));
9747
+ function parseGeneralSettingsConfigText(codec, rawText) {
9748
+ const parsed = parseConfigText(codec, rawText, "General settings");
9749
+ return wrapBusinessRuleError(() => GeneralSettingsConfigParser.parse(parsed));
10210
9750
  }
10211
9751
  //#endregion
10212
9752
  //#region src/core/application/generalSettings/applyGeneralSettings.ts
10213
9753
  async function applyGeneralSettings({ container }) {
10214
9754
  await applyFromConfig({
10215
9755
  getStorage: () => container.generalSettingsStorage.get(),
10216
- parseConfig: parseGeneralSettingsConfigText,
9756
+ parseConfig: (content) => parseGeneralSettingsConfigText(container.configCodec, content),
10217
9757
  fetchRemote: () => container.generalSettingsConfigurator.getGeneralSettings(),
10218
9758
  update: async (config, current) => {
10219
9759
  await container.generalSettingsConfigurator.updateGeneralSettings({
@@ -10247,91 +9787,32 @@ const { resolveFilePath: resolveSettingsFilePath, resolveContainerConfig: resolv
10247
9787
  });
10248
9788
  //#endregion
10249
9789
  //#region src/cli/commands/settings/apply.ts
10250
- async function runSettings(config) {
10251
- const container = createGeneralSettingsCliContainer(config);
10252
- const s = p.spinner();
10253
- s.start("Applying general settings...");
10254
- await applyGeneralSettings({ container });
10255
- s.stop("General settings applied.");
10256
- p.log.success("General settings applied successfully.");
10257
- return container;
10258
- }
10259
- var apply_default$1 = define({
10260
- name: "apply",
9790
+ var apply_default$1 = createApplyCommand({
10261
9791
  description: "Apply general settings from YAML to kintone app",
10262
- args: {
10263
- ...settingsArgs,
10264
- ...confirmArgs
10265
- },
10266
- run: async (ctx) => {
10267
- try {
10268
- const values = ctx.values;
10269
- const skipConfirm = values.yes === true;
10270
- await routeMultiApp(values, {
10271
- singleLegacy: async () => {
10272
- await confirmAndDeploy([await runSettings(resolveSettingsContainerConfig(values))], skipConfirm);
10273
- },
10274
- singleApp: async (app, projectConfig) => {
10275
- await confirmAndDeploy([await runSettings(resolveSettingsAppContainerConfig(app, projectConfig, values))], skipConfirm);
10276
- },
10277
- multiApp: async (plan, projectConfig) => {
10278
- const containers = [];
10279
- await runMultiAppWithHeaders(plan, async (app) => {
10280
- const container = await runSettings(resolveSettingsAppContainerConfig(app, projectConfig, values));
10281
- containers.push({
10282
- appDeployer: container.appDeployer,
10283
- appName: app.name
10284
- });
10285
- });
10286
- await confirmAndDeploy(containers, skipConfirm);
10287
- }
10288
- });
10289
- } catch (error) {
10290
- handleCliError(error);
10291
- }
10292
- }
9792
+ args: settingsArgs,
9793
+ spinnerMessage: "Applying general settings...",
9794
+ spinnerStopMessage: "General settings applied.",
9795
+ successMessage: "General settings applied successfully.",
9796
+ createContainer: createGeneralSettingsCliContainer,
9797
+ applyFn: applyGeneralSettings,
9798
+ resolveContainerConfig: resolveSettingsContainerConfig,
9799
+ resolveAppContainerConfig: resolveSettingsAppContainerConfig
10293
9800
  });
10294
9801
  //#endregion
10295
9802
  //#region src/cli/commands/settings/capture.ts
10296
- async function runCaptureSettings(config) {
10297
- const container = createGeneralSettingsCliContainer(config);
10298
- const s = p.spinner();
10299
- s.start("Capturing general settings...");
10300
- const result = await captureGeneralSettings({ container });
10301
- s.stop("General settings captured.");
10302
- await saveGeneralSettings({
10303
- container,
10304
- input: { configText: result.configText }
10305
- });
10306
- p.log.success(`General settings saved to: ${pc.cyan(config.settingsFilePath)}`);
10307
- if (result.hasExistingConfig) p.log.warn("Existing general settings file was overwritten.");
10308
- }
10309
- var capture_default$1 = define({
10310
- name: "capture",
9803
+ var capture_default$1 = createCaptureCommand({
10311
9804
  description: "Capture current general settings from kintone app to file",
10312
9805
  args: settingsArgs,
10313
- run: async (ctx) => {
10314
- try {
10315
- const values = ctx.values;
10316
- await routeMultiApp(values, {
10317
- singleLegacy: async () => {
10318
- await runCaptureSettings(resolveSettingsContainerConfig(values));
10319
- },
10320
- singleApp: async (app, projectConfig) => {
10321
- await runCaptureSettings(resolveSettingsAppContainerConfig(app, projectConfig, values));
10322
- },
10323
- multiApp: async (plan, projectConfig) => {
10324
- await runMultiAppWithFailCheck(plan, async (app) => {
10325
- const config = resolveSettingsAppContainerConfig(app, projectConfig, values);
10326
- printAppHeader(app.name, app.appId);
10327
- await runCaptureSettings(config);
10328
- }, "All general settings captures completed successfully.");
10329
- }
10330
- });
10331
- } catch (error) {
10332
- handleCliError(error);
10333
- }
10334
- }
9806
+ spinnerMessage: "Capturing general settings...",
9807
+ spinnerStopMessage: "General settings captured.",
9808
+ domainLabel: "General settings",
9809
+ multiAppSuccessMessage: "All general settings captures completed successfully.",
9810
+ createContainer: createGeneralSettingsCliContainer,
9811
+ captureFn: captureGeneralSettings,
9812
+ saveFn: saveGeneralSettings,
9813
+ getConfigFilePath: (config) => config.settingsFilePath,
9814
+ resolveContainerConfig: resolveSettingsContainerConfig,
9815
+ resolveAppContainerConfig: resolveSettingsAppContainerConfig
10335
9816
  });
10336
9817
  //#endregion
10337
9818
  //#region src/core/domain/generalSettings/services/diffDetector.ts
@@ -10400,7 +9881,7 @@ async function detectGeneralSettingsDiff({ container }) {
10400
9881
  return detectDiffFromConfig({
10401
9882
  getStorage: () => container.generalSettingsStorage.get(),
10402
9883
  fetchRemote: () => container.generalSettingsConfigurator.getGeneralSettings(),
10403
- parseConfig: parseGeneralSettingsConfigText,
9884
+ parseConfig: (content) => parseGeneralSettingsConfigText(container.configCodec, content),
10404
9885
  detect: (local, remote) => GeneralSettingsDiffDetector.detect(local, remote.config),
10405
9886
  notFoundMessage: "General settings config file not found"
10406
9887
  });
@@ -10460,12 +9941,8 @@ function parseViewConfig(name, raw) {
10460
9941
  ...raw.sort !== void 0 && { sort: String(raw.sort) }
10461
9942
  };
10462
9943
  }
10463
- const ViewConfigParser = { parse: (rawText) => {
10464
- const obj = parseYamlConfig(rawText, {
10465
- emptyConfigText: ViewErrorCode.VwEmptyConfigText,
10466
- invalidConfigYaml: ViewErrorCode.VwInvalidConfigYaml,
10467
- invalidConfigStructure: ViewErrorCode.VwInvalidConfigStructure
10468
- }, "View");
9944
+ const ViewConfigParser = { parse: (parsed) => {
9945
+ const obj = validateParsedConfig(parsed, ViewErrorCode.VwInvalidConfigStructure, "View");
10469
9946
  if (!isRecord(obj.views)) throw new BusinessRuleError(ViewErrorCode.VwInvalidConfigStructure, "Config must have a \"views\" object");
10470
9947
  const viewsObj = obj.views;
10471
9948
  const views = {};
@@ -10477,15 +9954,16 @@ const ViewConfigParser = { parse: (rawText) => {
10477
9954
  } };
10478
9955
  //#endregion
10479
9956
  //#region src/core/application/view/parseConfig.ts
10480
- function parseViewConfigText(rawText) {
10481
- return wrapBusinessRuleError(() => ViewConfigParser.parse(rawText));
9957
+ function parseViewConfigText(codec, rawText) {
9958
+ const parsed = parseConfigText(codec, rawText, "View");
9959
+ return wrapBusinessRuleError(() => ViewConfigParser.parse(parsed));
10482
9960
  }
10483
9961
  //#endregion
10484
9962
  //#region src/core/application/view/applyView.ts
10485
9963
  async function applyView({ container }) {
10486
9964
  const result = await container.viewStorage.get();
10487
9965
  if (!result.exists) throw new ValidationError(ValidationErrorCode.InvalidInput, "View config file not found");
10488
- const config = parseViewConfigText(result.content);
9966
+ const config = parseViewConfigText(container.configCodec, result.content);
10489
9967
  const skippedBuiltinViews = [];
10490
9968
  const filteredViews = {};
10491
9969
  for (const [name, view] of Object.entries(config.views)) if (view.builtinType !== void 0) skippedBuiltinViews.push(name);
@@ -10521,107 +9999,53 @@ const { resolveFilePath: resolveViewFilePath, resolveContainerConfig: resolveVie
10521
9999
  });
10522
10000
  //#endregion
10523
10001
  //#region src/cli/commands/view/apply.ts
10524
- async function runView(config) {
10525
- const container = createViewCliContainer(config);
10526
- const s = p.spinner();
10527
- s.start("Applying views...");
10528
- const result = await applyView({ container });
10529
- s.stop("Views applied.");
10530
- if (result.skippedBuiltinViews.length > 0) p.log.warn(`Skipped built-in views: ${result.skippedBuiltinViews.join(", ")}`);
10531
- p.log.success("Views applied successfully.");
10532
- return container;
10533
- }
10534
- var apply_default = define({
10535
- name: "apply",
10002
+ var apply_default = createApplyCommand({
10536
10003
  description: "Apply view settings from YAML to kintone app",
10537
- args: {
10538
- ...viewArgs,
10539
- ...confirmArgs
10004
+ args: viewArgs,
10005
+ spinnerMessage: "Applying views...",
10006
+ spinnerStopMessage: "Views applied.",
10007
+ successMessage: "Views applied successfully.",
10008
+ createContainer: createViewCliContainer,
10009
+ applyFn: applyView,
10010
+ onResult: (result) => {
10011
+ if (result.skippedBuiltinViews.length > 0) p.log.warn(`Skipped built-in views: ${result.skippedBuiltinViews.join(", ")}`);
10540
10012
  },
10541
- run: async (ctx) => {
10542
- try {
10543
- const values = ctx.values;
10544
- const skipConfirm = values.yes === true;
10545
- await routeMultiApp(values, {
10546
- singleLegacy: async () => {
10547
- await confirmAndDeploy([await runView(resolveViewContainerConfig(values))], skipConfirm);
10548
- },
10549
- singleApp: async (app, projectConfig) => {
10550
- await confirmAndDeploy([await runView(resolveViewAppContainerConfig(app, projectConfig, values))], skipConfirm);
10551
- },
10552
- multiApp: async (plan, projectConfig) => {
10553
- const containers = [];
10554
- await runMultiAppWithHeaders(plan, async (app) => {
10555
- const container = await runView(resolveViewAppContainerConfig(app, projectConfig, values));
10556
- containers.push({
10557
- appDeployer: container.appDeployer,
10558
- appName: app.name
10559
- });
10560
- });
10561
- await confirmAndDeploy(containers, skipConfirm);
10562
- }
10563
- });
10564
- } catch (error) {
10565
- handleCliError(error);
10566
- }
10567
- }
10013
+ resolveContainerConfig: resolveViewContainerConfig,
10014
+ resolveAppContainerConfig: resolveViewAppContainerConfig
10568
10015
  });
10569
10016
  //#endregion
10570
10017
  //#region src/cli/commands/view/capture.ts
10571
- async function runCaptureView(config) {
10572
- const container = createViewCliContainer(config);
10573
- const s = p.spinner();
10574
- s.start("Capturing views...");
10575
- const result = await captureView({ container });
10576
- s.stop("Views captured.");
10577
- await saveView({
10578
- container,
10579
- input: { configText: result.configText }
10580
- });
10581
- p.log.success(`Views saved to: ${pc.cyan(config.viewFilePath)}`);
10582
- if (result.hasExistingConfig) p.log.warn("Existing view file was overwritten.");
10583
- }
10584
- var capture_default = define({
10585
- name: "capture",
10018
+ var capture_default = createCaptureCommand({
10586
10019
  description: "Capture current view settings from kintone app to file",
10587
10020
  args: viewArgs,
10588
- run: async (ctx) => {
10589
- try {
10590
- const values = ctx.values;
10591
- await routeMultiApp(values, {
10592
- singleLegacy: async () => {
10593
- await runCaptureView(resolveViewContainerConfig(values));
10594
- },
10595
- singleApp: async (app, projectConfig) => {
10596
- await runCaptureView(resolveViewAppContainerConfig(app, projectConfig, values));
10597
- },
10598
- multiApp: async (plan, projectConfig) => {
10599
- await runMultiAppWithFailCheck(plan, async (app) => {
10600
- const config = resolveViewAppContainerConfig(app, projectConfig, values);
10601
- printAppHeader(app.name, app.appId);
10602
- await runCaptureView(config);
10603
- }, "All view captures completed successfully.");
10604
- }
10605
- });
10606
- } catch (error) {
10607
- handleCliError(error);
10608
- }
10609
- }
10021
+ spinnerMessage: "Capturing views...",
10022
+ spinnerStopMessage: "Views captured.",
10023
+ domainLabel: "Views",
10024
+ multiAppSuccessMessage: "All view captures completed successfully.",
10025
+ createContainer: createViewCliContainer,
10026
+ captureFn: captureView,
10027
+ saveFn: saveView,
10028
+ getConfigFilePath: (config) => config.viewFilePath,
10029
+ resolveContainerConfig: resolveViewContainerConfig,
10030
+ resolveAppContainerConfig: resolveViewAppContainerConfig
10610
10031
  });
10611
10032
  //#endregion
10612
10033
  //#region src/core/domain/view/services/diffDetector.ts
10034
+ function checkOptionalStringChange(changes, field, localVal, remoteVal) {
10035
+ if ((localVal ?? "") !== (remoteVal ?? "")) changes.push(`${field} changed`);
10036
+ }
10613
10037
  function describeChanges(local, remote) {
10614
10038
  const changes = [];
10615
10039
  if (local.type !== remote.type) changes.push(`type: ${remote.type} -> ${local.type}`);
10616
- if ((local.builtinType ?? "") !== (remote.builtinType ?? "")) changes.push("builtinType changed");
10040
+ checkOptionalStringChange(changes, "builtinType", local.builtinType, remote.builtinType);
10617
10041
  if (local.index !== remote.index) changes.push(`index: ${remote.index} -> ${local.index}`);
10618
- if ((local.filterCond ?? "") !== (remote.filterCond ?? "")) changes.push("filterCond changed");
10619
- if ((local.sort ?? "") !== (remote.sort ?? "")) changes.push("sort changed");
10620
- if ((local.date ?? "") !== (remote.date ?? "")) changes.push("date changed");
10621
- if ((local.title ?? "") !== (remote.title ?? "")) changes.push("title changed");
10622
- if ((local.html ?? "") !== (remote.html ?? "")) changes.push("html changed");
10042
+ checkOptionalStringChange(changes, "filterCond", local.filterCond, remote.filterCond);
10043
+ checkOptionalStringChange(changes, "sort", local.sort, remote.sort);
10044
+ checkOptionalStringChange(changes, "date", local.date, remote.date);
10045
+ checkOptionalStringChange(changes, "title", local.title, remote.title);
10046
+ checkOptionalStringChange(changes, "html", local.html, remote.html);
10623
10047
  if ((local.pager ?? false) !== (remote.pager ?? false)) changes.push(`pager: ${String(remote.pager ?? false)} -> ${String(local.pager ?? false)}`);
10624
- if ((local.device ?? "") !== (remote.device ?? "")) changes.push("device changed");
10048
+ checkOptionalStringChange(changes, "device", local.device, remote.device);
10625
10049
  if (!deepEqual(local.fields ?? [], remote.fields ?? [])) changes.push("fields changed");
10626
10050
  return changes;
10627
10051
  }
@@ -10653,7 +10077,7 @@ async function detectViewDiff({ container }) {
10653
10077
  return detectDiffFromConfig({
10654
10078
  getStorage: () => container.viewStorage.get(),
10655
10079
  fetchRemote: () => container.viewConfigurator.getViews(),
10656
- parseConfig: (content) => parseViewConfigText(content).views,
10080
+ parseConfig: (content) => parseViewConfigText(container.configCodec, content).views,
10657
10081
  detect: (views, remote) => ViewDiffDetector.detect(views, remote.views),
10658
10082
  notFoundMessage: "View config file not found"
10659
10083
  });