kintone-migrator 0.24.1 → 0.24.3

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
@@ -25,7 +25,8 @@ const ActionErrorCode = {
25
25
  AcInvalidConfigStructure: "AC_INVALID_CONFIG_STRUCTURE",
26
26
  AcInvalidSrcType: "AC_INVALID_SRC_TYPE",
27
27
  AcInvalidEntityType: "AC_INVALID_ENTITY_TYPE",
28
- AcEmptyActionName: "AC_EMPTY_ACTION_NAME"
28
+ AcEmptyActionName: "AC_EMPTY_ACTION_NAME",
29
+ AcDuplicateIndex: "AC_DUPLICATE_INDEX"
29
30
  };
30
31
 
31
32
  //#endregion
@@ -67,6 +68,7 @@ const FieldPermissionErrorCode = {
67
68
  FpInvalidConfigStructure: "FP_INVALID_CONFIG_STRUCTURE",
68
69
  FpInvalidAccessibility: "FP_INVALID_ACCESSIBILITY",
69
70
  FpInvalidEntityType: "FP_INVALID_ENTITY_TYPE",
71
+ FpInvalidBooleanField: "FP_INVALID_BOOLEAN_FIELD",
70
72
  FpEmptyFieldCode: "FP_EMPTY_FIELD_CODE",
71
73
  FpEmptyEntityCode: "FP_EMPTY_ENTITY_CODE",
72
74
  FpDuplicateFieldCode: "FP_DUPLICATE_FIELD_CODE"
@@ -94,7 +96,9 @@ const GeneralSettingsErrorCode = {
94
96
  GsInvalidConfigYaml: "GS_INVALID_CONFIG_YAML",
95
97
  GsInvalidConfigStructure: "GS_INVALID_CONFIG_STRUCTURE",
96
98
  GsInvalidTheme: "GS_INVALID_THEME",
97
- GsInvalidIconType: "GS_INVALID_ICON_TYPE"
99
+ GsInvalidIconType: "GS_INVALID_ICON_TYPE",
100
+ GsInvalidBooleanField: "GS_INVALID_BOOLEAN_FIELD",
101
+ GsInvalidNumberPrecision: "GS_INVALID_NUMBER_PRECISION"
98
102
  };
99
103
 
100
104
  //#endregion
@@ -106,7 +110,9 @@ const NotificationErrorCode = {
106
110
  NtInvalidEntityType: "NT_INVALID_ENTITY_TYPE",
107
111
  NtEmptyEntityCode: "NT_EMPTY_ENTITY_CODE",
108
112
  NtMissingRequiredField: "NT_MISSING_REQUIRED_FIELD",
109
- NtConflictingTimingFields: "NT_CONFLICTING_TIMING_FIELDS"
113
+ NtConflictingTimingFields: "NT_CONFLICTING_TIMING_FIELDS",
114
+ NtInvalidHoursLater: "NT_INVALID_HOURS_LATER",
115
+ NtInvalidDaysLater: "NT_INVALID_DAYS_LATER"
110
116
  };
111
117
 
112
118
  //#endregion
@@ -115,7 +121,8 @@ const PluginErrorCode = {
115
121
  PlEmptyConfigText: "PL_EMPTY_CONFIG_TEXT",
116
122
  PlInvalidConfigYaml: "PL_INVALID_CONFIG_YAML",
117
123
  PlInvalidConfigStructure: "PL_INVALID_CONFIG_STRUCTURE",
118
- PlEmptyPluginId: "PL_EMPTY_PLUGIN_ID"
124
+ PlEmptyPluginId: "PL_EMPTY_PLUGIN_ID",
125
+ PlDuplicatePluginId: "PL_DUPLICATE_PLUGIN_ID"
119
126
  };
120
127
 
121
128
  //#endregion
@@ -126,7 +133,9 @@ const ProcessManagementErrorCode = {
126
133
  PmInvalidConfigStructure: "PM_INVALID_CONFIG_STRUCTURE",
127
134
  PmInvalidAssigneeType: "PM_INVALID_ASSIGNEE_TYPE",
128
135
  PmInvalidEntityType: "PM_INVALID_ENTITY_TYPE",
129
- PmInvalidActionReference: "PM_INVALID_ACTION_REFERENCE"
136
+ PmInvalidBooleanField: "PM_INVALID_BOOLEAN_FIELD",
137
+ PmInvalidActionReference: "PM_INVALID_ACTION_REFERENCE",
138
+ PmDuplicateActionName: "PM_DUPLICATE_ACTION_NAME"
130
139
  };
131
140
 
132
141
  //#endregion
@@ -150,7 +159,8 @@ const RecordPermissionErrorCode = {
150
159
  RpInvalidConfigStructure: "RP_INVALID_CONFIG_STRUCTURE",
151
160
  RpInvalidEntityType: "RP_INVALID_ENTITY_TYPE",
152
161
  RpEmptyEntityCode: "RP_EMPTY_ENTITY_CODE",
153
- RpInvalidPermissionValue: "RP_INVALID_PERMISSION_VALUE"
162
+ RpInvalidPermissionValue: "RP_INVALID_PERMISSION_VALUE",
163
+ RpDuplicateEntity: "RP_DUPLICATE_ENTITY"
154
164
  };
155
165
 
156
166
  //#endregion
@@ -176,6 +186,10 @@ const SeedDataErrorCode = {
176
186
  SdInvalidKeyFieldValue: "SD_INVALID_KEY_FIELD_VALUE"
177
187
  };
178
188
 
189
+ //#endregion
190
+ //#region src/core/domain/services/errorCode.ts
191
+ const DomainServiceErrorCode = { YamlSerializationFailed: "DS_YAML_SERIALIZATION_FAILED" };
192
+
179
193
  //#endregion
180
194
  //#region src/core/domain/view/errorCode.ts
181
195
  const ViewErrorCode = {
@@ -184,7 +198,8 @@ const ViewErrorCode = {
184
198
  VwInvalidConfigStructure: "VW_INVALID_CONFIG_STRUCTURE",
185
199
  VwInvalidViewType: "VW_INVALID_VIEW_TYPE",
186
200
  VwInvalidDeviceType: "VW_INVALID_DEVICE_TYPE",
187
- VwEmptyViewName: "VW_EMPTY_VIEW_NAME"
201
+ VwEmptyViewName: "VW_EMPTY_VIEW_NAME",
202
+ VwInvalidIndex: "VW_INVALID_INDEX"
188
203
  };
189
204
 
190
205
  //#endregion
@@ -204,6 +219,7 @@ const BusinessRuleErrorCode = {
204
219
  ...RecordPermissionErrorCode,
205
220
  ...ReportErrorCode,
206
221
  ...SeedDataErrorCode,
222
+ ...DomainServiceErrorCode,
207
223
  ...ViewErrorCode
208
224
  };
209
225
  /**
@@ -339,10 +355,6 @@ function isRecord(value) {
339
355
  //#endregion
340
356
  //#region src/core/domain/typeGuards.ts
341
357
  /**
342
- * Type guard utilities for safe runtime type narrowing.
343
- * Use these functions instead of `as` casts when working with `unknown` values.
344
- */
345
- /**
346
358
  * Narrows `unknown` to `{ code: string }`.
347
359
  */
348
360
  function hasCode(value) {
@@ -364,61 +376,121 @@ function hasOptionalType(value) {
364
376
  if (!isRecord(value)) return false;
365
377
  return value.type === void 0 || typeof value.type === "string";
366
378
  }
379
+ /**
380
+ * Strict boolean validation — rejects non-boolean values.
381
+ * Returns the boolean value if valid, or `defaultValue` when the value is undefined/null.
382
+ * Throws `BusinessRuleError` when the value is present but not a boolean.
383
+ */
384
+ function parseStrictBoolean(value, fieldName, context, errorCode, defaultValue) {
385
+ if (value === void 0 || value === null) {
386
+ if (defaultValue !== void 0) return defaultValue;
387
+ throw new BusinessRuleError(errorCode, `${context} must have a boolean "${fieldName}" property`);
388
+ }
389
+ if (typeof value !== "boolean") throw new BusinessRuleError(errorCode, `${context} has invalid "${fieldName}" value: ${String(value)}. Must be a boolean`);
390
+ return value;
391
+ }
392
+ /**
393
+ * Validates that an unknown value is a string within an allowed set and returns a typed value.
394
+ * Replaces manual `typeof` + `Set.has` + `as` cast patterns.
395
+ */
396
+ function parseEnum(value, validValues, errorCode, message) {
397
+ if (typeof value !== "string" || !validValues.has(value)) throw new BusinessRuleError(errorCode, message);
398
+ return value;
399
+ }
400
+ /**
401
+ * Shared entity parsing logic for domains that use `{ type, code }` entities.
402
+ * Handles common validation: isRecord check, type enum validation, and code non-empty check.
403
+ * Use `allowEmptyCode` to permit empty/missing code for specific entity types (e.g., CREATOR).
404
+ */
405
+ function parseEntityBase(raw, index, validTypes, errorCodes, options) {
406
+ if (!isRecord(raw)) throw new BusinessRuleError(errorCodes.invalidStructure, `Entity at index ${index} must be an object`);
407
+ const type = parseEnum(raw.type, validTypes, errorCodes.invalidType, `Entity at index ${index} has invalid type: ${String(raw.type)}. Must be ${[...validTypes].join(", ")}`);
408
+ if (options?.allowEmptyCode?.(type)) return {
409
+ type,
410
+ code: typeof raw.code === "string" ? raw.code : ""
411
+ };
412
+ if (typeof raw.code !== "string" || raw.code.length === 0) throw new BusinessRuleError(errorCodes.emptyCode, `Entity at index ${index} must have a non-empty "code" property`);
413
+ return {
414
+ type,
415
+ code: raw.code
416
+ };
417
+ }
418
+
419
+ //#endregion
420
+ //#region src/core/domain/services/yamlConfigParser.ts
421
+ function parseYamlConfig(rawText, errorCodes, domainLabel) {
422
+ if (rawText.trim().length === 0) throw new BusinessRuleError(errorCodes.emptyConfigText, `${domainLabel} config text is empty`);
423
+ let parsed;
424
+ try {
425
+ parsed = parse(rawText);
426
+ } catch (error) {
427
+ throw new BusinessRuleError(errorCodes.invalidConfigYaml, `Failed to parse ${domainLabel} YAML: ${error instanceof Error ? error.message : String(error)}`, error);
428
+ }
429
+ if (!isRecord(parsed)) throw new BusinessRuleError(errorCodes.invalidConfigStructure, `${domainLabel} config must be a YAML object`);
430
+ return parsed;
431
+ }
367
432
 
368
433
  //#endregion
369
434
  //#region src/core/domain/action/valueObject.ts
370
- const VALID_SRC_TYPES = new Set(["FIELD", "RECORD_URL"]);
371
- const VALID_ENTITY_TYPES$10 = new Set([
435
+ const SRC_TYPES = ["FIELD", "RECORD_URL"];
436
+ const VALID_SRC_TYPES = new Set(SRC_TYPES);
437
+ function isActionMappingSrcType(value) {
438
+ return VALID_SRC_TYPES.has(value);
439
+ }
440
+ const ENTITY_TYPES$1 = [
372
441
  "USER",
373
442
  "GROUP",
374
443
  "ORGANIZATION"
375
- ]);
444
+ ];
445
+ const VALID_ENTITY_TYPES$9 = new Set(ENTITY_TYPES$1);
446
+ function isActionEntityType(value) {
447
+ return VALID_ENTITY_TYPES$9.has(value);
448
+ }
376
449
 
377
450
  //#endregion
378
451
  //#region src/core/domain/action/services/configParser.ts
379
452
  function parseDestApp(raw, actionName) {
380
453
  if (!isRecord(raw)) throw new BusinessRuleError(ActionErrorCode.AcInvalidConfigStructure, `Action "${actionName}" destApp must be an object`);
381
- const obj = raw;
382
454
  const result = {};
383
- if (obj.app !== void 0 && obj.app !== null) result.app = String(obj.app);
384
- if (obj.code !== void 0 && obj.code !== null) result.code = String(obj.code);
455
+ if (raw.app !== void 0 && raw.app !== null) result.app = String(raw.app);
456
+ if (raw.code !== void 0 && raw.code !== null) result.code = String(raw.code);
457
+ if (result.app === void 0 && result.code === void 0) throw new BusinessRuleError(ActionErrorCode.AcInvalidConfigStructure, `Action "${actionName}" destApp must have at least "app" or "code" property`);
385
458
  return result;
386
459
  }
387
460
  function parseMapping(raw, index, actionName) {
388
461
  if (!isRecord(raw)) throw new BusinessRuleError(ActionErrorCode.AcInvalidConfigStructure, `Action "${actionName}" mapping at index ${index} must be an object`);
389
- const obj = raw;
390
- if (typeof obj.srcType !== "string" || !VALID_SRC_TYPES.has(obj.srcType)) throw new BusinessRuleError(ActionErrorCode.AcInvalidSrcType, `Action "${actionName}" mapping at index ${index} has invalid srcType: ${String(obj.srcType)}. Must be FIELD or RECORD_URL`);
391
- if (typeof obj.destField !== "string" || obj.destField.length === 0) throw new BusinessRuleError(ActionErrorCode.AcInvalidConfigStructure, `Action "${actionName}" mapping at index ${index} must have a non-empty "destField" property`);
392
- return {
393
- srcType: obj.srcType,
394
- destField: obj.destField,
395
- ...obj.srcField !== void 0 && obj.srcField !== null && typeof obj.srcField === "string" ? { srcField: obj.srcField } : {}
462
+ if (typeof raw.srcType !== "string" || !isActionMappingSrcType(raw.srcType)) throw new BusinessRuleError(ActionErrorCode.AcInvalidSrcType, `Action "${actionName}" mapping at index ${index} has invalid srcType: ${String(raw.srcType)}. Must be FIELD or RECORD_URL`);
463
+ if (typeof raw.destField !== "string" || raw.destField.length === 0) throw new BusinessRuleError(ActionErrorCode.AcInvalidConfigStructure, `Action "${actionName}" mapping at index ${index} must have a non-empty "destField" property`);
464
+ const result = {
465
+ srcType: raw.srcType,
466
+ destField: raw.destField,
467
+ ...raw.srcField !== void 0 && raw.srcField !== null && typeof raw.srcField === "string" ? { srcField: raw.srcField } : {}
396
468
  };
469
+ if (result.srcType === "FIELD" && result.srcField === void 0) throw new BusinessRuleError(ActionErrorCode.AcInvalidConfigStructure, `Action "${actionName}" mapping at index ${index} with srcType "FIELD" must have a "srcField" property`);
470
+ return result;
397
471
  }
398
472
  function parseEntity$4(raw, index, actionName) {
399
473
  if (!isRecord(raw)) throw new BusinessRuleError(ActionErrorCode.AcInvalidConfigStructure, `Action "${actionName}" entity at index ${index} must be an object`);
400
- const obj = raw;
401
- if (typeof obj.type !== "string" || !VALID_ENTITY_TYPES$10.has(obj.type)) throw new BusinessRuleError(ActionErrorCode.AcInvalidEntityType, `Action "${actionName}" entity at index ${index} has invalid type: ${String(obj.type)}. Must be USER, GROUP, or ORGANIZATION`);
402
- if (typeof obj.code !== "string" || obj.code.length === 0) throw new BusinessRuleError(ActionErrorCode.AcInvalidConfigStructure, `Action "${actionName}" entity at index ${index} must have a non-empty "code" property`);
474
+ if (typeof raw.type !== "string" || !isActionEntityType(raw.type)) throw new BusinessRuleError(ActionErrorCode.AcInvalidEntityType, `Action "${actionName}" entity at index ${index} has invalid type: ${String(raw.type)}. Must be USER, GROUP, or ORGANIZATION`);
475
+ if (typeof raw.code !== "string" || raw.code.length === 0) throw new BusinessRuleError(ActionErrorCode.AcInvalidConfigStructure, `Action "${actionName}" entity at index ${index} must have a non-empty "code" property`);
403
476
  return {
404
- type: obj.type,
405
- code: obj.code
477
+ type: raw.type,
478
+ code: raw.code
406
479
  };
407
480
  }
408
481
  function parseActionConfig(raw, actionName) {
409
- if (!isRecord(raw)) throw new BusinessRuleError(ActionErrorCode.AcInvalidConfigStructure, `Action "${actionName}" must be an object`);
410
- const obj = raw;
411
482
  if (actionName.length === 0) throw new BusinessRuleError(ActionErrorCode.AcEmptyActionName, "Action name (key) must not be empty");
412
- if (typeof obj.index !== "number") throw new BusinessRuleError(ActionErrorCode.AcInvalidConfigStructure, `Action "${actionName}" must have a numeric "index" property`);
413
- if (!isRecord(obj.destApp)) throw new BusinessRuleError(ActionErrorCode.AcInvalidConfigStructure, `Action "${actionName}" must have a "destApp" object`);
414
- const destApp = parseDestApp(obj.destApp, actionName);
415
- if (!Array.isArray(obj.mappings)) throw new BusinessRuleError(ActionErrorCode.AcInvalidConfigStructure, `Action "${actionName}" must have a "mappings" array`);
416
- const mappings = obj.mappings.map((item, i) => parseMapping(item, i, actionName));
417
- if (!Array.isArray(obj.entities)) throw new BusinessRuleError(ActionErrorCode.AcInvalidConfigStructure, `Action "${actionName}" must have an "entities" array`);
418
- const entities = obj.entities.map((item, i) => parseEntity$4(item, i, actionName));
419
- const filterCond = typeof obj.filterCond === "string" ? obj.filterCond : "";
420
- return {
421
- index: obj.index,
483
+ if (!isRecord(raw)) throw new BusinessRuleError(ActionErrorCode.AcInvalidConfigStructure, `Action "${actionName}" must be an object`);
484
+ if (typeof raw.index !== "number" || !Number.isInteger(raw.index) || raw.index < 0) throw new BusinessRuleError(ActionErrorCode.AcInvalidConfigStructure, `Action "${actionName}" must have a non-negative integer "index" property`);
485
+ if (!isRecord(raw.destApp)) throw new BusinessRuleError(ActionErrorCode.AcInvalidConfigStructure, `Action "${actionName}" must have a "destApp" object`);
486
+ const destApp = parseDestApp(raw.destApp, actionName);
487
+ if (!Array.isArray(raw.mappings)) throw new BusinessRuleError(ActionErrorCode.AcInvalidConfigStructure, `Action "${actionName}" must have a "mappings" array`);
488
+ const mappings = raw.mappings.map((item, i) => parseMapping(item, i, actionName));
489
+ if (!Array.isArray(raw.entities)) throw new BusinessRuleError(ActionErrorCode.AcInvalidConfigStructure, `Action "${actionName}" must have an "entities" array`);
490
+ const entities = raw.entities.map((item, i) => parseEntity$4(item, i, actionName));
491
+ const filterCond = typeof raw.filterCond === "string" ? raw.filterCond : "";
492
+ return {
493
+ index: raw.index,
422
494
  name: actionName,
423
495
  destApp,
424
496
  mappings,
@@ -427,19 +499,20 @@ function parseActionConfig(raw, actionName) {
427
499
  };
428
500
  }
429
501
  const ActionConfigParser = { parse: (rawText) => {
430
- if (rawText.trim().length === 0) throw new BusinessRuleError(ActionErrorCode.AcEmptyConfigText, "Action config text is empty");
431
- let parsed;
432
- try {
433
- parsed = parse(rawText);
434
- } catch (error) {
435
- throw new BusinessRuleError(ActionErrorCode.AcInvalidConfigYaml, `Failed to parse YAML: ${error instanceof Error ? error.message : String(error)}`);
436
- }
437
- if (!isRecord(parsed)) throw new BusinessRuleError(ActionErrorCode.AcInvalidConfigStructure, "Config must be a YAML object");
438
- const obj = parsed;
502
+ const obj = parseYamlConfig(rawText, {
503
+ emptyConfigText: ActionErrorCode.AcEmptyConfigText,
504
+ invalidConfigYaml: ActionErrorCode.AcInvalidConfigYaml,
505
+ invalidConfigStructure: ActionErrorCode.AcInvalidConfigStructure
506
+ }, "Action");
439
507
  if (!isRecord(obj.actions)) throw new BusinessRuleError(ActionErrorCode.AcInvalidConfigStructure, "Config must have an \"actions\" object");
440
508
  const rawActions = obj.actions;
441
509
  const actions = {};
442
510
  for (const [key, value] of Object.entries(rawActions)) actions[key] = parseActionConfig(value, key);
511
+ const seenIndices = /* @__PURE__ */ new Set();
512
+ for (const [key, action] of Object.entries(actions)) {
513
+ if (seenIndices.has(action.index)) throw new BusinessRuleError(ActionErrorCode.AcDuplicateIndex, `Duplicate action index ${action.index} found in action "${key}"`);
514
+ seenIndices.add(action.index);
515
+ }
443
516
  return { actions };
444
517
  } };
445
518
 
@@ -471,7 +544,7 @@ function fromKintoneDestApp(raw) {
471
544
  return result;
472
545
  }
473
546
  function fromKintoneMapping(raw) {
474
- if (!VALID_SRC_TYPES.has(raw.srcType)) throw new SystemError(SystemErrorCode.ExternalApiError, `Unexpected srcType value from kintone API: ${raw.srcType}`);
547
+ if (!isActionMappingSrcType(raw.srcType)) throw new SystemError(SystemErrorCode.ExternalApiError, `Unexpected srcType value from kintone API: ${raw.srcType}`);
475
548
  return {
476
549
  srcType: raw.srcType,
477
550
  destField: raw.destField,
@@ -479,15 +552,17 @@ function fromKintoneMapping(raw) {
479
552
  };
480
553
  }
481
554
  function fromKintoneEntity$4(raw) {
482
- if (!VALID_ENTITY_TYPES$10.has(raw.type)) throw new SystemError(SystemErrorCode.ExternalApiError, `Unexpected entity type from kintone API: ${raw.type}`);
555
+ if (!isActionEntityType(raw.type)) throw new SystemError(SystemErrorCode.ExternalApiError, `Unexpected entity type from kintone API: ${raw.type}`);
483
556
  return {
484
557
  type: raw.type,
485
558
  code: raw.code
486
559
  };
487
560
  }
488
561
  function fromKintoneAction$1(raw) {
562
+ const index = Number(raw.index);
563
+ if (!Number.isFinite(index)) throw new SystemError(SystemErrorCode.ExternalApiError, `Unexpected non-numeric index from kintone API: ${raw.index}`);
489
564
  return {
490
- index: Number(raw.index),
565
+ index,
491
566
  name: raw.name,
492
567
  destApp: fromKintoneDestApp(raw.destApp),
493
568
  mappings: raw.mappings.map(fromKintoneMapping),
@@ -665,25 +740,42 @@ function createLocalFileActionStorage(filePath) {
665
740
  }
666
741
 
667
742
  //#endregion
668
- //#region src/core/adapters/kintone/customizationConfigurator.ts
669
- const VALID_SCOPES$1 = new Set([
743
+ //#region src/core/domain/customization/valueObject.ts
744
+ const SCOPES = [
670
745
  "ALL",
671
746
  "ADMIN",
672
747
  "NONE"
673
- ]);
748
+ ];
749
+ const VALID_SCOPES = new Set(SCOPES);
750
+ function isCustomizationScope(value) {
751
+ return VALID_SCOPES.has(value);
752
+ }
753
+ const RESOURCE_TYPES = ["FILE", "URL"];
754
+ const VALID_RESOURCE_TYPES = new Set(RESOURCE_TYPES);
755
+ function isResourceType(value) {
756
+ return VALID_RESOURCE_TYPES.has(value);
757
+ }
758
+ const DEFAULT_CUSTOMIZATION_SCOPE = "ALL";
759
+
760
+ //#endregion
761
+ //#region src/core/adapters/kintone/customizationConfigurator.ts
674
762
  function fromKintoneResource(raw) {
675
- if (raw.type === "FILE" && raw.file) return {
676
- type: "FILE",
677
- file: {
678
- fileKey: raw.file.fileKey,
679
- name: raw.file.name,
680
- contentType: raw.file.contentType,
681
- size: raw.file.size
682
- }
683
- };
763
+ if (raw.type === "FILE") {
764
+ if (!raw.file) throw new SystemError(SystemErrorCode.ExternalApiError, "FILE resource from kintone API is missing file metadata");
765
+ return {
766
+ type: "FILE",
767
+ file: {
768
+ fileKey: raw.file.fileKey,
769
+ name: raw.file.name,
770
+ contentType: raw.file.contentType,
771
+ size: raw.file.size
772
+ }
773
+ };
774
+ }
775
+ if (!raw.url) throw new SystemError(SystemErrorCode.ExternalApiError, "URL resource from kintone API is missing url property");
684
776
  return {
685
777
  type: "URL",
686
- url: raw.url ?? ""
778
+ url: raw.url
687
779
  };
688
780
  }
689
781
  function fromKintoneResourceList(raw) {
@@ -714,7 +806,7 @@ var KintoneCustomizationConfigurator = class {
714
806
  preview: true
715
807
  });
716
808
  const rawScope = String(response.scope);
717
- if (!VALID_SCOPES$1.has(rawScope)) throw new SystemError(SystemErrorCode.ExternalApiError, `Unexpected scope value from kintone API: ${rawScope}`);
809
+ if (!isCustomizationScope(rawScope)) throw new SystemError(SystemErrorCode.ExternalApiError, `Unexpected scope value from kintone API: ${rawScope}`);
718
810
  return {
719
811
  scope: rawScope,
720
812
  desktop: {
@@ -1799,7 +1891,7 @@ function parseAuth(raw) {
1799
1891
  }
1800
1892
  throw new BusinessRuleError(ProjectConfigErrorCode.PcInvalidAuthConfig, "Auth must have either apiToken or username/password");
1801
1893
  }
1802
- const ConfigParser$1 = { parse: parseProjectConfig };
1894
+ const ConfigParser = { parse: parseProjectConfig };
1803
1895
 
1804
1896
  //#endregion
1805
1897
  //#region src/core/application/projectConfig/loadProjectConfig.ts
@@ -1815,7 +1907,7 @@ function loadProjectConfig(input) {
1815
1907
  } catch (cause) {
1816
1908
  throw new ValidationError(ValidationErrorCode.InvalidInput, "Invalid YAML syntax in config file", cause);
1817
1909
  }
1818
- return ConfigParser$1.parse(raw);
1910
+ return ConfigParser.parse(raw);
1819
1911
  }
1820
1912
 
1821
1913
  //#endregion
@@ -2397,6 +2489,20 @@ var apply_default$12 = define({
2397
2489
  }
2398
2490
  });
2399
2491
 
2492
+ //#endregion
2493
+ //#region src/core/domain/services/yamlConfigSerializer.ts
2494
+ function serializeToYaml(data) {
2495
+ try {
2496
+ return stringify(data, {
2497
+ lineWidth: 0,
2498
+ defaultKeyType: "PLAIN",
2499
+ defaultStringType: "PLAIN"
2500
+ });
2501
+ } catch (error) {
2502
+ throw new BusinessRuleError(DomainServiceErrorCode.YamlSerializationFailed, `Failed to serialize config to YAML: ${error instanceof Error ? error.message : String(error)}`, error);
2503
+ }
2504
+ }
2505
+
2400
2506
  //#endregion
2401
2507
  //#region src/core/domain/action/services/configSerializer.ts
2402
2508
  function serializeDestApp(destApp) {
@@ -2433,11 +2539,7 @@ const ActionConfigSerializer = { serialize: (config) => {
2433
2539
  const actions = {};
2434
2540
  for (const [key, value] of Object.entries(config.actions)) actions[key] = serializeActionConfig(value);
2435
2541
  serialized.actions = actions;
2436
- return stringify(serialized, {
2437
- lineWidth: 0,
2438
- defaultKeyType: "PLAIN",
2439
- defaultStringType: "PLAIN"
2440
- });
2542
+ return serializeToYaml(serialized);
2441
2543
  } };
2442
2544
 
2443
2545
  //#endregion
@@ -2611,12 +2713,24 @@ function buildDiffResult(entries, warnings = []) {
2611
2713
  };
2612
2714
  }
2613
2715
 
2716
+ //#endregion
2717
+ //#region src/core/domain/services/recordDiffDetector.ts
2718
+ function detectRecordDiff(localRecord, remoteRecord, callbacks) {
2719
+ const entries = [];
2720
+ for (const [key, localValue] of Object.entries(localRecord)) if (!Object.hasOwn(remoteRecord, key)) entries.push(callbacks.onAdded(key, localValue));
2721
+ else {
2722
+ const entry = callbacks.onModified(key, localValue, remoteRecord[key]);
2723
+ if (entry !== void 0) entries.push(entry);
2724
+ }
2725
+ for (const [key, remoteValue] of Object.entries(remoteRecord)) if (!Object.hasOwn(localRecord, key)) entries.push(callbacks.onDeleted(key, remoteValue));
2726
+ return entries;
2727
+ }
2728
+
2614
2729
  //#endregion
2615
2730
  //#region src/core/domain/action/services/diffDetector.ts
2616
2731
  function compareActions$1(local, remote) {
2617
2732
  const diffs = [];
2618
2733
  if (local.index !== remote.index) diffs.push(`index: ${remote.index} -> ${local.index}`);
2619
- if (local.name !== remote.name) diffs.push(`name: "${remote.name}" -> "${local.name}"`);
2620
2734
  if (!deepEqual(local.destApp, remote.destApp)) diffs.push("destApp changed");
2621
2735
  if (local.filterCond !== remote.filterCond) diffs.push("filterCond changed");
2622
2736
  if (!deepEqual(local.mappings, remote.mappings)) if (local.mappings.length !== remote.mappings.length) diffs.push(`mappings: ${remote.mappings.length} -> ${local.mappings.length}`);
@@ -2624,30 +2738,30 @@ function compareActions$1(local, remote) {
2624
2738
  if (!deepEqual(local.entities, remote.entities)) diffs.push("entities changed");
2625
2739
  return diffs;
2626
2740
  }
2741
+ function destAppLabel(action) {
2742
+ return `dest: ${action.destApp.app ?? action.destApp.code ?? "(unspecified)"}`;
2743
+ }
2627
2744
  const ActionDiffDetector = { detect: (local, remote) => {
2628
- const entries = [];
2629
- for (const [name, localAction] of Object.entries(local.actions)) {
2630
- const remoteAction = remote.actions[name];
2631
- if (!remoteAction) entries.push({
2745
+ return buildDiffResult(detectRecordDiff(local.actions, remote.actions, {
2746
+ onAdded: (name, localAction) => ({
2632
2747
  type: "added",
2633
2748
  actionName: name,
2634
- details: `dest: ${localAction.destApp.app ?? localAction.destApp.code ?? "(unspecified)"}`
2635
- });
2636
- else {
2749
+ details: destAppLabel(localAction)
2750
+ }),
2751
+ onModified: (name, localAction, remoteAction) => {
2637
2752
  const diffs = compareActions$1(localAction, remoteAction);
2638
- if (diffs.length > 0) entries.push({
2753
+ if (diffs.length > 0) return {
2639
2754
  type: "modified",
2640
2755
  actionName: name,
2641
2756
  details: diffs.join(", ")
2642
- });
2643
- }
2644
- }
2645
- for (const [name, remoteAction] of Object.entries(remote.actions)) if (!local.actions[name]) entries.push({
2646
- type: "deleted",
2647
- actionName: name,
2648
- details: `dest: ${remoteAction.destApp.app ?? remoteAction.destApp.code ?? "(unspecified)"}`
2649
- });
2650
- return buildDiffResult(entries);
2757
+ };
2758
+ },
2759
+ onDeleted: (name, remoteAction) => ({
2760
+ type: "deleted",
2761
+ actionName: name,
2762
+ details: destAppLabel(remoteAction)
2763
+ })
2764
+ }));
2651
2765
  } };
2652
2766
 
2653
2767
  //#endregion
@@ -2747,20 +2861,16 @@ var action_default = define({
2747
2861
  //#endregion
2748
2862
  //#region src/core/domain/adminNotes/services/configParser.ts
2749
2863
  const AdminNotesConfigParser = { parse: (rawText) => {
2750
- if (rawText.trim().length === 0) throw new BusinessRuleError(AdminNotesErrorCode.AnEmptyConfigText, "Admin notes config text is empty");
2751
- let parsed;
2752
- try {
2753
- parsed = parse(rawText);
2754
- } catch (error) {
2755
- throw new BusinessRuleError(AdminNotesErrorCode.AnInvalidConfigYaml, `Failed to parse YAML: ${error instanceof Error ? error.message : String(error)}`);
2756
- }
2757
- if (!isRecord(parsed)) throw new BusinessRuleError(AdminNotesErrorCode.AnInvalidConfigStructure, "Config must be a YAML object");
2758
- const obj = parsed;
2759
- if (typeof obj.content !== "string") throw new BusinessRuleError(AdminNotesErrorCode.AnInvalidConfigStructure, "Config must have a \"content\" string property");
2760
- if (typeof obj.includeInTemplateAndDuplicates !== "boolean") throw new BusinessRuleError(AdminNotesErrorCode.AnInvalidConfigStructure, "Config must have an \"includeInTemplateAndDuplicates\" boolean property");
2864
+ const parsed = parseYamlConfig(rawText, {
2865
+ emptyConfigText: AdminNotesErrorCode.AnEmptyConfigText,
2866
+ invalidConfigYaml: AdminNotesErrorCode.AnInvalidConfigYaml,
2867
+ invalidConfigStructure: AdminNotesErrorCode.AnInvalidConfigStructure
2868
+ }, "Admin notes");
2869
+ if (typeof parsed.content !== "string") throw new BusinessRuleError(AdminNotesErrorCode.AnInvalidConfigStructure, "Config must have a \"content\" string property");
2870
+ if (typeof parsed.includeInTemplateAndDuplicates !== "boolean") throw new BusinessRuleError(AdminNotesErrorCode.AnInvalidConfigStructure, "Config must have an \"includeInTemplateAndDuplicates\" boolean property");
2761
2871
  return {
2762
- content: obj.content,
2763
- includeInTemplateAndDuplicates: obj.includeInTemplateAndDuplicates
2872
+ content: parsed.content,
2873
+ includeInTemplateAndDuplicates: parsed.includeInTemplateAndDuplicates
2764
2874
  };
2765
2875
  } };
2766
2876
 
@@ -2918,13 +3028,9 @@ var apply_default$11 = define({
2918
3028
  //#endregion
2919
3029
  //#region src/core/domain/adminNotes/services/configSerializer.ts
2920
3030
  const AdminNotesConfigSerializer = { serialize: (config) => {
2921
- return stringify({
3031
+ return serializeToYaml({
2922
3032
  content: config.content,
2923
3033
  includeInTemplateAndDuplicates: config.includeInTemplateAndDuplicates
2924
- }, {
2925
- lineWidth: 0,
2926
- defaultKeyType: "PLAIN",
2927
- defaultStringType: "PLAIN"
2928
3034
  });
2929
3035
  } };
2930
3036
 
@@ -3048,59 +3154,41 @@ var admin_notes_default = define({
3048
3154
 
3049
3155
  //#endregion
3050
3156
  //#region src/core/domain/appPermission/services/configParser.ts
3051
- const VALID_ENTITY_TYPES$9 = new Set([
3157
+ const VALID_ENTITY_TYPES$8 = new Set([
3052
3158
  "USER",
3053
3159
  "GROUP",
3054
3160
  "ORGANIZATION",
3055
3161
  "CREATOR"
3056
3162
  ]);
3057
3163
  function parseEntity$3(raw, index) {
3058
- if (!isRecord(raw)) throw new BusinessRuleError(AppPermissionErrorCode.ApInvalidConfigStructure, `Entity at index ${index} must be an object`);
3059
- const obj = raw;
3060
- if (typeof obj.type !== "string" || !VALID_ENTITY_TYPES$9.has(obj.type)) throw new BusinessRuleError(AppPermissionErrorCode.ApInvalidEntityType, `Entity at index ${index} has invalid type: ${String(obj.type)}. Must be USER, GROUP, ORGANIZATION, or CREATOR`);
3061
- const type = obj.type;
3062
- if (type === "CREATOR") return {
3063
- type,
3064
- code: typeof obj.code === "string" ? obj.code : ""
3065
- };
3066
- if (typeof obj.code !== "string" || obj.code.length === 0) throw new BusinessRuleError(AppPermissionErrorCode.ApEmptyEntityCode, `Entity at index ${index} must have a non-empty "code" property`);
3067
- return {
3068
- type,
3069
- code: obj.code
3070
- };
3071
- }
3072
- function parseBooleanField$1(obj, field, index) {
3073
- const value = obj[field];
3074
- if (typeof value !== "boolean") throw new BusinessRuleError(AppPermissionErrorCode.ApInvalidBooleanField, `App right at index ${index} must have a boolean "${field}" property`);
3075
- return value;
3164
+ return parseEntityBase(raw, index, VALID_ENTITY_TYPES$8, {
3165
+ invalidStructure: AppPermissionErrorCode.ApInvalidConfigStructure,
3166
+ invalidType: AppPermissionErrorCode.ApInvalidEntityType,
3167
+ emptyCode: AppPermissionErrorCode.ApEmptyEntityCode
3168
+ }, { allowEmptyCode: (type) => type === "CREATOR" });
3076
3169
  }
3077
3170
  function parseAppRight(raw, index) {
3078
3171
  if (!isRecord(raw)) throw new BusinessRuleError(AppPermissionErrorCode.ApInvalidConfigStructure, `App right at index ${index} must be an object`);
3079
- const obj = raw;
3080
3172
  return {
3081
- entity: parseEntity$3(obj.entity, index),
3082
- includeSubs: parseBooleanField$1(obj, "includeSubs", index),
3083
- appEditable: parseBooleanField$1(obj, "appEditable", index),
3084
- recordViewable: parseBooleanField$1(obj, "recordViewable", index),
3085
- recordAddable: parseBooleanField$1(obj, "recordAddable", index),
3086
- recordEditable: parseBooleanField$1(obj, "recordEditable", index),
3087
- recordDeletable: parseBooleanField$1(obj, "recordDeletable", index),
3088
- recordImportable: parseBooleanField$1(obj, "recordImportable", index),
3089
- recordExportable: parseBooleanField$1(obj, "recordExportable", index)
3173
+ entity: parseEntity$3(raw.entity, index),
3174
+ includeSubs: parseStrictBoolean(raw.includeSubs, "includeSubs", `App right at index ${index}`, AppPermissionErrorCode.ApInvalidBooleanField),
3175
+ appEditable: parseStrictBoolean(raw.appEditable, "appEditable", `App right at index ${index}`, AppPermissionErrorCode.ApInvalidBooleanField),
3176
+ recordViewable: parseStrictBoolean(raw.recordViewable, "recordViewable", `App right at index ${index}`, AppPermissionErrorCode.ApInvalidBooleanField),
3177
+ recordAddable: parseStrictBoolean(raw.recordAddable, "recordAddable", `App right at index ${index}`, AppPermissionErrorCode.ApInvalidBooleanField),
3178
+ recordEditable: parseStrictBoolean(raw.recordEditable, "recordEditable", `App right at index ${index}`, AppPermissionErrorCode.ApInvalidBooleanField),
3179
+ recordDeletable: parseStrictBoolean(raw.recordDeletable, "recordDeletable", `App right at index ${index}`, AppPermissionErrorCode.ApInvalidBooleanField),
3180
+ recordImportable: parseStrictBoolean(raw.recordImportable, "recordImportable", `App right at index ${index}`, AppPermissionErrorCode.ApInvalidBooleanField),
3181
+ recordExportable: parseStrictBoolean(raw.recordExportable, "recordExportable", `App right at index ${index}`, AppPermissionErrorCode.ApInvalidBooleanField)
3090
3182
  };
3091
3183
  }
3092
3184
  const AppPermissionConfigParser = { parse: (rawText) => {
3093
- if (rawText.trim().length === 0) throw new BusinessRuleError(AppPermissionErrorCode.ApEmptyConfigText, "App permission config text is empty");
3094
- let parsed;
3095
- try {
3096
- parsed = parse(rawText);
3097
- } catch (error) {
3098
- throw new BusinessRuleError(AppPermissionErrorCode.ApInvalidConfigYaml, `Failed to parse YAML: ${error instanceof Error ? error.message : String(error)}`);
3099
- }
3100
- if (!isRecord(parsed)) throw new BusinessRuleError(AppPermissionErrorCode.ApInvalidConfigStructure, "Config must be a YAML object");
3101
- const obj = parsed;
3102
- if (!Array.isArray(obj.rights)) throw new BusinessRuleError(AppPermissionErrorCode.ApInvalidConfigStructure, "Config must have a \"rights\" array");
3103
- const rights = obj.rights.map((item, i) => parseAppRight(item, i));
3185
+ const parsed = parseYamlConfig(rawText, {
3186
+ emptyConfigText: AppPermissionErrorCode.ApEmptyConfigText,
3187
+ invalidConfigYaml: AppPermissionErrorCode.ApInvalidConfigYaml,
3188
+ invalidConfigStructure: AppPermissionErrorCode.ApInvalidConfigStructure
3189
+ }, "App permission");
3190
+ if (!Array.isArray(parsed.rights)) throw new BusinessRuleError(AppPermissionErrorCode.ApInvalidConfigStructure, "Config must have a \"rights\" array");
3191
+ const rights = parsed.rights.map((item, i) => parseAppRight(item, i));
3104
3192
  const seenKeys = /* @__PURE__ */ new Set();
3105
3193
  for (const right of rights) {
3106
3194
  const key = `${right.entity.type}:${right.entity.code}`;
@@ -3131,14 +3219,14 @@ async function applyAppPermission({ container }) {
3131
3219
 
3132
3220
  //#endregion
3133
3221
  //#region src/core/adapters/kintone/appPermissionConfigurator.ts
3134
- const VALID_ENTITY_TYPES$8 = new Set([
3222
+ const VALID_ENTITY_TYPES$7 = new Set([
3135
3223
  "USER",
3136
3224
  "GROUP",
3137
3225
  "ORGANIZATION",
3138
3226
  "CREATOR"
3139
3227
  ]);
3140
3228
  function fromKintoneRight$2(raw) {
3141
- if (!VALID_ENTITY_TYPES$8.has(raw.entity.type)) throw new SystemError(SystemErrorCode.ExternalApiError, `Unexpected entity type from kintone API: ${raw.entity.type}`);
3229
+ if (!VALID_ENTITY_TYPES$7.has(raw.entity.type)) throw new SystemError(SystemErrorCode.ExternalApiError, `Unexpected entity type from kintone API: ${raw.entity.type}`);
3142
3230
  const type = raw.entity.type;
3143
3231
  let code;
3144
3232
  if (type === "CREATOR") code = raw.entity.code ?? "";
@@ -3322,11 +3410,7 @@ function serializeAppRight(right) {
3322
3410
  };
3323
3411
  }
3324
3412
  const AppPermissionConfigSerializer = { serialize: (config) => {
3325
- return stringify({ rights: config.rights.map(serializeAppRight) }, {
3326
- lineWidth: 0,
3327
- defaultKeyType: "PLAIN",
3328
- defaultStringType: "PLAIN"
3329
- });
3413
+ return serializeToYaml({ rights: config.rights.map(serializeAppRight) });
3330
3414
  } };
3331
3415
 
3332
3416
  //#endregion
@@ -3509,28 +3593,21 @@ const ResourceMerger = {
3509
3593
 
3510
3594
  //#endregion
3511
3595
  //#region src/core/domain/customization/services/configParser.ts
3512
- const VALID_SCOPES = new Set([
3513
- "ALL",
3514
- "ADMIN",
3515
- "NONE"
3516
- ]);
3517
- const VALID_RESOURCE_TYPES = new Set(["FILE", "URL"]);
3518
3596
  function parseResource(raw, index) {
3519
3597
  if (!isRecord(raw)) throw new BusinessRuleError(CustomizationErrorCode.CzInvalidConfigStructure, `Resource at index ${index} must be an object`);
3520
- const obj = raw;
3521
- const type = obj.type;
3522
- if (typeof type !== "string" || !VALID_RESOURCE_TYPES.has(type)) throw new BusinessRuleError(CustomizationErrorCode.CzInvalidResourceType, `Resource at index ${index} has invalid type: ${String(type)}. Must be FILE or URL`);
3598
+ const type = raw.type;
3599
+ if (typeof type !== "string" || !isResourceType(type)) throw new BusinessRuleError(CustomizationErrorCode.CzInvalidResourceType, `Resource at index ${index} has invalid type: ${String(type)}. Must be FILE or URL`);
3523
3600
  if (type === "FILE") {
3524
- if (typeof obj.path !== "string" || obj.path.length === 0) throw new BusinessRuleError(CustomizationErrorCode.CzInvalidConfigStructure, `FILE resource at index ${index} must have a non-empty "path" property`);
3601
+ if (typeof raw.path !== "string" || raw.path.length === 0) throw new BusinessRuleError(CustomizationErrorCode.CzInvalidConfigStructure, `FILE resource at index ${index} must have a non-empty "path" property`);
3525
3602
  return {
3526
3603
  type: "FILE",
3527
- path: obj.path
3604
+ path: raw.path
3528
3605
  };
3529
3606
  }
3530
- if (typeof obj.url !== "string" || obj.url.length === 0) throw new BusinessRuleError(CustomizationErrorCode.CzInvalidConfigStructure, `URL resource at index ${index} must have a non-empty "url" property`);
3607
+ if (typeof raw.url !== "string" || raw.url.length === 0) throw new BusinessRuleError(CustomizationErrorCode.CzInvalidConfigStructure, `URL resource at index ${index} must have a non-empty "url" property`);
3531
3608
  return {
3532
3609
  type: "URL",
3533
- url: obj.url
3610
+ url: raw.url
3534
3611
  };
3535
3612
  }
3536
3613
  function parseResourceList(raw) {
@@ -3539,25 +3616,20 @@ function parseResourceList(raw) {
3539
3616
  }
3540
3617
  function parsePlatform(raw) {
3541
3618
  if (!isRecord(raw)) throw new BusinessRuleError(CustomizationErrorCode.CzInvalidConfigStructure, "Platform configuration must be an object");
3542
- const obj = raw;
3543
3619
  return {
3544
- js: obj.js === void 0 || obj.js === null ? [] : parseResourceList(obj.js),
3545
- css: obj.css === void 0 || obj.css === null ? [] : parseResourceList(obj.css)
3620
+ js: raw.js === void 0 || raw.js === null ? [] : parseResourceList(raw.js),
3621
+ css: raw.css === void 0 || raw.css === null ? [] : parseResourceList(raw.css)
3546
3622
  };
3547
3623
  }
3548
- const ConfigParser = { parse: (rawText) => {
3549
- if (rawText.trim().length === 0) throw new BusinessRuleError(CustomizationErrorCode.CzEmptyConfigText, "Customization config text is empty");
3550
- let parsed;
3551
- try {
3552
- parsed = parse(rawText);
3553
- } catch (error) {
3554
- throw new BusinessRuleError(CustomizationErrorCode.CzInvalidConfigYaml, `Failed to parse YAML: ${error instanceof Error ? error.message : String(error)}`);
3555
- }
3556
- if (!isRecord(parsed)) throw new BusinessRuleError(CustomizationErrorCode.CzInvalidConfigStructure, "Config must be a YAML object");
3557
- const obj = parsed;
3624
+ const CustomizationConfigParser = { parse: (rawText) => {
3625
+ const obj = parseYamlConfig(rawText, {
3626
+ emptyConfigText: CustomizationErrorCode.CzEmptyConfigText,
3627
+ invalidConfigYaml: CustomizationErrorCode.CzInvalidConfigYaml,
3628
+ invalidConfigStructure: CustomizationErrorCode.CzInvalidConfigStructure
3629
+ }, "Customization");
3558
3630
  let scope;
3559
3631
  if (obj.scope !== void 0 && obj.scope !== null) {
3560
- if (typeof obj.scope !== "string" || !VALID_SCOPES.has(obj.scope)) throw new BusinessRuleError(CustomizationErrorCode.CzInvalidScope, `Invalid scope: ${String(obj.scope)}. Must be ALL, ADMIN, or NONE`);
3632
+ if (typeof obj.scope !== "string" || !isCustomizationScope(obj.scope)) throw new BusinessRuleError(CustomizationErrorCode.CzInvalidScope, `Invalid scope: ${String(obj.scope)}. Must be ALL, ADMIN, or NONE`);
3561
3633
  scope = obj.scope;
3562
3634
  }
3563
3635
  const desktop = obj.desktop === void 0 || obj.desktop === null ? {
@@ -3578,7 +3650,7 @@ const ConfigParser = { parse: (rawText) => {
3578
3650
  //#endregion
3579
3651
  //#region src/core/application/customization/parseConfig.ts
3580
3652
  function parseConfigText(rawText) {
3581
- return wrapBusinessRuleError(() => ConfigParser.parse(rawText));
3653
+ return wrapBusinessRuleError(() => CustomizationConfigParser.parse(rawText));
3582
3654
  }
3583
3655
 
3584
3656
  //#endregion
@@ -3681,14 +3753,10 @@ function serializePlatform(platform) {
3681
3753
  };
3682
3754
  }
3683
3755
  const CustomizationConfigSerializer = { serialize: (config) => {
3684
- return stringify({
3756
+ return serializeToYaml({
3685
3757
  ...config.scope !== void 0 ? { scope: config.scope } : {},
3686
3758
  ...hasPlatformResources(config.desktop) ? { desktop: serializePlatform(config.desktop) } : {},
3687
3759
  ...hasPlatformResources(config.mobile) ? { mobile: serializePlatform(config.mobile) } : {}
3688
- }, {
3689
- lineWidth: 0,
3690
- defaultKeyType: "PLAIN",
3691
- defaultStringType: "PLAIN"
3692
3760
  });
3693
3761
  } };
3694
3762
 
@@ -3950,15 +4018,11 @@ var apply_default$9 = define({
3950
4018
  }
3951
4019
  });
3952
4020
 
3953
- //#endregion
3954
- //#region src/core/domain/customization/valueObject.ts
3955
- const DEFAULT_CUSTOMIZATION_SCOPE = "ALL";
3956
-
3957
4021
  //#endregion
3958
4022
  //#region src/core/domain/customization/services/diffDetector.ts
3959
4023
  function resourceName(resource) {
3960
4024
  if (resource.type === "URL") return resource.url;
3961
- const parts = resource.path.replace(/\\/g, "/").split("/");
4025
+ const parts = resource.path.replace(/\\/g, "/").split("/").filter(Boolean);
3962
4026
  return parts[parts.length - 1];
3963
4027
  }
3964
4028
  function remoteResourceName(resource) {
@@ -4069,7 +4133,7 @@ const VALID_ACCESSIBILITIES$1 = new Set([
4069
4133
  "WRITE",
4070
4134
  "NONE"
4071
4135
  ]);
4072
- const VALID_ENTITY_TYPES$7 = new Set([
4136
+ const VALID_ENTITY_TYPES$6 = new Set([
4073
4137
  "USER",
4074
4138
  "GROUP",
4075
4139
  "ORGANIZATION",
@@ -4077,7 +4141,7 @@ const VALID_ENTITY_TYPES$7 = new Set([
4077
4141
  ]);
4078
4142
  function fromKintoneEntity$3(raw) {
4079
4143
  if (!VALID_ACCESSIBILITIES$1.has(raw.accessibility)) throw new SystemError(SystemErrorCode.ExternalApiError, `Unexpected accessibility value from kintone API: ${raw.accessibility}`);
4080
- if (!VALID_ENTITY_TYPES$7.has(raw.entity.type)) throw new SystemError(SystemErrorCode.ExternalApiError, `Unexpected entity type from kintone API: ${raw.entity.type}`);
4144
+ if (!VALID_ENTITY_TYPES$6.has(raw.entity.type)) throw new SystemError(SystemErrorCode.ExternalApiError, `Unexpected entity type from kintone API: ${raw.entity.type}`);
4081
4145
  const result = {
4082
4146
  accessibility: raw.accessibility,
4083
4147
  entity: {
@@ -4179,60 +4243,49 @@ const VALID_ACCESSIBILITIES = new Set([
4179
4243
  "WRITE",
4180
4244
  "NONE"
4181
4245
  ]);
4182
- const VALID_ENTITY_TYPES$6 = new Set([
4246
+ const VALID_ENTITY_TYPES$5 = new Set([
4183
4247
  "USER",
4184
4248
  "GROUP",
4185
4249
  "ORGANIZATION",
4186
4250
  "FIELD_ENTITY"
4187
4251
  ]);
4188
4252
  function parseEntity$2(raw, index) {
4189
- if (!isRecord(raw)) throw new BusinessRuleError(FieldPermissionErrorCode.FpInvalidConfigStructure, `Entity at index ${index} must be an object`);
4190
- const obj = raw;
4191
- if (typeof obj.type !== "string" || !VALID_ENTITY_TYPES$6.has(obj.type)) throw new BusinessRuleError(FieldPermissionErrorCode.FpInvalidEntityType, `Entity at index ${index} has invalid type: ${String(obj.type)}. Must be USER, GROUP, ORGANIZATION, or FIELD_ENTITY`);
4192
- if (typeof obj.code !== "string" || obj.code.length === 0) throw new BusinessRuleError(FieldPermissionErrorCode.FpEmptyEntityCode, `Entity at index ${index} must have a non-empty "code" property`);
4193
- return {
4194
- type: obj.type,
4195
- code: obj.code
4196
- };
4253
+ return parseEntityBase(raw, index, VALID_ENTITY_TYPES$5, {
4254
+ invalidStructure: FieldPermissionErrorCode.FpInvalidConfigStructure,
4255
+ invalidType: FieldPermissionErrorCode.FpInvalidEntityType,
4256
+ emptyCode: FieldPermissionErrorCode.FpEmptyEntityCode
4257
+ });
4197
4258
  }
4198
4259
  function parseFieldRightEntity(raw, index) {
4199
4260
  if (!isRecord(raw)) throw new BusinessRuleError(FieldPermissionErrorCode.FpInvalidConfigStructure, `Field right entity at index ${index} must be an object`);
4200
- const obj = raw;
4201
- if (typeof obj.accessibility !== "string" || !VALID_ACCESSIBILITIES.has(obj.accessibility)) throw new BusinessRuleError(FieldPermissionErrorCode.FpInvalidAccessibility, `Field right entity at index ${index} has invalid accessibility: ${String(obj.accessibility)}. Must be READ, WRITE, or NONE`);
4202
- const entity = parseEntity$2(obj.entity, index);
4203
4261
  const result = {
4204
- accessibility: obj.accessibility,
4205
- entity
4262
+ accessibility: parseEnum(raw.accessibility, VALID_ACCESSIBILITIES, FieldPermissionErrorCode.FpInvalidAccessibility, `Field right entity at index ${index} has invalid accessibility: ${String(raw.accessibility)}. Must be READ, WRITE, or NONE`),
4263
+ entity: parseEntity$2(raw.entity, index)
4206
4264
  };
4207
- if (obj.includeSubs !== void 0 && obj.includeSubs !== null) return {
4265
+ if (raw.includeSubs !== void 0 && raw.includeSubs !== null) return {
4208
4266
  ...result,
4209
- includeSubs: Boolean(obj.includeSubs)
4267
+ includeSubs: parseStrictBoolean(raw.includeSubs, "includeSubs", `Field right entity at index ${index}`, FieldPermissionErrorCode.FpInvalidBooleanField)
4210
4268
  };
4211
4269
  return result;
4212
4270
  }
4213
4271
  function parseFieldRight(raw, index) {
4214
4272
  if (!isRecord(raw)) throw new BusinessRuleError(FieldPermissionErrorCode.FpInvalidConfigStructure, `Field right at index ${index} must be an object`);
4215
- const obj = raw;
4216
- if (typeof obj.code !== "string" || obj.code.length === 0) throw new BusinessRuleError(FieldPermissionErrorCode.FpEmptyFieldCode, `Field right at index ${index} must have a non-empty "code" property`);
4217
- if (!Array.isArray(obj.entities)) throw new BusinessRuleError(FieldPermissionErrorCode.FpInvalidConfigStructure, `Field right at index ${index} must have an "entities" array`);
4218
- const entities = obj.entities.map((item, i) => parseFieldRightEntity(item, i));
4273
+ if (typeof raw.code !== "string" || raw.code.length === 0) throw new BusinessRuleError(FieldPermissionErrorCode.FpEmptyFieldCode, `Field right at index ${index} must have a non-empty "code" property`);
4274
+ if (!Array.isArray(raw.entities)) throw new BusinessRuleError(FieldPermissionErrorCode.FpInvalidConfigStructure, `Field right at index ${index} must have an "entities" array`);
4275
+ const entities = raw.entities.map((item, i) => parseFieldRightEntity(item, i));
4219
4276
  return {
4220
- code: obj.code,
4277
+ code: raw.code,
4221
4278
  entities
4222
4279
  };
4223
4280
  }
4224
4281
  const FieldPermissionConfigParser = { parse: (rawText) => {
4225
- if (rawText.trim().length === 0) throw new BusinessRuleError(FieldPermissionErrorCode.FpEmptyConfigText, "Field permission config text is empty");
4226
- let parsed;
4227
- try {
4228
- parsed = parse(rawText);
4229
- } catch (error) {
4230
- throw new BusinessRuleError(FieldPermissionErrorCode.FpInvalidConfigYaml, `Failed to parse YAML: ${error instanceof Error ? error.message : String(error)}`);
4231
- }
4232
- if (!isRecord(parsed)) throw new BusinessRuleError(FieldPermissionErrorCode.FpInvalidConfigStructure, "Config must be a YAML object");
4233
- const obj = parsed;
4234
- if (!Array.isArray(obj.rights)) throw new BusinessRuleError(FieldPermissionErrorCode.FpInvalidConfigStructure, "Config must have a \"rights\" array");
4235
- const rights = obj.rights.map((item, i) => parseFieldRight(item, i));
4282
+ const parsed = parseYamlConfig(rawText, {
4283
+ emptyConfigText: FieldPermissionErrorCode.FpEmptyConfigText,
4284
+ invalidConfigYaml: FieldPermissionErrorCode.FpInvalidConfigYaml,
4285
+ invalidConfigStructure: FieldPermissionErrorCode.FpInvalidConfigStructure
4286
+ }, "Field permission");
4287
+ if (!Array.isArray(parsed.rights)) throw new BusinessRuleError(FieldPermissionErrorCode.FpInvalidConfigStructure, "Config must have a \"rights\" array");
4288
+ const rights = parsed.rights.map((item, i) => parseFieldRight(item, i));
4236
4289
  const seenCodes = /* @__PURE__ */ new Set();
4237
4290
  for (const right of rights) {
4238
4291
  if (seenCodes.has(right.code)) throw new BusinessRuleError(FieldPermissionErrorCode.FpDuplicateFieldCode, `Duplicate field code: ${right.code}`);
@@ -4342,14 +4395,10 @@ function serializeFieldRightEntity(entity) {
4342
4395
  return result;
4343
4396
  }
4344
4397
  const FieldPermissionConfigSerializer = { serialize: (config) => {
4345
- return stringify({ rights: config.rights.map((right) => ({
4398
+ return serializeToYaml({ rights: config.rights.map((right) => ({
4346
4399
  code: right.code,
4347
4400
  entities: right.entities.map(serializeFieldRightEntity)
4348
- })) }, {
4349
- lineWidth: 0,
4350
- defaultKeyType: "PLAIN",
4351
- defaultStringType: "PLAIN"
4352
- });
4401
+ })) });
4353
4402
  } };
4354
4403
 
4355
4404
  //#endregion
@@ -4413,7 +4462,7 @@ var capture_default$9 = define({
4413
4462
 
4414
4463
  //#endregion
4415
4464
  //#region src/core/domain/fieldPermission/services/diffDetector.ts
4416
- function areEntitiesEqual(a, b) {
4465
+ function isEntitiesEqual$1(a, b) {
4417
4466
  return deepEqual(a.entities.map((e) => ({
4418
4467
  accessibility: e.accessibility,
4419
4468
  type: e.entity.type,
@@ -4437,7 +4486,7 @@ const FieldPermissionDiffDetector = { detect: (local, remote) => {
4437
4486
  fieldCode: code,
4438
4487
  details: `${localRight.entities.length} entities`
4439
4488
  });
4440
- else if (!areEntitiesEqual(localRight, remoteRight)) entries.push({
4489
+ else if (!isEntitiesEqual$1(localRight, remoteRight)) entries.push({
4441
4490
  type: "modified",
4442
4491
  fieldCode: code,
4443
4492
  details: "entities changed"
@@ -4649,18 +4698,22 @@ function createGeneralSettingsCliContainer(config) {
4649
4698
  }
4650
4699
 
4651
4700
  //#endregion
4652
- //#region src/core/adapters/kintone/notificationConfigurator.ts
4653
- const VALID_ENTITY_TYPES$5 = new Set([
4701
+ //#region src/core/domain/notification/valueObject.ts
4702
+ const ENTITY_TYPES = [
4654
4703
  "USER",
4655
4704
  "GROUP",
4656
4705
  "ORGANIZATION",
4657
4706
  "FIELD_ENTITY"
4658
- ]);
4659
- function validateEntityType(type, context) {
4660
- if (!VALID_ENTITY_TYPES$5.has(type)) throw new SystemError(SystemErrorCode.ExternalApiError, `Unexpected entity type from kintone API (${context}): ${type}`);
4707
+ ];
4708
+ const VALID_ENTITY_TYPES$4 = new Set(ENTITY_TYPES);
4709
+ function isNotificationEntityType(value) {
4710
+ return VALID_ENTITY_TYPES$4.has(value);
4661
4711
  }
4712
+
4713
+ //#endregion
4714
+ //#region src/core/adapters/kintone/notificationConfigurator.ts
4662
4715
  function fromKintoneEntity$2(raw) {
4663
- validateEntityType(raw.type, "entity");
4716
+ if (!isNotificationEntityType(raw.type)) throw new SystemError(SystemErrorCode.ExternalApiError, `Unexpected entity type from kintone API (entity): ${raw.type}`);
4664
4717
  return {
4665
4718
  type: raw.type,
4666
4719
  code: raw.code
@@ -4681,7 +4734,7 @@ function fromKintoneGeneralNotification(raw) {
4681
4734
  };
4682
4735
  return result;
4683
4736
  }
4684
- function fromKintonePerRecordTarget(raw) {
4737
+ function fromKintoneTarget(raw) {
4685
4738
  const result = { entity: fromKintoneEntity$2(raw.entity) };
4686
4739
  if (raw.includeSubs !== void 0) return {
4687
4740
  ...result,
@@ -4693,29 +4746,27 @@ function fromKintonePerRecordNotification(raw) {
4693
4746
  return {
4694
4747
  filterCond: raw.filterCond,
4695
4748
  title: raw.title,
4696
- targets: raw.targets.map(fromKintonePerRecordTarget)
4697
- };
4698
- }
4699
- function fromKintoneReminderTarget(raw) {
4700
- const result = { entity: fromKintoneEntity$2(raw.entity) };
4701
- if (raw.includeSubs !== void 0) return {
4702
- ...result,
4703
- includeSubs: raw.includeSubs
4749
+ targets: raw.targets.map(fromKintoneTarget)
4704
4750
  };
4705
- return result;
4706
4751
  }
4707
4752
  function fromKintoneReminderNotification(raw) {
4753
+ const daysLater = Number(raw.timing.daysLater);
4754
+ if (!Number.isFinite(daysLater)) throw new SystemError(SystemErrorCode.ExternalApiError, `Unexpected non-numeric daysLater from kintone API: ${raw.timing.daysLater}`);
4708
4755
  const result = {
4709
4756
  code: raw.timing.code,
4710
- daysLater: Number(raw.timing.daysLater),
4757
+ daysLater,
4711
4758
  filterCond: raw.filterCond,
4712
4759
  title: raw.title,
4713
- targets: raw.targets.map(fromKintoneReminderTarget)
4714
- };
4715
- if ("hoursLater" in raw.timing) return {
4716
- ...result,
4717
- hoursLater: Number(raw.timing.hoursLater)
4760
+ targets: raw.targets.map(fromKintoneTarget)
4718
4761
  };
4762
+ if ("hoursLater" in raw.timing) {
4763
+ const hoursLater = Number(raw.timing.hoursLater);
4764
+ if (!Number.isFinite(hoursLater)) throw new SystemError(SystemErrorCode.ExternalApiError, `Unexpected non-numeric hoursLater from kintone API: ${raw.timing.hoursLater}`);
4765
+ return {
4766
+ ...result,
4767
+ hoursLater
4768
+ };
4769
+ }
4719
4770
  if ("time" in raw.timing) return {
4720
4771
  ...result,
4721
4772
  time: raw.timing.time
@@ -4740,7 +4791,7 @@ function toKintoneGeneralNotification(notification) {
4740
4791
  if (notification.includeSubs !== void 0) result.includeSubs = notification.includeSubs;
4741
4792
  return result;
4742
4793
  }
4743
- function toKintonePerRecordTarget(target) {
4794
+ function toKintoneTarget(target) {
4744
4795
  const result = { entity: toKintoneEntity$2(target.entity) };
4745
4796
  if (target.includeSubs !== void 0) result.includeSubs = target.includeSubs;
4746
4797
  return result;
@@ -4749,14 +4800,9 @@ function toKintonePerRecordNotification(notification) {
4749
4800
  return {
4750
4801
  filterCond: notification.filterCond,
4751
4802
  title: notification.title,
4752
- targets: notification.targets.map(toKintonePerRecordTarget)
4803
+ targets: notification.targets.map(toKintoneTarget)
4753
4804
  };
4754
4805
  }
4755
- function toKintoneReminderTarget(target) {
4756
- const result = { entity: toKintoneEntity$2(target.entity) };
4757
- if (target.includeSubs !== void 0) result.includeSubs = target.includeSubs;
4758
- return result;
4759
- }
4760
4806
  function toKintoneReminderNotification(notification) {
4761
4807
  if (notification.hoursLater !== void 0 && notification.time !== void 0) throw new SystemError(SystemErrorCode.ExternalApiError, `Reminder notification "${notification.code}" must not have both "hoursLater" and "time"`);
4762
4808
  const timing = {
@@ -4769,7 +4815,7 @@ function toKintoneReminderNotification(notification) {
4769
4815
  timing,
4770
4816
  filterCond: notification.filterCond,
4771
4817
  title: notification.title,
4772
- targets: notification.targets.map(toKintoneReminderTarget)
4818
+ targets: notification.targets.map(toKintoneTarget)
4773
4819
  };
4774
4820
  }
4775
4821
  var KintoneNotificationConfigurator = class {
@@ -4966,7 +5012,7 @@ const VALID_ASSIGNEE_TYPES$1 = new Set([
4966
5012
  "ALL",
4967
5013
  "ANY"
4968
5014
  ]);
4969
- const VALID_ENTITY_TYPES$4 = new Set([
5015
+ const VALID_ENTITY_TYPES$3 = new Set([
4970
5016
  "USER",
4971
5017
  "GROUP",
4972
5018
  "ORGANIZATION",
@@ -4976,7 +5022,7 @@ const VALID_ENTITY_TYPES$4 = new Set([
4976
5022
  ]);
4977
5023
  const VALID_ACTION_TYPES$1 = new Set(["PRIMARY", "SECONDARY"]);
4978
5024
  function fromKintoneEntity$1(raw) {
4979
- if (!VALID_ENTITY_TYPES$4.has(raw.entity.type)) throw new SystemError(SystemErrorCode.ExternalApiError, `Unexpected entity type from kintone API: ${raw.entity.type}`);
5025
+ if (!VALID_ENTITY_TYPES$3.has(raw.entity.type)) throw new SystemError(SystemErrorCode.ExternalApiError, `Unexpected entity type from kintone API: ${raw.entity.type}`);
4980
5026
  const result = { type: raw.entity.type };
4981
5027
  const withCode = raw.entity.code !== void 0 ? {
4982
5028
  ...result,
@@ -5118,14 +5164,14 @@ function createProcessManagementCliContainer(config) {
5118
5164
 
5119
5165
  //#endregion
5120
5166
  //#region src/core/adapters/kintone/recordPermissionConfigurator.ts
5121
- const VALID_ENTITY_TYPES$3 = new Set([
5167
+ const VALID_ENTITY_TYPES$2 = new Set([
5122
5168
  "USER",
5123
5169
  "GROUP",
5124
5170
  "ORGANIZATION",
5125
5171
  "FIELD_ENTITY"
5126
5172
  ]);
5127
5173
  function fromKintoneEntity(raw) {
5128
- if (!VALID_ENTITY_TYPES$3.has(raw.entity.type)) throw new SystemError(SystemErrorCode.ExternalApiError, `Unexpected entity type from kintone API: ${raw.entity.type}`);
5174
+ if (!VALID_ENTITY_TYPES$2.has(raw.entity.type)) throw new SystemError(SystemErrorCode.ExternalApiError, `Unexpected entity type from kintone API: ${raw.entity.type}`);
5129
5175
  return {
5130
5176
  entity: {
5131
5177
  type: raw.entity.type,
@@ -5220,8 +5266,8 @@ function createRecordPermissionCliContainer(config) {
5220
5266
  }
5221
5267
 
5222
5268
  //#endregion
5223
- //#region src/core/adapters/kintone/reportConfigurator.ts
5224
- const VALID_CHART_TYPES$1 = new Set([
5269
+ //#region src/core/domain/report/valueObject.ts
5270
+ const CHART_TYPES = [
5225
5271
  "BAR",
5226
5272
  "COLUMN",
5227
5273
  "PIE",
@@ -5231,13 +5277,21 @@ const VALID_CHART_TYPES$1 = new Set([
5231
5277
  "AREA",
5232
5278
  "SPLINE",
5233
5279
  "SPLINE_AREA"
5234
- ]);
5235
- const VALID_CHART_MODES$1 = new Set([
5280
+ ];
5281
+ const VALID_CHART_TYPES = new Set(CHART_TYPES);
5282
+ function isChartType(value) {
5283
+ return VALID_CHART_TYPES.has(value);
5284
+ }
5285
+ const CHART_MODES = [
5236
5286
  "NORMAL",
5237
5287
  "STACKED",
5238
5288
  "PERCENTAGE"
5239
- ]);
5240
- const VALID_GROUP_PERS = new Set([
5289
+ ];
5290
+ const VALID_CHART_MODES = new Set(CHART_MODES);
5291
+ function isChartMode(value) {
5292
+ return VALID_CHART_MODES.has(value);
5293
+ }
5294
+ const GROUP_PERS = [
5241
5295
  "YEAR",
5242
5296
  "QUARTER",
5243
5297
  "MONTH",
@@ -5245,38 +5299,78 @@ const VALID_GROUP_PERS = new Set([
5245
5299
  "DAY",
5246
5300
  "HOUR",
5247
5301
  "MINUTE"
5248
- ]);
5249
- const VALID_AGGREGATION_TYPES$1 = new Set([
5302
+ ];
5303
+ const VALID_GROUP_PERS = new Set(GROUP_PERS);
5304
+ function isGroupPer(value) {
5305
+ return VALID_GROUP_PERS.has(value);
5306
+ }
5307
+ const AGGREGATION_TYPES = [
5250
5308
  "COUNT",
5251
5309
  "SUM",
5252
5310
  "AVERAGE",
5253
5311
  "MAX",
5254
5312
  "MIN"
5255
- ]);
5256
- const VALID_SORT_BYS = new Set([
5313
+ ];
5314
+ const VALID_AGGREGATION_TYPES = new Set(AGGREGATION_TYPES);
5315
+ function isAggregationType(value) {
5316
+ return VALID_AGGREGATION_TYPES.has(value);
5317
+ }
5318
+ const SORT_BYS = [
5257
5319
  "TOTAL",
5258
5320
  "GROUP1",
5259
5321
  "GROUP2",
5260
5322
  "GROUP3"
5261
- ]);
5262
- const VALID_SORT_ORDERS = new Set(["ASC", "DESC"]);
5263
- const VALID_PERIODIC_REPORT_EVERYS = new Set([
5323
+ ];
5324
+ const VALID_SORT_BYS = new Set(SORT_BYS);
5325
+ function isSortBy(value) {
5326
+ return VALID_SORT_BYS.has(value);
5327
+ }
5328
+ const SORT_ORDERS = ["ASC", "DESC"];
5329
+ const VALID_SORT_ORDERS = new Set(SORT_ORDERS);
5330
+ function isSortOrder(value) {
5331
+ return VALID_SORT_ORDERS.has(value);
5332
+ }
5333
+ const PERIODIC_REPORT_EVERYS = [
5264
5334
  "YEAR",
5265
5335
  "QUARTER",
5266
5336
  "MONTH",
5267
5337
  "WEEK",
5268
5338
  "DAY",
5269
5339
  "HOUR"
5270
- ]);
5271
- const VALID_PERIODIC_REPORT_PATTERNS = new Set([
5340
+ ];
5341
+ const VALID_PERIODIC_REPORT_EVERYS = new Set(PERIODIC_REPORT_EVERYS);
5342
+ function isPeriodicReportEvery(value) {
5343
+ return VALID_PERIODIC_REPORT_EVERYS.has(value);
5344
+ }
5345
+ const PERIODIC_REPORT_PATTERNS = [
5272
5346
  "JAN_APR_JUL_OCT",
5273
5347
  "FEB_MAY_AUG_NOV",
5274
5348
  "MAR_JUN_SEP_DEC"
5275
- ]);
5349
+ ];
5350
+ const VALID_PERIODIC_REPORT_PATTERNS = new Set(PERIODIC_REPORT_PATTERNS);
5351
+ function isPeriodicReportPattern(value) {
5352
+ return VALID_PERIODIC_REPORT_PATTERNS.has(value);
5353
+ }
5354
+ const DAYS_OF_WEEK = [
5355
+ "SUNDAY",
5356
+ "MONDAY",
5357
+ "TUESDAY",
5358
+ "WEDNESDAY",
5359
+ "THURSDAY",
5360
+ "FRIDAY",
5361
+ "SATURDAY"
5362
+ ];
5363
+ const VALID_DAYS_OF_WEEK = new Set(DAYS_OF_WEEK);
5364
+ function isDayOfWeek(value) {
5365
+ return VALID_DAYS_OF_WEEK.has(value);
5366
+ }
5367
+
5368
+ //#endregion
5369
+ //#region src/core/adapters/kintone/reportConfigurator.ts
5276
5370
  function fromKintoneGroup(raw) {
5277
5371
  const result = { code: raw.code };
5278
5372
  if (raw.per !== void 0) {
5279
- if (!VALID_GROUP_PERS.has(raw.per)) throw new SystemError(SystemErrorCode.ExternalApiError, `Unexpected group per value from kintone API: ${raw.per}`);
5373
+ if (!isGroupPer(raw.per)) throw new SystemError(SystemErrorCode.ExternalApiError, `Unexpected group per value from kintone API: ${raw.per}`);
5280
5374
  return {
5281
5375
  ...result,
5282
5376
  per: raw.per
@@ -5285,7 +5379,7 @@ function fromKintoneGroup(raw) {
5285
5379
  return result;
5286
5380
  }
5287
5381
  function fromKintoneAggregation(raw) {
5288
- if (!VALID_AGGREGATION_TYPES$1.has(raw.type)) throw new SystemError(SystemErrorCode.ExternalApiError, `Unexpected aggregation type value from kintone API: ${raw.type}`);
5382
+ if (!isAggregationType(raw.type)) throw new SystemError(SystemErrorCode.ExternalApiError, `Unexpected aggregation type value from kintone API: ${raw.type}`);
5289
5383
  const result = { type: raw.type };
5290
5384
  if (raw.code !== void 0) return {
5291
5385
  ...result,
@@ -5294,30 +5388,37 @@ function fromKintoneAggregation(raw) {
5294
5388
  return result;
5295
5389
  }
5296
5390
  function fromKintoneSort(raw) {
5297
- if (!VALID_SORT_BYS.has(raw.by)) throw new SystemError(SystemErrorCode.ExternalApiError, `Unexpected sort by value from kintone API: ${raw.by}`);
5298
- if (!VALID_SORT_ORDERS.has(raw.order)) throw new SystemError(SystemErrorCode.ExternalApiError, `Unexpected sort order value from kintone API: ${raw.order}`);
5391
+ if (!isSortBy(raw.by)) throw new SystemError(SystemErrorCode.ExternalApiError, `Unexpected sort by value from kintone API: ${raw.by}`);
5392
+ if (!isSortOrder(raw.order)) throw new SystemError(SystemErrorCode.ExternalApiError, `Unexpected sort order value from kintone API: ${raw.order}`);
5299
5393
  return {
5300
5394
  by: raw.by,
5301
5395
  order: raw.order
5302
5396
  };
5303
5397
  }
5304
5398
  function fromKintonePeriodicReportPeriod(raw) {
5305
- if (!VALID_PERIODIC_REPORT_EVERYS.has(raw.every)) throw new SystemError(SystemErrorCode.ExternalApiError, `Unexpected periodicReport every value from kintone API: ${raw.every}`);
5306
- if (raw.pattern !== void 0 && !VALID_PERIODIC_REPORT_PATTERNS.has(raw.pattern)) throw new SystemError(SystemErrorCode.ExternalApiError, `Unexpected periodicReport pattern value from kintone API: ${raw.pattern}`);
5399
+ if (!isPeriodicReportEvery(raw.every)) throw new SystemError(SystemErrorCode.ExternalApiError, `Unexpected periodicReport every value from kintone API: ${raw.every}`);
5400
+ if (raw.pattern !== void 0 && !isPeriodicReportPattern(raw.pattern)) throw new SystemError(SystemErrorCode.ExternalApiError, `Unexpected periodicReport pattern value from kintone API: ${raw.pattern}`);
5307
5401
  let dayOfMonth;
5308
5402
  if (raw.dayOfMonth !== void 0) if (String(raw.dayOfMonth) === "END_OF_MONTH") dayOfMonth = "END_OF_MONTH";
5309
5403
  else {
5310
5404
  const parsed = Number(raw.dayOfMonth);
5311
- if (Number.isNaN(parsed)) throw new SystemError(SystemErrorCode.ExternalApiError, `Unexpected periodicReport dayOfMonth value from kintone API: ${String(raw.dayOfMonth)}`);
5405
+ if (!Number.isFinite(parsed) || !Number.isInteger(parsed)) throw new SystemError(SystemErrorCode.ExternalApiError, `Unexpected periodicReport dayOfMonth value from kintone API: ${String(raw.dayOfMonth)}`);
5312
5406
  dayOfMonth = parsed;
5313
5407
  }
5314
5408
  return {
5315
5409
  every: raw.every,
5316
- ...raw.month !== void 0 ? { month: Number(raw.month) } : {},
5317
- ...raw.pattern !== void 0 ? { pattern: raw.pattern } : {},
5410
+ ...raw.month !== void 0 ? { month: (() => {
5411
+ const m = Number(raw.month);
5412
+ if (!Number.isFinite(m)) throw new SystemError(SystemErrorCode.ExternalApiError, `Unexpected non-numeric month from kintone API: ${raw.month}`);
5413
+ return m;
5414
+ })() } : {},
5415
+ ...raw.pattern !== void 0 && isPeriodicReportPattern(raw.pattern) ? { pattern: raw.pattern } : {},
5318
5416
  ...dayOfMonth !== void 0 ? { dayOfMonth } : {},
5319
5417
  ...raw.time !== void 0 ? { time: raw.time } : {},
5320
- ...raw.dayOfWeek !== void 0 ? { dayOfWeek: raw.dayOfWeek } : {},
5418
+ ...raw.dayOfWeek !== void 0 ? (() => {
5419
+ if (!isDayOfWeek(raw.dayOfWeek)) throw new SystemError(SystemErrorCode.ExternalApiError, `Unexpected periodicReport dayOfWeek value from kintone API: ${raw.dayOfWeek}`);
5420
+ return { dayOfWeek: raw.dayOfWeek };
5421
+ })() : {},
5321
5422
  ...raw.minute !== void 0 ? { minute: raw.minute } : {}
5322
5423
  };
5323
5424
  }
@@ -5328,12 +5429,16 @@ function fromKintonePeriodicReport(raw) {
5328
5429
  };
5329
5430
  }
5330
5431
  function fromKintoneReportConfig(raw) {
5331
- if (!VALID_CHART_TYPES$1.has(raw.chartType)) throw new SystemError(SystemErrorCode.ExternalApiError, `Unexpected chartType value from kintone API: ${raw.chartType}`);
5332
- if (raw.chartMode !== void 0 && raw.chartMode !== "" && !VALID_CHART_MODES$1.has(raw.chartMode)) throw new SystemError(SystemErrorCode.ExternalApiError, `Unexpected chartMode value from kintone API: ${raw.chartMode}`);
5432
+ if (!isChartType(raw.chartType)) throw new SystemError(SystemErrorCode.ExternalApiError, `Unexpected chartType value from kintone API: ${raw.chartType}`);
5433
+ if (raw.chartMode !== void 0 && raw.chartMode !== "" && !isChartMode(raw.chartMode)) throw new SystemError(SystemErrorCode.ExternalApiError, `Unexpected chartMode value from kintone API: ${raw.chartMode}`);
5333
5434
  const result = {
5334
5435
  chartType: raw.chartType,
5335
- ...raw.chartMode !== void 0 && raw.chartMode !== "" && VALID_CHART_MODES$1.has(raw.chartMode) ? { chartMode: raw.chartMode } : {},
5336
- index: Number(raw.index),
5436
+ ...raw.chartMode !== void 0 && raw.chartMode !== "" && isChartMode(raw.chartMode) ? { chartMode: raw.chartMode } : {},
5437
+ index: (() => {
5438
+ const idx = Number(raw.index);
5439
+ if (!Number.isFinite(idx)) throw new SystemError(SystemErrorCode.ExternalApiError, `Unexpected non-numeric index from kintone API: ${raw.index}`);
5440
+ return idx;
5441
+ })(),
5337
5442
  name: raw.name,
5338
5443
  groups: raw.groups.map(fromKintoneGroup),
5339
5444
  aggregations: raw.aggregations.map(fromKintoneAggregation),
@@ -5366,7 +5471,7 @@ function toKintonePeriodicReportPeriod(period) {
5366
5471
  const result = { every: period.every };
5367
5472
  if (period.month !== void 0) result.month = String(period.month);
5368
5473
  if (period.pattern !== void 0) result.pattern = period.pattern;
5369
- if (period.dayOfMonth !== void 0) result.dayOfMonth = period.dayOfMonth;
5474
+ if (period.dayOfMonth !== void 0) result.dayOfMonth = typeof period.dayOfMonth === "number" ? String(period.dayOfMonth) : period.dayOfMonth;
5370
5475
  if (period.time !== void 0) result.time = period.time;
5371
5476
  if (period.dayOfWeek !== void 0) result.dayOfWeek = period.dayOfWeek;
5372
5477
  if (period.minute !== void 0) result.minute = period.minute;
@@ -5463,16 +5568,31 @@ const VIEW_TYPES = [
5463
5568
  "CUSTOM"
5464
5569
  ];
5465
5570
  const VALID_VIEW_TYPES = new Set(VIEW_TYPES);
5571
+ function isViewType(value) {
5572
+ return VALID_VIEW_TYPES.has(value);
5573
+ }
5466
5574
  const DEVICE_TYPES = ["DESKTOP", "ANY"];
5467
5575
  const VALID_DEVICE_TYPES = new Set(DEVICE_TYPES);
5576
+ function isDeviceType(value) {
5577
+ return VALID_DEVICE_TYPES.has(value);
5578
+ }
5468
5579
 
5469
5580
  //#endregion
5470
5581
  //#region src/core/adapters/kintone/viewConfigurator.ts
5471
5582
  function fromKintoneView(name, raw) {
5472
- if (!VALID_VIEW_TYPES.has(raw.type)) throw new SystemError(SystemErrorCode.ExternalApiError, `Unexpected view type from kintone API: ${raw.type}`);
5583
+ if (!isViewType(raw.type)) throw new SystemError(SystemErrorCode.ExternalApiError, `Unexpected view type from kintone API: ${raw.type}`);
5584
+ let device;
5585
+ if (raw.device !== void 0) {
5586
+ if (!isDeviceType(raw.device)) throw new SystemError(SystemErrorCode.ExternalApiError, `Unexpected device type from kintone API: ${raw.device}`);
5587
+ device = raw.device;
5588
+ }
5473
5589
  return {
5474
5590
  type: raw.type,
5475
- index: typeof raw.index === "string" ? Number(raw.index) : raw.index,
5591
+ index: (() => {
5592
+ const idx = typeof raw.index === "string" ? Number(raw.index) : raw.index;
5593
+ if (!Number.isFinite(idx)) throw new SystemError(SystemErrorCode.ExternalApiError, `Unexpected non-numeric index from kintone API: ${raw.index}`);
5594
+ return idx;
5595
+ })(),
5476
5596
  name,
5477
5597
  ...raw.builtinType !== void 0 && { builtinType: raw.builtinType },
5478
5598
  ...raw.fields !== void 0 && { fields: raw.fields },
@@ -5480,10 +5600,7 @@ function fromKintoneView(name, raw) {
5480
5600
  ...raw.title !== void 0 && { title: raw.title },
5481
5601
  ...raw.html !== void 0 && { html: raw.html },
5482
5602
  ...raw.pager !== void 0 && { pager: raw.pager },
5483
- ...raw.device !== void 0 && { device: (() => {
5484
- if (!VALID_DEVICE_TYPES.has(raw.device)) throw new SystemError(SystemErrorCode.ExternalApiError, `Unexpected device type from kintone API: ${raw.device}`);
5485
- return raw.device;
5486
- })() },
5603
+ ...device !== void 0 && { device },
5487
5604
  ...raw.filterCond !== void 0 && { filterCond: raw.filterCond },
5488
5605
  ...raw.sort !== void 0 && { sort: raw.sort }
5489
5606
  };
@@ -5915,11 +6032,7 @@ function serializeConfig(config) {
5915
6032
  return result;
5916
6033
  }
5917
6034
  const GeneralSettingsConfigSerializer = { serialize: (config) => {
5918
- return stringify(serializeConfig(config), {
5919
- lineWidth: 0,
5920
- defaultKeyType: "PLAIN",
5921
- defaultStringType: "PLAIN"
5922
- });
6035
+ return serializeToYaml(serializeConfig(config));
5923
6036
  } };
5924
6037
 
5925
6038
  //#endregion
@@ -5958,7 +6071,7 @@ function serializeGeneralNotification(notification) {
5958
6071
  if (notification.includeSubs !== void 0) result.includeSubs = notification.includeSubs;
5959
6072
  return result;
5960
6073
  }
5961
- function serializePerRecordTarget(target) {
6074
+ function serializeTarget(target) {
5962
6075
  const result = { entity: serializeEntity$1(target.entity) };
5963
6076
  if (target.includeSubs !== void 0) result.includeSubs = target.includeSubs;
5964
6077
  return result;
@@ -5967,21 +6080,16 @@ function serializePerRecordNotification(notification) {
5967
6080
  return {
5968
6081
  filterCond: notification.filterCond,
5969
6082
  title: notification.title,
5970
- targets: notification.targets.map(serializePerRecordTarget)
6083
+ targets: notification.targets.map(serializeTarget)
5971
6084
  };
5972
6085
  }
5973
- function serializeReminderTarget(target) {
5974
- const result = { entity: serializeEntity$1(target.entity) };
5975
- if (target.includeSubs !== void 0) result.includeSubs = target.includeSubs;
5976
- return result;
5977
- }
5978
6086
  function serializeReminderNotification(notification) {
5979
6087
  const result = {
5980
6088
  code: notification.code,
5981
6089
  daysLater: notification.daysLater,
5982
6090
  filterCond: notification.filterCond,
5983
6091
  title: notification.title,
5984
- targets: notification.targets.map(serializeReminderTarget)
6092
+ targets: notification.targets.map(serializeTarget)
5985
6093
  };
5986
6094
  if (notification.hoursLater !== void 0) result.hoursLater = notification.hoursLater;
5987
6095
  if (notification.time !== void 0) result.time = notification.time;
@@ -5998,11 +6106,7 @@ const NotificationConfigSerializer = { serialize: (config) => {
5998
6106
  timezone: config.reminder.timezone,
5999
6107
  notifications: config.reminder.notifications.map(serializeReminderNotification)
6000
6108
  };
6001
- return stringify(serialized, {
6002
- lineWidth: 0,
6003
- defaultKeyType: "PLAIN",
6004
- defaultStringType: "PLAIN"
6005
- });
6109
+ return serializeToYaml(serialized);
6006
6110
  } };
6007
6111
 
6008
6112
  //#endregion
@@ -6037,15 +6141,11 @@ async function saveNotification({ container, input }) {
6037
6141
  //#endregion
6038
6142
  //#region src/core/domain/plugin/services/configSerializer.ts
6039
6143
  const PluginConfigSerializer = { serialize: (config) => {
6040
- return stringify({ plugins: config.plugins.map((plugin) => ({
6144
+ return serializeToYaml({ plugins: config.plugins.map((plugin) => ({
6041
6145
  id: plugin.id,
6042
6146
  name: plugin.name,
6043
6147
  enabled: plugin.enabled
6044
- })) }, {
6045
- lineWidth: 0,
6046
- defaultKeyType: "PLAIN",
6047
- defaultStringType: "PLAIN"
6048
- });
6148
+ })) });
6049
6149
  } };
6050
6150
 
6051
6151
  //#endregion
@@ -6081,7 +6181,7 @@ const ProcessManagementConfigSerializer = { serialize: (config) => {
6081
6181
  entities: state.assignee.entities.map(serializeProcessEntity)
6082
6182
  }
6083
6183
  };
6084
- return stringify({
6184
+ return serializeToYaml({
6085
6185
  enable: config.enable,
6086
6186
  states: serializedStates,
6087
6187
  actions: config.actions.map((action) => {
@@ -6095,10 +6195,6 @@ const ProcessManagementConfigSerializer = { serialize: (config) => {
6095
6195
  if (action.executableUser !== void 0) serializedAction.executableUser = { entities: action.executableUser.entities.map(serializeProcessEntity) };
6096
6196
  return serializedAction;
6097
6197
  })
6098
- }, {
6099
- lineWidth: 0,
6100
- defaultKeyType: "PLAIN",
6101
- defaultStringType: "PLAIN"
6102
6198
  });
6103
6199
  } };
6104
6200
 
@@ -6133,14 +6229,10 @@ function serializeRecordRightEntity(entity) {
6133
6229
  };
6134
6230
  }
6135
6231
  const RecordPermissionConfigSerializer = { serialize: (config) => {
6136
- return stringify({ rights: config.rights.map((right) => ({
6232
+ return serializeToYaml({ rights: config.rights.map((right) => ({
6137
6233
  filterCond: right.filterCond,
6138
6234
  entities: right.entities.map(serializeRecordRightEntity)
6139
- })) }, {
6140
- lineWidth: 0,
6141
- defaultKeyType: "PLAIN",
6142
- defaultStringType: "PLAIN"
6143
- });
6235
+ })) });
6144
6236
  } };
6145
6237
 
6146
6238
  //#endregion
@@ -6211,11 +6303,7 @@ const ReportConfigSerializer = { serialize: (config) => {
6211
6303
  const reports = {};
6212
6304
  for (const [name, reportConfig] of Object.entries(config.reports)) reports[name] = serializeReportConfig(reportConfig);
6213
6305
  serialized.reports = reports;
6214
- return stringify(serialized, {
6215
- lineWidth: 0,
6216
- defaultKeyType: "PLAIN",
6217
- defaultStringType: "PLAIN"
6218
- });
6306
+ return serializeToYaml(serialized);
6219
6307
  } };
6220
6308
 
6221
6309
  //#endregion
@@ -6299,11 +6387,7 @@ function serializeViewConfig(config) {
6299
6387
  const ViewConfigSerializer = { serialize: (config) => {
6300
6388
  const serialized = {};
6301
6389
  for (const [name, viewConfig] of Object.entries(config.views)) serialized[name] = serializeViewConfig(viewConfig);
6302
- return stringify({ views: serialized }, {
6303
- lineWidth: 0,
6304
- defaultKeyType: "PLAIN",
6305
- defaultStringType: "PLAIN"
6306
- });
6390
+ return serializeToYaml({ views: serialized });
6307
6391
  } };
6308
6392
 
6309
6393
  //#endregion
@@ -6660,68 +6744,57 @@ var init_default = define({
6660
6744
 
6661
6745
  //#endregion
6662
6746
  //#region src/core/domain/notification/services/configParser.ts
6663
- const VALID_ENTITY_TYPES$2 = new Set([
6664
- "USER",
6665
- "GROUP",
6666
- "ORGANIZATION",
6667
- "FIELD_ENTITY"
6668
- ]);
6669
6747
  function parseEntity$1(raw, context) {
6670
6748
  if (!isRecord(raw)) throw new BusinessRuleError(NotificationErrorCode.NtInvalidConfigStructure, `${context}: entity must be an object`);
6671
- const obj = raw;
6672
- if (typeof obj.type !== "string" || !VALID_ENTITY_TYPES$2.has(obj.type)) throw new BusinessRuleError(NotificationErrorCode.NtInvalidEntityType, `${context}: entity has invalid type: ${String(obj.type)}. Must be USER, GROUP, ORGANIZATION, or FIELD_ENTITY`);
6673
- if (typeof obj.code !== "string" || obj.code.length === 0) throw new BusinessRuleError(NotificationErrorCode.NtEmptyEntityCode, `${context}: entity must have a non-empty "code" property`);
6749
+ if (typeof raw.type !== "string" || !isNotificationEntityType(raw.type)) throw new BusinessRuleError(NotificationErrorCode.NtInvalidEntityType, `${context}: entity has invalid type: ${String(raw.type)}. Must be USER, GROUP, ORGANIZATION, or FIELD_ENTITY`);
6750
+ if (typeof raw.code !== "string" || raw.code.length === 0) throw new BusinessRuleError(NotificationErrorCode.NtEmptyEntityCode, `${context}: entity must have a non-empty "code" property`);
6674
6751
  return {
6675
- type: obj.type,
6676
- code: obj.code
6752
+ type: raw.type,
6753
+ code: raw.code
6677
6754
  };
6678
6755
  }
6679
6756
  function parseGeneralNotification(raw, index) {
6680
6757
  if (!isRecord(raw)) throw new BusinessRuleError(NotificationErrorCode.NtInvalidConfigStructure, `General notification at index ${index} must be an object`);
6681
- const obj = raw;
6682
6758
  const result = {
6683
- entity: parseEntity$1(obj.entity, `General notification at index ${index}`),
6684
- recordAdded: Boolean(obj.recordAdded),
6685
- recordEdited: Boolean(obj.recordEdited),
6686
- commentAdded: Boolean(obj.commentAdded),
6687
- statusChanged: Boolean(obj.statusChanged),
6688
- fileImported: Boolean(obj.fileImported)
6689
- };
6690
- if (obj.includeSubs !== void 0 && obj.includeSubs !== null) return {
6759
+ entity: parseEntity$1(raw.entity, `General notification at index ${index}`),
6760
+ recordAdded: Boolean(raw.recordAdded),
6761
+ recordEdited: Boolean(raw.recordEdited),
6762
+ commentAdded: Boolean(raw.commentAdded),
6763
+ statusChanged: Boolean(raw.statusChanged),
6764
+ fileImported: Boolean(raw.fileImported)
6765
+ };
6766
+ if (raw.includeSubs !== void 0 && raw.includeSubs !== null) return {
6691
6767
  ...result,
6692
- includeSubs: Boolean(obj.includeSubs)
6768
+ includeSubs: Boolean(raw.includeSubs)
6693
6769
  };
6694
6770
  return result;
6695
6771
  }
6696
6772
  function parseGeneralConfig(raw) {
6697
6773
  if (!isRecord(raw)) throw new BusinessRuleError(NotificationErrorCode.NtInvalidConfigStructure, "\"general\" must be an object");
6698
- const obj = raw;
6699
- if (!Array.isArray(obj.notifications)) throw new BusinessRuleError(NotificationErrorCode.NtInvalidConfigStructure, "\"general\" must have a \"notifications\" array");
6700
- const notifications = obj.notifications.map((item, i) => parseGeneralNotification(item, i));
6774
+ if (!Array.isArray(raw.notifications)) throw new BusinessRuleError(NotificationErrorCode.NtInvalidConfigStructure, "\"general\" must have a \"notifications\" array");
6775
+ const notifications = raw.notifications.map((item, i) => parseGeneralNotification(item, i));
6701
6776
  return {
6702
- notifyToCommenter: Boolean(obj.notifyToCommenter),
6777
+ notifyToCommenter: Boolean(raw.notifyToCommenter),
6703
6778
  notifications
6704
6779
  };
6705
6780
  }
6706
- function parsePerRecordTarget(raw, index) {
6707
- if (!isRecord(raw)) throw new BusinessRuleError(NotificationErrorCode.NtInvalidConfigStructure, `Per-record target at index ${index} must be an object`);
6708
- const obj = raw;
6709
- const result = { entity: parseEntity$1(obj.entity, `Per-record target at index ${index}`) };
6710
- if (obj.includeSubs !== void 0 && obj.includeSubs !== null) return {
6781
+ function parseTarget(raw, index, context) {
6782
+ if (!isRecord(raw)) throw new BusinessRuleError(NotificationErrorCode.NtInvalidConfigStructure, `${context} target at index ${index} must be an object`);
6783
+ const result = { entity: parseEntity$1(raw.entity, `${context} target at index ${index}`) };
6784
+ if (raw.includeSubs !== void 0 && raw.includeSubs !== null) return {
6711
6785
  ...result,
6712
- includeSubs: Boolean(obj.includeSubs)
6786
+ includeSubs: Boolean(raw.includeSubs)
6713
6787
  };
6714
6788
  return result;
6715
6789
  }
6716
6790
  function parsePerRecordNotification(raw, index) {
6717
6791
  if (!isRecord(raw)) throw new BusinessRuleError(NotificationErrorCode.NtInvalidConfigStructure, `Per-record notification at index ${index} must be an object`);
6718
- const obj = raw;
6719
- if (!Array.isArray(obj.targets)) throw new BusinessRuleError(NotificationErrorCode.NtInvalidConfigStructure, `Per-record notification at index ${index} must have a "targets" array`);
6720
- const targets = obj.targets.map((item, i) => parsePerRecordTarget(item, i));
6721
- if (typeof obj.filterCond !== "string") throw new BusinessRuleError(NotificationErrorCode.NtMissingRequiredField, `Per-record notification at index ${index} must have a "filterCond" string property`);
6792
+ if (!Array.isArray(raw.targets)) throw new BusinessRuleError(NotificationErrorCode.NtInvalidConfigStructure, `Per-record notification at index ${index} must have a "targets" array`);
6793
+ const targets = raw.targets.map((item, i) => parseTarget(item, i, "Per-record"));
6794
+ if (typeof raw.filterCond !== "string") throw new BusinessRuleError(NotificationErrorCode.NtMissingRequiredField, `Per-record notification at index ${index} must have a "filterCond" string property`);
6722
6795
  return {
6723
- filterCond: obj.filterCond,
6724
- title: typeof obj.title === "string" ? obj.title : "",
6796
+ filterCond: raw.filterCond,
6797
+ title: typeof raw.title === "string" ? raw.title : "",
6725
6798
  targets
6726
6799
  };
6727
6800
  }
@@ -6729,63 +6802,50 @@ function parsePerRecordConfig(raw) {
6729
6802
  if (!Array.isArray(raw)) throw new BusinessRuleError(NotificationErrorCode.NtInvalidConfigStructure, "\"perRecord\" must be an array");
6730
6803
  return raw.map((item, i) => parsePerRecordNotification(item, i));
6731
6804
  }
6732
- function parseReminderTarget(raw, index) {
6733
- if (!isRecord(raw)) throw new BusinessRuleError(NotificationErrorCode.NtInvalidConfigStructure, `Reminder target at index ${index} must be an object`);
6734
- const obj = raw;
6735
- const result = { entity: parseEntity$1(obj.entity, `Reminder target at index ${index}`) };
6736
- if (obj.includeSubs !== void 0 && obj.includeSubs !== null) return {
6737
- ...result,
6738
- includeSubs: Boolean(obj.includeSubs)
6739
- };
6740
- return result;
6741
- }
6742
6805
  function parseReminderNotification(raw, index) {
6743
6806
  if (!isRecord(raw)) throw new BusinessRuleError(NotificationErrorCode.NtInvalidConfigStructure, `Reminder notification at index ${index} must be an object`);
6744
- const obj = raw;
6745
- if (typeof obj.code !== "string" || obj.code.length === 0) throw new BusinessRuleError(NotificationErrorCode.NtInvalidConfigStructure, `Reminder notification at index ${index} must have a non-empty "code" property`);
6746
- if (!Array.isArray(obj.targets)) throw new BusinessRuleError(NotificationErrorCode.NtInvalidConfigStructure, `Reminder notification at index ${index} must have a "targets" array`);
6747
- const targets = obj.targets.map((item, i) => parseReminderTarget(item, i));
6748
- if (typeof obj.daysLater !== "number") throw new BusinessRuleError(NotificationErrorCode.NtMissingRequiredField, `Reminder notification at index ${index} must have a "daysLater" number property`);
6749
- const hasHoursLater = obj.hoursLater !== void 0 && obj.hoursLater !== null;
6750
- const hasTime = obj.time !== void 0 && obj.time !== null;
6807
+ if (typeof raw.code !== "string" || raw.code.length === 0) throw new BusinessRuleError(NotificationErrorCode.NtInvalidConfigStructure, `Reminder notification at index ${index} must have a non-empty "code" property`);
6808
+ if (!Array.isArray(raw.targets)) throw new BusinessRuleError(NotificationErrorCode.NtInvalidConfigStructure, `Reminder notification at index ${index} must have a "targets" array`);
6809
+ const targets = raw.targets.map((item, i) => parseTarget(item, i, "Reminder"));
6810
+ if (typeof raw.daysLater !== "number") throw new BusinessRuleError(NotificationErrorCode.NtMissingRequiredField, `Reminder notification at index ${index} must have a "daysLater" property`);
6811
+ if (!Number.isInteger(raw.daysLater) || raw.daysLater < 0) throw new BusinessRuleError(NotificationErrorCode.NtInvalidDaysLater, `Reminder notification at index ${index} has invalid "daysLater": ${raw.daysLater}. Must be a non-negative integer`);
6812
+ const hasHoursLater = raw.hoursLater !== void 0 && raw.hoursLater !== null;
6813
+ const hasTime = raw.time !== void 0 && raw.time !== null;
6751
6814
  if (hasHoursLater && hasTime) throw new BusinessRuleError(NotificationErrorCode.NtConflictingTimingFields, `Reminder notification at index ${index} must not have both "hoursLater" and "time"`);
6815
+ if (hasHoursLater && (typeof raw.hoursLater !== "number" || !Number.isInteger(raw.hoursLater) || raw.hoursLater < 0)) throw new BusinessRuleError(NotificationErrorCode.NtInvalidHoursLater, `Reminder notification at index ${index} has invalid "hoursLater": ${String(raw.hoursLater)}. Must be a non-negative integer`);
6816
+ if (hasTime && typeof raw.time !== "string") throw new BusinessRuleError(NotificationErrorCode.NtInvalidConfigStructure, `Reminder notification at index ${index} has non-string "time": ${String(raw.time)}`);
6752
6817
  const result = {
6753
- code: obj.code,
6754
- daysLater: obj.daysLater,
6755
- filterCond: typeof obj.filterCond === "string" ? obj.filterCond : "",
6756
- title: typeof obj.title === "string" ? obj.title : "",
6818
+ code: raw.code,
6819
+ daysLater: raw.daysLater,
6820
+ filterCond: typeof raw.filterCond === "string" ? raw.filterCond : "",
6821
+ title: typeof raw.title === "string" ? raw.title : "",
6757
6822
  targets
6758
6823
  };
6759
- if (hasHoursLater) return {
6824
+ if (hasHoursLater && typeof raw.hoursLater === "number") return {
6760
6825
  ...result,
6761
- hoursLater: typeof obj.hoursLater === "number" ? obj.hoursLater : 0
6826
+ hoursLater: raw.hoursLater
6762
6827
  };
6763
6828
  if (hasTime) return {
6764
6829
  ...result,
6765
- time: typeof obj.time === "string" ? obj.time : ""
6830
+ time: typeof raw.time === "string" ? raw.time : ""
6766
6831
  };
6767
6832
  return result;
6768
6833
  }
6769
6834
  function parseReminderConfig(raw) {
6770
6835
  if (!isRecord(raw)) throw new BusinessRuleError(NotificationErrorCode.NtInvalidConfigStructure, "\"reminder\" must be an object");
6771
- const obj = raw;
6772
- if (!Array.isArray(obj.notifications)) throw new BusinessRuleError(NotificationErrorCode.NtInvalidConfigStructure, "\"reminder\" must have a \"notifications\" array");
6773
- const notifications = obj.notifications.map((item, i) => parseReminderNotification(item, i));
6836
+ if (!Array.isArray(raw.notifications)) throw new BusinessRuleError(NotificationErrorCode.NtInvalidConfigStructure, "\"reminder\" must have a \"notifications\" array");
6837
+ const notifications = raw.notifications.map((item, i) => parseReminderNotification(item, i));
6774
6838
  return {
6775
- timezone: typeof obj.timezone === "string" ? obj.timezone : "Asia/Tokyo",
6839
+ timezone: typeof raw.timezone === "string" ? raw.timezone : "Asia/Tokyo",
6776
6840
  notifications
6777
6841
  };
6778
6842
  }
6779
6843
  const NotificationConfigParser = { parse: (rawText) => {
6780
- if (rawText.trim().length === 0) throw new BusinessRuleError(NotificationErrorCode.NtEmptyConfigText, "Notification config text is empty");
6781
- let parsed;
6782
- try {
6783
- parsed = parse(rawText);
6784
- } catch (error) {
6785
- throw new BusinessRuleError(NotificationErrorCode.NtInvalidConfigYaml, `Failed to parse YAML: ${error instanceof Error ? error.message : String(error)}`);
6786
- }
6787
- if (!isRecord(parsed)) throw new BusinessRuleError(NotificationErrorCode.NtInvalidConfigStructure, "Config must be a YAML object");
6788
- const obj = parsed;
6844
+ const obj = parseYamlConfig(rawText, {
6845
+ emptyConfigText: NotificationErrorCode.NtEmptyConfigText,
6846
+ invalidConfigYaml: NotificationErrorCode.NtInvalidConfigYaml,
6847
+ invalidConfigStructure: NotificationErrorCode.NtInvalidConfigStructure
6848
+ }, "Notification");
6789
6849
  const config = {};
6790
6850
  if (obj.general !== void 0) config.general = parseGeneralConfig(obj.general);
6791
6851
  if (obj.perRecord !== void 0) config.perRecord = parsePerRecordConfig(obj.perRecord);
@@ -7204,26 +7264,28 @@ var notification_default = define({
7204
7264
  //#region src/core/domain/plugin/services/configParser.ts
7205
7265
  function parsePluginEntry(raw, index) {
7206
7266
  if (!isRecord(raw)) throw new BusinessRuleError(PluginErrorCode.PlInvalidConfigStructure, `Plugin at index ${index} must be an object`);
7207
- const obj = raw;
7208
- if (typeof obj.id !== "string" || obj.id.length === 0) throw new BusinessRuleError(PluginErrorCode.PlEmptyPluginId, `Plugin at index ${index} must have a non-empty "id" property`);
7267
+ if (typeof raw.id !== "string" || raw.id.length === 0) throw new BusinessRuleError(PluginErrorCode.PlEmptyPluginId, `Plugin at index ${index} must have a non-empty "id" property`);
7268
+ if (raw.enabled !== void 0 && raw.enabled !== null && typeof raw.enabled !== "boolean") throw new BusinessRuleError(PluginErrorCode.PlInvalidConfigStructure, `Plugin at index ${index} has invalid "enabled": must be a boolean`);
7209
7269
  return {
7210
- id: obj.id,
7211
- name: typeof obj.name === "string" ? obj.name : "",
7212
- enabled: typeof obj.enabled === "boolean" ? obj.enabled : true
7270
+ id: raw.id,
7271
+ name: typeof raw.name === "string" ? raw.name : "",
7272
+ enabled: typeof raw.enabled === "boolean" ? raw.enabled : true
7213
7273
  };
7214
7274
  }
7215
7275
  const PluginConfigParser = { parse: (rawText) => {
7216
- if (rawText.trim().length === 0) throw new BusinessRuleError(PluginErrorCode.PlEmptyConfigText, "Plugin config text is empty");
7217
- let parsed;
7218
- try {
7219
- parsed = parse(rawText);
7220
- } catch (error) {
7221
- throw new BusinessRuleError(PluginErrorCode.PlInvalidConfigYaml, `Failed to parse YAML: ${error instanceof Error ? error.message : String(error)}`);
7222
- }
7223
- if (!isRecord(parsed)) throw new BusinessRuleError(PluginErrorCode.PlInvalidConfigStructure, "Config must be a YAML object");
7224
- const obj = parsed;
7276
+ const obj = parseYamlConfig(rawText, {
7277
+ emptyConfigText: PluginErrorCode.PlEmptyConfigText,
7278
+ invalidConfigYaml: PluginErrorCode.PlInvalidConfigYaml,
7279
+ invalidConfigStructure: PluginErrorCode.PlInvalidConfigStructure
7280
+ }, "Plugin");
7225
7281
  if (!Array.isArray(obj.plugins)) throw new BusinessRuleError(PluginErrorCode.PlInvalidConfigStructure, "Config must have a \"plugins\" array");
7226
- return { plugins: obj.plugins.map((item, i) => parsePluginEntry(item, i)) };
7282
+ const plugins = obj.plugins.map((item, i) => parsePluginEntry(item, i));
7283
+ const seenIds = /* @__PURE__ */ new Set();
7284
+ for (const plugin of plugins) {
7285
+ if (seenIds.has(plugin.id)) throw new BusinessRuleError(PluginErrorCode.PlDuplicatePluginId, `Duplicate plugin ID: "${plugin.id}"`);
7286
+ seenIds.add(plugin.id);
7287
+ }
7288
+ return { plugins };
7227
7289
  } };
7228
7290
 
7229
7291
  //#endregion
@@ -7447,85 +7509,77 @@ const VALID_ENTITY_TYPES$1 = new Set([
7447
7509
  const VALID_ACTION_TYPES = new Set(["PRIMARY", "SECONDARY"]);
7448
7510
  function parseProcessEntity(raw, index) {
7449
7511
  if (!isRecord(raw)) throw new BusinessRuleError(ProcessManagementErrorCode.PmInvalidConfigStructure, `Entity at index ${index} must be an object`);
7450
- const obj = raw;
7451
- if (typeof obj.type !== "string" || !VALID_ENTITY_TYPES$1.has(obj.type)) throw new BusinessRuleError(ProcessManagementErrorCode.PmInvalidEntityType, `Entity at index ${index} has invalid type: ${String(obj.type)}. Must be USER, GROUP, ORGANIZATION, FIELD_ENTITY, CREATOR, or CUSTOM_FIELD`);
7452
- const result = { type: obj.type };
7453
- const withCode = obj.code !== void 0 && obj.code !== null ? {
7512
+ const result = { type: parseEnum(raw.type, VALID_ENTITY_TYPES$1, ProcessManagementErrorCode.PmInvalidEntityType, `Entity at index ${index} has invalid type: ${String(raw.type)}. Must be USER, GROUP, ORGANIZATION, FIELD_ENTITY, CREATOR, or CUSTOM_FIELD`) };
7513
+ const withCode = raw.code !== void 0 && raw.code !== null ? {
7454
7514
  ...result,
7455
- code: String(obj.code)
7515
+ code: String(raw.code)
7456
7516
  } : result;
7457
- if (obj.includeSubs !== void 0 && obj.includeSubs !== null) return {
7517
+ if (raw.includeSubs !== void 0 && raw.includeSubs !== null) return {
7458
7518
  ...withCode,
7459
- includeSubs: Boolean(obj.includeSubs)
7519
+ includeSubs: parseStrictBoolean(raw.includeSubs, "includeSubs", `Entity at index ${index}`, ProcessManagementErrorCode.PmInvalidBooleanField)
7460
7520
  };
7461
7521
  return withCode;
7462
7522
  }
7463
7523
  function parseAssignee(raw, stateName) {
7464
7524
  if (!isRecord(raw)) throw new BusinessRuleError(ProcessManagementErrorCode.PmInvalidConfigStructure, `Assignee for state "${stateName}" must be an object`);
7465
- const obj = raw;
7466
- if (typeof obj.type !== "string" || !VALID_ASSIGNEE_TYPES.has(obj.type)) throw new BusinessRuleError(ProcessManagementErrorCode.PmInvalidAssigneeType, `Assignee for state "${stateName}" has invalid type: ${String(obj.type)}. Must be ONE, ALL, or ANY`);
7467
- if (!Array.isArray(obj.entities)) throw new BusinessRuleError(ProcessManagementErrorCode.PmInvalidConfigStructure, `Assignee for state "${stateName}" must have an "entities" array`);
7468
- const entities = obj.entities.map((item, i) => parseProcessEntity(item, i));
7525
+ if (!Array.isArray(raw.entities)) throw new BusinessRuleError(ProcessManagementErrorCode.PmInvalidConfigStructure, `Assignee for state "${stateName}" must have an "entities" array`);
7469
7526
  return {
7470
- type: obj.type,
7471
- entities
7527
+ type: parseEnum(raw.type, VALID_ASSIGNEE_TYPES, ProcessManagementErrorCode.PmInvalidAssigneeType, `Assignee for state "${stateName}" has invalid type: ${String(raw.type)}. Must be ONE, ALL, or ANY`),
7528
+ entities: raw.entities.map((item, i) => parseProcessEntity(item, i))
7472
7529
  };
7473
7530
  }
7474
7531
  function parseState(raw, stateName) {
7475
7532
  if (!isRecord(raw)) throw new BusinessRuleError(ProcessManagementErrorCode.PmInvalidConfigStructure, `State "${stateName}" must be an object`);
7476
- const obj = raw;
7477
- if (typeof obj.index !== "number") throw new BusinessRuleError(ProcessManagementErrorCode.PmInvalidConfigStructure, `State "${stateName}" must have a numeric "index" property`);
7478
- if (obj.assignee === void 0 || obj.assignee === null) throw new BusinessRuleError(ProcessManagementErrorCode.PmInvalidConfigStructure, `State "${stateName}" must have an "assignee" property`);
7479
- const assignee = parseAssignee(obj.assignee, stateName);
7533
+ if (typeof raw.index !== "number") throw new BusinessRuleError(ProcessManagementErrorCode.PmInvalidConfigStructure, `State "${stateName}" must have a numeric "index" property`);
7534
+ if (raw.assignee === void 0 || raw.assignee === null) throw new BusinessRuleError(ProcessManagementErrorCode.PmInvalidConfigStructure, `State "${stateName}" must have an "assignee" property`);
7535
+ const assignee = parseAssignee(raw.assignee, stateName);
7480
7536
  return {
7481
- index: obj.index,
7537
+ index: raw.index,
7482
7538
  assignee
7483
7539
  };
7484
7540
  }
7485
7541
  function parseExecutableUser(raw, actionIndex) {
7486
7542
  if (!isRecord(raw)) throw new BusinessRuleError(ProcessManagementErrorCode.PmInvalidConfigStructure, `Action at index ${actionIndex}: executableUser must be an object`);
7487
- const obj = raw;
7488
- if (!Array.isArray(obj.entities)) throw new BusinessRuleError(ProcessManagementErrorCode.PmInvalidConfigStructure, `Action at index ${actionIndex}: executableUser must have an "entities" array`);
7489
- return { entities: obj.entities.map((item, i) => parseProcessEntity(item, i)) };
7543
+ if (!Array.isArray(raw.entities)) throw new BusinessRuleError(ProcessManagementErrorCode.PmInvalidConfigStructure, `Action at index ${actionIndex}: executableUser must have an "entities" array`);
7544
+ return { entities: raw.entities.map((item, i) => parseProcessEntity(item, i)) };
7490
7545
  }
7491
7546
  function parseAction(raw, index) {
7492
7547
  if (!isRecord(raw)) throw new BusinessRuleError(ProcessManagementErrorCode.PmInvalidConfigStructure, `Action at index ${index} must be an object`);
7493
- const obj = raw;
7494
- if (typeof obj.name !== "string") throw new BusinessRuleError(ProcessManagementErrorCode.PmInvalidConfigStructure, `Action at index ${index} must have a "name" string property`);
7495
- if (typeof obj.from !== "string") throw new BusinessRuleError(ProcessManagementErrorCode.PmInvalidConfigStructure, `Action at index ${index} must have a "from" string property`);
7496
- if (typeof obj.to !== "string") throw new BusinessRuleError(ProcessManagementErrorCode.PmInvalidConfigStructure, `Action at index ${index} must have a "to" string property`);
7497
- if (obj.type !== void 0 && obj.type !== null && (typeof obj.type !== "string" || !VALID_ACTION_TYPES.has(obj.type))) throw new BusinessRuleError(ProcessManagementErrorCode.PmInvalidConfigStructure, `Action at index ${index} has invalid type: ${String(obj.type)}. Must be PRIMARY or SECONDARY`);
7498
- const actionType = typeof obj.type === "string" && VALID_ACTION_TYPES.has(obj.type) ? obj.type : "PRIMARY";
7548
+ if (typeof raw.name !== "string") throw new BusinessRuleError(ProcessManagementErrorCode.PmInvalidConfigStructure, `Action at index ${index} must have a "name" string property`);
7549
+ if (typeof raw.from !== "string") throw new BusinessRuleError(ProcessManagementErrorCode.PmInvalidConfigStructure, `Action at index ${index} must have a "from" string property`);
7550
+ if (typeof raw.to !== "string") throw new BusinessRuleError(ProcessManagementErrorCode.PmInvalidConfigStructure, `Action at index ${index} must have a "to" string property`);
7551
+ const actionType = raw.type === void 0 || raw.type === null ? "PRIMARY" : parseEnum(raw.type, VALID_ACTION_TYPES, ProcessManagementErrorCode.PmInvalidConfigStructure, `Action at index ${index} has invalid type: ${String(raw.type)}. Must be PRIMARY or SECONDARY`);
7499
7552
  const result = {
7500
- name: obj.name,
7501
- from: obj.from,
7502
- to: obj.to,
7503
- filterCond: typeof obj.filterCond === "string" ? obj.filterCond : "",
7553
+ name: raw.name,
7554
+ from: raw.from,
7555
+ to: raw.to,
7556
+ filterCond: typeof raw.filterCond === "string" ? raw.filterCond : "",
7504
7557
  type: actionType
7505
7558
  };
7506
- if (actionType === "SECONDARY" && obj.executableUser !== void 0 && obj.executableUser !== null) return {
7559
+ if (actionType === "SECONDARY" && raw.executableUser !== void 0 && raw.executableUser !== null) return {
7507
7560
  ...result,
7508
- executableUser: parseExecutableUser(obj.executableUser, index)
7561
+ executableUser: parseExecutableUser(raw.executableUser, index)
7509
7562
  };
7510
7563
  return result;
7511
7564
  }
7512
7565
  const ProcessManagementConfigParser = { parse: (rawText) => {
7513
- if (rawText.trim().length === 0) throw new BusinessRuleError(ProcessManagementErrorCode.PmEmptyConfigText, "Process management config text is empty");
7514
- let parsed;
7515
- try {
7516
- parsed = parse(rawText);
7517
- } catch (error) {
7518
- throw new BusinessRuleError(ProcessManagementErrorCode.PmInvalidConfigYaml, `Failed to parse YAML: ${error instanceof Error ? error.message : String(error)}`);
7519
- }
7520
- if (!isRecord(parsed)) throw new BusinessRuleError(ProcessManagementErrorCode.PmInvalidConfigStructure, "Config must be a YAML object");
7521
- const obj = parsed;
7522
- const enable = obj.enable !== void 0 && obj.enable !== null ? Boolean(obj.enable) : false;
7523
- 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)");
7524
- const rawStates = isRecord(obj.states) ? obj.states : {};
7566
+ const parsed = parseYamlConfig(rawText, {
7567
+ emptyConfigText: ProcessManagementErrorCode.PmEmptyConfigText,
7568
+ invalidConfigYaml: ProcessManagementErrorCode.PmInvalidConfigYaml,
7569
+ invalidConfigStructure: ProcessManagementErrorCode.PmInvalidConfigStructure
7570
+ }, "Process management");
7571
+ const enable = parsed.enable !== void 0 && parsed.enable !== null ? parseStrictBoolean(parsed.enable, "enable", "Config", ProcessManagementErrorCode.PmInvalidBooleanField) : false;
7572
+ 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)");
7573
+ const rawStates = isRecord(parsed.states) ? parsed.states : {};
7525
7574
  const states = {};
7526
7575
  for (const [name, value] of Object.entries(rawStates)) states[name] = parseState(value, name);
7527
- if (!Array.isArray(obj.actions) && obj.actions !== void 0) throw new BusinessRuleError(ProcessManagementErrorCode.PmInvalidConfigStructure, "Config \"actions\" must be an array");
7528
- const actions = (obj.actions ?? []).map((item, i) => parseAction(item, i));
7576
+ if (!Array.isArray(parsed.actions) && parsed.actions !== void 0) throw new BusinessRuleError(ProcessManagementErrorCode.PmInvalidConfigStructure, "Config \"actions\" must be an array");
7577
+ const actions = (Array.isArray(parsed.actions) ? parsed.actions : []).map((item, i) => parseAction(item, i));
7578
+ const actionNames = /* @__PURE__ */ new Set();
7579
+ for (const action of actions) {
7580
+ if (actionNames.has(action.name)) throw new BusinessRuleError(ProcessManagementErrorCode.PmDuplicateActionName, `Duplicate action name: "${action.name}"`);
7581
+ actionNames.add(action.name);
7582
+ }
7529
7583
  const stateNames = new Set(Object.keys(states));
7530
7584
  for (const action of actions) {
7531
7585
  if (!stateNames.has(action.from)) throw new BusinessRuleError(ProcessManagementErrorCode.PmInvalidActionReference, `Action "${action.name}" references unknown "from" state: "${action.from}"`);
@@ -7678,7 +7732,7 @@ var capture_default$6 = define({
7678
7732
  //#region src/core/domain/processManagement/services/diffDetector.ts
7679
7733
  function isEntityEqual(a, b) {
7680
7734
  if (a.type !== b.type) return false;
7681
- if (a.code !== b.code) return false;
7735
+ if ((a.code ?? "") !== (b.code ?? "")) return false;
7682
7736
  if (Boolean(a.includeSubs) !== Boolean(b.includeSubs)) return false;
7683
7737
  return true;
7684
7738
  }
@@ -7816,56 +7870,51 @@ const VALID_ENTITY_TYPES = new Set([
7816
7870
  "ORGANIZATION",
7817
7871
  "FIELD_ENTITY"
7818
7872
  ]);
7819
- function parseBooleanField(value, fieldName, context) {
7820
- if (value === void 0) return false;
7821
- if (typeof value === "boolean") return value;
7822
- throw new BusinessRuleError(RecordPermissionErrorCode.RpInvalidPermissionValue, `${context} has invalid "${fieldName}" value: ${String(value)}. Must be a boolean`);
7823
- }
7824
7873
  function parseEntity(raw, index) {
7825
- if (!isRecord(raw)) throw new BusinessRuleError(RecordPermissionErrorCode.RpInvalidConfigStructure, `Entity at index ${index} must be an object`);
7826
- const obj = raw;
7827
- if (typeof obj.type !== "string" || !VALID_ENTITY_TYPES.has(obj.type)) throw new BusinessRuleError(RecordPermissionErrorCode.RpInvalidEntityType, `Entity at index ${index} has invalid type: ${String(obj.type)}. Must be USER, GROUP, ORGANIZATION, or FIELD_ENTITY`);
7828
- if (typeof obj.code !== "string" || obj.code.length === 0) throw new BusinessRuleError(RecordPermissionErrorCode.RpEmptyEntityCode, `Entity at index ${index} must have a non-empty "code" property`);
7829
- return {
7830
- type: obj.type,
7831
- code: obj.code
7832
- };
7874
+ return parseEntityBase(raw, index, VALID_ENTITY_TYPES, {
7875
+ invalidStructure: RecordPermissionErrorCode.RpInvalidConfigStructure,
7876
+ invalidType: RecordPermissionErrorCode.RpInvalidEntityType,
7877
+ emptyCode: RecordPermissionErrorCode.RpEmptyEntityCode
7878
+ });
7833
7879
  }
7834
7880
  function parseRecordRightEntity(raw, index) {
7835
7881
  if (!isRecord(raw)) throw new BusinessRuleError(RecordPermissionErrorCode.RpInvalidConfigStructure, `Record right entity at index ${index} must be an object`);
7836
- const obj = raw;
7837
- const entity = parseEntity(obj.entity, index);
7882
+ const entity = parseEntity(raw.entity, index);
7838
7883
  const context = `Record right entity at index ${index}`;
7839
7884
  return {
7840
7885
  entity,
7841
- viewable: parseBooleanField(obj.viewable, "viewable", context),
7842
- editable: parseBooleanField(obj.editable, "editable", context),
7843
- deletable: parseBooleanField(obj.deletable, "deletable", context),
7844
- includeSubs: parseBooleanField(obj.includeSubs, "includeSubs", context)
7886
+ viewable: parseStrictBoolean(raw.viewable, "viewable", context, RecordPermissionErrorCode.RpInvalidPermissionValue, false),
7887
+ editable: parseStrictBoolean(raw.editable, "editable", context, RecordPermissionErrorCode.RpInvalidPermissionValue, false),
7888
+ deletable: parseStrictBoolean(raw.deletable, "deletable", context, RecordPermissionErrorCode.RpInvalidPermissionValue, false),
7889
+ includeSubs: parseStrictBoolean(raw.includeSubs, "includeSubs", context, RecordPermissionErrorCode.RpInvalidPermissionValue, false)
7845
7890
  };
7846
7891
  }
7847
7892
  function parseRecordRight(raw, index) {
7848
7893
  if (!isRecord(raw)) throw new BusinessRuleError(RecordPermissionErrorCode.RpInvalidConfigStructure, `Record right at index ${index} must be an object`);
7849
- const obj = raw;
7850
- if (!Array.isArray(obj.entities)) throw new BusinessRuleError(RecordPermissionErrorCode.RpInvalidConfigStructure, `Record right at index ${index} must have an "entities" array`);
7851
- const entities = obj.entities.map((item, i) => parseRecordRightEntity(item, i));
7894
+ if (!Array.isArray(raw.entities)) throw new BusinessRuleError(RecordPermissionErrorCode.RpInvalidConfigStructure, `Record right at index ${index} must have an "entities" array`);
7895
+ const entities = raw.entities.map((item, i) => parseRecordRightEntity(item, i));
7852
7896
  return {
7853
- filterCond: typeof obj.filterCond === "string" ? obj.filterCond : "",
7897
+ filterCond: typeof raw.filterCond === "string" ? raw.filterCond : "",
7854
7898
  entities
7855
7899
  };
7856
7900
  }
7857
7901
  const RecordPermissionConfigParser = { parse: (rawText) => {
7858
- if (rawText.trim().length === 0) throw new BusinessRuleError(RecordPermissionErrorCode.RpEmptyConfigText, "Record permission config text is empty");
7859
- let parsed;
7860
- try {
7861
- parsed = parse(rawText);
7862
- } catch (error) {
7863
- throw new BusinessRuleError(RecordPermissionErrorCode.RpInvalidConfigYaml, `Failed to parse YAML: ${error instanceof Error ? error.message : String(error)}`);
7902
+ const parsed = parseYamlConfig(rawText, {
7903
+ emptyConfigText: RecordPermissionErrorCode.RpEmptyConfigText,
7904
+ invalidConfigYaml: RecordPermissionErrorCode.RpInvalidConfigYaml,
7905
+ invalidConfigStructure: RecordPermissionErrorCode.RpInvalidConfigStructure
7906
+ }, "Record permission");
7907
+ if (!Array.isArray(parsed.rights)) throw new BusinessRuleError(RecordPermissionErrorCode.RpInvalidConfigStructure, "Config must have a \"rights\" array");
7908
+ const rights = parsed.rights.map((item, i) => parseRecordRight(item, i));
7909
+ for (const right of rights) {
7910
+ const seenKeys = /* @__PURE__ */ new Set();
7911
+ for (const re of right.entities) {
7912
+ const key = `${re.entity.type}:${re.entity.code}`;
7913
+ if (seenKeys.has(key)) throw new BusinessRuleError(RecordPermissionErrorCode.RpDuplicateEntity, `Duplicate entity in filterCond "${right.filterCond}": ${re.entity.type} ${re.entity.code}`);
7914
+ seenKeys.add(key);
7915
+ }
7864
7916
  }
7865
- if (!isRecord(parsed)) throw new BusinessRuleError(RecordPermissionErrorCode.RpInvalidConfigStructure, "Config must be a YAML object");
7866
- const obj = parsed;
7867
- if (!Array.isArray(obj.rights)) throw new BusinessRuleError(RecordPermissionErrorCode.RpInvalidConfigStructure, "Config must have a \"rights\" array");
7868
- return { rights: obj.rights.map((item, i) => parseRecordRight(item, i)) };
7917
+ return { rights };
7869
7918
  } };
7870
7919
 
7871
7920
  //#endregion
@@ -8000,7 +8049,7 @@ var capture_default$5 = define({
8000
8049
 
8001
8050
  //#endregion
8002
8051
  //#region src/core/domain/recordPermission/services/diffDetector.ts
8003
- function areRightsEqual(a, b) {
8052
+ function isRightEqual(a, b) {
8004
8053
  return deepEqual(a.entities.map((e) => ({
8005
8054
  type: e.entity.type,
8006
8055
  code: e.entity.code,
@@ -8049,7 +8098,7 @@ const RecordPermissionDiffDetector = { detect: (local, remote) => {
8049
8098
  details: describeRight(remoteRight)
8050
8099
  });
8051
8100
  else if (localRight && remoteRight) {
8052
- if (!areRightsEqual(localRight, remoteRight)) {
8101
+ if (!isRightEqual(localRight, remoteRight)) {
8053
8102
  const diffs = [];
8054
8103
  if (localRight.entities.length !== remoteRight.entities.length) diffs.push(`entities: ${remoteRight.entities.length} -> ${localRight.entities.length}`);
8055
8104
  else diffs.push("entities changed");
@@ -8111,140 +8160,76 @@ var record_acl_default = define({
8111
8160
 
8112
8161
  //#endregion
8113
8162
  //#region src/core/domain/report/services/configParser.ts
8114
- const VALID_CHART_TYPES = new Set([
8115
- "BAR",
8116
- "COLUMN",
8117
- "PIE",
8118
- "LINE",
8119
- "PIVOT_TABLE",
8120
- "TABLE",
8121
- "AREA",
8122
- "SPLINE",
8123
- "SPLINE_AREA"
8124
- ]);
8125
- const VALID_CHART_MODES = new Set([
8126
- "NORMAL",
8127
- "STACKED",
8128
- "PERCENTAGE"
8129
- ]);
8130
- const VALID_GROUP_PER = new Set([
8131
- "YEAR",
8132
- "QUARTER",
8133
- "MONTH",
8134
- "WEEK",
8135
- "DAY",
8136
- "HOUR",
8137
- "MINUTE"
8138
- ]);
8139
- const VALID_AGGREGATION_TYPES = new Set([
8140
- "COUNT",
8141
- "SUM",
8142
- "AVERAGE",
8143
- "MAX",
8144
- "MIN"
8145
- ]);
8146
- const VALID_SORT_BY = new Set([
8147
- "TOTAL",
8148
- "GROUP1",
8149
- "GROUP2",
8150
- "GROUP3"
8151
- ]);
8152
- const VALID_DAY_OF_WEEK = new Set([
8153
- "SUNDAY",
8154
- "MONDAY",
8155
- "TUESDAY",
8156
- "WEDNESDAY",
8157
- "THURSDAY",
8158
- "FRIDAY",
8159
- "SATURDAY"
8160
- ]);
8161
- const VALID_PERIODIC_PATTERN = new Set([
8162
- "JAN_APR_JUL_OCT",
8163
- "FEB_MAY_AUG_NOV",
8164
- "MAR_JUN_SEP_DEC"
8165
- ]);
8166
- const VALID_SORT_ORDER = new Set(["ASC", "DESC"]);
8167
- const VALID_PERIODIC_EVERY = new Set([
8168
- "YEAR",
8169
- "QUARTER",
8170
- "MONTH",
8171
- "WEEK",
8172
- "DAY",
8173
- "HOUR"
8174
- ]);
8175
8163
  function parseGroup(raw, index) {
8176
8164
  if (!isRecord(raw)) throw new BusinessRuleError(ReportErrorCode.RtInvalidConfigStructure, `Group at index ${index} must be an object`);
8177
- const obj = raw;
8178
- if (typeof obj.code !== "string" || obj.code.length === 0) throw new BusinessRuleError(ReportErrorCode.RtInvalidConfigStructure, `Group at index ${index} must have a non-empty "code" property`);
8179
- const result = { code: obj.code };
8180
- if (obj.per !== void 0 && obj.per !== null) {
8181
- if (typeof obj.per !== "string" || !VALID_GROUP_PER.has(obj.per)) throw new BusinessRuleError(ReportErrorCode.RtInvalidConfigStructure, `Group at index ${index} has invalid per: ${String(obj.per)}. Must be YEAR, QUARTER, MONTH, WEEK, DAY, HOUR, or MINUTE`);
8165
+ if (typeof raw.code !== "string" || raw.code.length === 0) throw new BusinessRuleError(ReportErrorCode.RtInvalidConfigStructure, `Group at index ${index} must have a non-empty "code" property`);
8166
+ const result = { code: raw.code };
8167
+ if (raw.per !== void 0 && raw.per !== null) {
8168
+ if (typeof raw.per !== "string" || !isGroupPer(raw.per)) throw new BusinessRuleError(ReportErrorCode.RtInvalidConfigStructure, `Group at index ${index} has invalid per: ${String(raw.per)}. Must be YEAR, QUARTER, MONTH, WEEK, DAY, HOUR, or MINUTE`);
8182
8169
  return {
8183
8170
  ...result,
8184
- per: obj.per
8171
+ per: raw.per
8185
8172
  };
8186
8173
  }
8187
8174
  return result;
8188
8175
  }
8189
8176
  function parseAggregation(raw, index) {
8190
8177
  if (!isRecord(raw)) throw new BusinessRuleError(ReportErrorCode.RtInvalidConfigStructure, `Aggregation at index ${index} must be an object`);
8191
- const obj = raw;
8192
- if (typeof obj.type !== "string" || !VALID_AGGREGATION_TYPES.has(obj.type)) throw new BusinessRuleError(ReportErrorCode.RtInvalidConfigStructure, `Aggregation at index ${index} has invalid type: ${String(obj.type)}. Must be COUNT, SUM, AVERAGE, MAX, or MIN`);
8193
- const result = { type: obj.type };
8194
- if (obj.code !== void 0 && obj.code !== null) {
8195
- if (typeof obj.code !== "string") throw new BusinessRuleError(ReportErrorCode.RtInvalidConfigStructure, `Aggregation at index ${index} has invalid code: must be a string`);
8178
+ if (typeof raw.type !== "string" || !isAggregationType(raw.type)) throw new BusinessRuleError(ReportErrorCode.RtInvalidConfigStructure, `Aggregation at index ${index} has invalid type: ${String(raw.type)}. Must be COUNT, SUM, AVERAGE, MAX, or MIN`);
8179
+ const result = { type: raw.type };
8180
+ if (raw.code !== void 0 && raw.code !== null) {
8181
+ if (typeof raw.code !== "string") throw new BusinessRuleError(ReportErrorCode.RtInvalidConfigStructure, `Aggregation at index ${index} has invalid code: must be a string`);
8196
8182
  return {
8197
8183
  ...result,
8198
- code: obj.code
8184
+ code: raw.code
8199
8185
  };
8200
8186
  }
8201
8187
  return result;
8202
8188
  }
8203
8189
  function parseSort(raw, index) {
8204
8190
  if (!isRecord(raw)) throw new BusinessRuleError(ReportErrorCode.RtInvalidConfigStructure, `Sort at index ${index} must be an object`);
8205
- const obj = raw;
8206
- if (typeof obj.by !== "string" || !VALID_SORT_BY.has(obj.by)) throw new BusinessRuleError(ReportErrorCode.RtInvalidConfigStructure, `Sort at index ${index} has invalid by: ${String(obj.by)}. Must be TOTAL, GROUP1, GROUP2, or GROUP3`);
8207
- if (typeof obj.order !== "string" || !VALID_SORT_ORDER.has(obj.order)) throw new BusinessRuleError(ReportErrorCode.RtInvalidConfigStructure, `Sort at index ${index} has invalid order: ${String(obj.order)}. Must be ASC or DESC`);
8191
+ if (typeof raw.by !== "string" || !isSortBy(raw.by)) throw new BusinessRuleError(ReportErrorCode.RtInvalidConfigStructure, `Sort at index ${index} has invalid by: ${String(raw.by)}. Must be TOTAL, GROUP1, GROUP2, or GROUP3`);
8192
+ if (typeof raw.order !== "string" || !isSortOrder(raw.order)) throw new BusinessRuleError(ReportErrorCode.RtInvalidConfigStructure, `Sort at index ${index} has invalid order: ${String(raw.order)}. Must be ASC or DESC`);
8208
8193
  return {
8209
- by: obj.by,
8210
- order: obj.order
8194
+ by: raw.by,
8195
+ order: raw.order
8211
8196
  };
8212
8197
  }
8213
8198
  function parsePeriodicReportPeriod(raw) {
8214
8199
  if (!isRecord(raw)) throw new BusinessRuleError(ReportErrorCode.RtInvalidConfigStructure, "periodicReport.period must be an object");
8215
- const obj = raw;
8216
- if (typeof obj.every !== "string" || !VALID_PERIODIC_EVERY.has(obj.every)) throw new BusinessRuleError(ReportErrorCode.RtInvalidConfigStructure, `periodicReport.period has invalid every: ${String(obj.every)}. Must be YEAR, QUARTER, MONTH, WEEK, DAY, or HOUR`);
8217
- const every = obj.every;
8200
+ 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`);
8201
+ const every = raw.every;
8218
8202
  let month;
8219
- if (obj.month !== void 0 && obj.month !== null) {
8220
- const parsed = Number(obj.month);
8221
- if (Number.isNaN(parsed)) throw new BusinessRuleError(ReportErrorCode.RtInvalidConfigStructure, `periodicReport.period has invalid month: ${String(obj.month)}. Must be a number`);
8203
+ if (raw.month !== void 0 && raw.month !== null) {
8204
+ const parsed = Number(raw.month);
8205
+ 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`);
8222
8206
  month = parsed;
8223
8207
  }
8224
8208
  let pattern;
8225
- if (obj.pattern !== void 0 && obj.pattern !== null) {
8226
- if (typeof obj.pattern !== "string" || !VALID_PERIODIC_PATTERN.has(obj.pattern)) throw new BusinessRuleError(ReportErrorCode.RtInvalidConfigStructure, `periodicReport.period has invalid pattern: ${String(obj.pattern)}. Must be JAN_APR_JUL_OCT, FEB_MAY_AUG_NOV, or MAR_JUN_SEP_DEC`);
8227
- pattern = obj.pattern;
8209
+ if (raw.pattern !== void 0 && raw.pattern !== null) {
8210
+ 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`);
8211
+ pattern = raw.pattern;
8228
8212
  }
8229
8213
  let dayOfMonth;
8230
- if (obj.dayOfMonth !== void 0 && obj.dayOfMonth !== null) if (obj.dayOfMonth === "END_OF_MONTH") dayOfMonth = "END_OF_MONTH";
8214
+ if (raw.dayOfMonth !== void 0 && raw.dayOfMonth !== null) if (raw.dayOfMonth === "END_OF_MONTH") dayOfMonth = "END_OF_MONTH";
8231
8215
  else {
8232
- const parsed = Number(obj.dayOfMonth);
8233
- if (Number.isNaN(parsed)) throw new BusinessRuleError(ReportErrorCode.RtInvalidConfigStructure, `periodicReport.period has invalid dayOfMonth: ${String(obj.dayOfMonth)}. Must be a number or "END_OF_MONTH"`);
8216
+ const parsed = Number(raw.dayOfMonth);
8217
+ 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"`);
8218
+ if (parsed < 1 || parsed > 31) throw new BusinessRuleError(ReportErrorCode.RtInvalidConfigStructure, `periodicReport.period has out-of-range dayOfMonth: ${parsed}. Must be 1-31`);
8234
8219
  dayOfMonth = parsed;
8235
8220
  }
8236
8221
  let time;
8237
- if (obj.time !== void 0 && obj.time !== null) time = String(obj.time);
8222
+ if (raw.time !== void 0 && raw.time !== null) time = String(raw.time);
8238
8223
  let dayOfWeek;
8239
- if (obj.dayOfWeek !== void 0 && obj.dayOfWeek !== null) {
8240
- const dayStr = String(obj.dayOfWeek);
8241
- if (!VALID_DAY_OF_WEEK.has(dayStr)) throw new BusinessRuleError(ReportErrorCode.RtInvalidConfigStructure, `periodicReport.period has invalid dayOfWeek: ${dayStr}. Must be SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, or SATURDAY`);
8224
+ if (raw.dayOfWeek !== void 0 && raw.dayOfWeek !== null) {
8225
+ const dayStr = String(raw.dayOfWeek);
8226
+ if (!isDayOfWeek(dayStr)) throw new BusinessRuleError(ReportErrorCode.RtInvalidConfigStructure, `periodicReport.period has invalid dayOfWeek: ${dayStr}. Must be SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, or SATURDAY`);
8242
8227
  dayOfWeek = dayStr;
8243
8228
  }
8244
8229
  let minute;
8245
- if (obj.minute !== void 0 && obj.minute !== null) {
8246
- const parsed = Number(obj.minute);
8247
- if (Number.isNaN(parsed)) throw new BusinessRuleError(ReportErrorCode.RtInvalidConfigStructure, `periodicReport.period has invalid minute: ${String(obj.minute)}. Must be a number`);
8230
+ if (raw.minute !== void 0 && raw.minute !== null) {
8231
+ const parsed = Number(raw.minute);
8232
+ 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)`);
8248
8233
  minute = parsed;
8249
8234
  }
8250
8235
  return {
@@ -8259,32 +8244,34 @@ function parsePeriodicReportPeriod(raw) {
8259
8244
  }
8260
8245
  function parsePeriodicReport(raw) {
8261
8246
  if (!isRecord(raw)) throw new BusinessRuleError(ReportErrorCode.RtInvalidConfigStructure, "periodicReport must be an object");
8262
- const obj = raw;
8263
- if (typeof obj.active !== "boolean") throw new BusinessRuleError(ReportErrorCode.RtInvalidConfigStructure, "periodicReport.active must be a boolean");
8264
- const period = parsePeriodicReportPeriod(obj.period);
8247
+ const active = raw.active;
8248
+ if (typeof active !== "boolean") throw new BusinessRuleError(ReportErrorCode.RtInvalidConfigStructure, "periodicReport.active must be a boolean");
8265
8249
  return {
8266
- active: obj.active,
8267
- period
8250
+ active,
8251
+ period: parsePeriodicReportPeriod(raw.period)
8268
8252
  };
8269
8253
  }
8270
8254
  function parseReportConfig(raw, reportName) {
8255
+ if (reportName.length === 0) throw new BusinessRuleError(ReportErrorCode.RtEmptyReportName, "Report name (key) must not be empty");
8271
8256
  if (!isRecord(raw)) throw new BusinessRuleError(ReportErrorCode.RtInvalidConfigStructure, `Report "${reportName}" must be an object`);
8272
- const obj = raw;
8273
- if (typeof obj.chartType !== "string" || !VALID_CHART_TYPES.has(obj.chartType)) throw new BusinessRuleError(ReportErrorCode.RtInvalidChartType, `Report "${reportName}" has invalid chartType: ${String(obj.chartType)}. Must be BAR, COLUMN, PIE, LINE, PIVOT_TABLE, TABLE, AREA, SPLINE, or SPLINE_AREA`);
8257
+ if (typeof raw.chartType !== "string" || !isChartType(raw.chartType)) throw new BusinessRuleError(ReportErrorCode.RtInvalidChartType, `Report "${reportName}" has invalid chartType: ${String(raw.chartType)}. Must be BAR, COLUMN, PIE, LINE, PIVOT_TABLE, TABLE, AREA, SPLINE, or SPLINE_AREA`);
8274
8258
  let chartMode;
8275
- if (obj.chartMode !== void 0 && obj.chartMode !== null) {
8276
- if (typeof obj.chartMode !== "string" || !VALID_CHART_MODES.has(obj.chartMode)) throw new BusinessRuleError(ReportErrorCode.RtInvalidChartMode, `Report "${reportName}" has invalid chartMode: ${String(obj.chartMode)}. Must be NORMAL, STACKED, or PERCENTAGE`);
8277
- chartMode = obj.chartMode;
8278
- }
8279
- const name = typeof obj.name === "string" && obj.name.length > 0 ? obj.name : reportName;
8280
- if (name.length === 0) throw new BusinessRuleError(ReportErrorCode.RtEmptyReportName, "Report must have a non-empty name");
8281
- const index = obj.index !== void 0 && obj.index !== null ? Number(obj.index) : 0;
8282
- const groups = Array.isArray(obj.groups) ? obj.groups.map((item, i) => parseGroup(item, i)) : [];
8283
- const aggregations = Array.isArray(obj.aggregations) ? obj.aggregations.map((item, i) => parseAggregation(item, i)) : [];
8284
- const filterCond = typeof obj.filterCond === "string" ? obj.filterCond : "";
8285
- const sorts = Array.isArray(obj.sorts) ? obj.sorts.map((item, i) => parseSort(item, i)) : [];
8259
+ if (raw.chartMode !== void 0 && raw.chartMode !== null) {
8260
+ if (typeof raw.chartMode !== "string" || !isChartMode(raw.chartMode)) throw new BusinessRuleError(ReportErrorCode.RtInvalidChartMode, `Report "${reportName}" has invalid chartMode: ${String(raw.chartMode)}. Must be NORMAL, STACKED, or PERCENTAGE`);
8261
+ chartMode = raw.chartMode;
8262
+ }
8263
+ const name = typeof raw.name === "string" && raw.name.length > 0 ? raw.name : reportName;
8264
+ 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`);
8265
+ const index = typeof raw.index === "number" ? raw.index : 0;
8266
+ if (raw.groups !== void 0 && !Array.isArray(raw.groups)) throw new BusinessRuleError(ReportErrorCode.RtInvalidConfigStructure, `Report "${reportName}" has invalid groups: must be an array`);
8267
+ const groups = Array.isArray(raw.groups) ? raw.groups.map((item, i) => parseGroup(item, i)) : [];
8268
+ if (raw.aggregations !== void 0 && !Array.isArray(raw.aggregations)) throw new BusinessRuleError(ReportErrorCode.RtInvalidConfigStructure, `Report "${reportName}" has invalid aggregations: must be an array`);
8269
+ const aggregations = Array.isArray(raw.aggregations) ? raw.aggregations.map((item, i) => parseAggregation(item, i)) : [];
8270
+ const filterCond = typeof raw.filterCond === "string" ? raw.filterCond : "";
8271
+ if (raw.sorts !== void 0 && !Array.isArray(raw.sorts)) throw new BusinessRuleError(ReportErrorCode.RtInvalidConfigStructure, `Report "${reportName}" has invalid sorts: must be an array`);
8272
+ const sorts = Array.isArray(raw.sorts) ? raw.sorts.map((item, i) => parseSort(item, i)) : [];
8286
8273
  const result = {
8287
- chartType: obj.chartType,
8274
+ chartType: raw.chartType,
8288
8275
  ...chartMode !== void 0 ? { chartMode } : {},
8289
8276
  index,
8290
8277
  name,
@@ -8293,22 +8280,18 @@ function parseReportConfig(raw, reportName) {
8293
8280
  filterCond,
8294
8281
  sorts
8295
8282
  };
8296
- if (obj.periodicReport !== void 0 && obj.periodicReport !== null) return {
8283
+ if (raw.periodicReport !== void 0 && raw.periodicReport !== null) return {
8297
8284
  ...result,
8298
- periodicReport: parsePeriodicReport(obj.periodicReport)
8285
+ periodicReport: parsePeriodicReport(raw.periodicReport)
8299
8286
  };
8300
8287
  return result;
8301
8288
  }
8302
8289
  const ReportConfigParser = { parse: (rawText) => {
8303
- if (rawText.trim().length === 0) throw new BusinessRuleError(ReportErrorCode.RtEmptyConfigText, "Report config text is empty");
8304
- let parsed;
8305
- try {
8306
- parsed = parse(rawText);
8307
- } catch (error) {
8308
- throw new BusinessRuleError(ReportErrorCode.RtInvalidConfigYaml, `Failed to parse YAML: ${error instanceof Error ? error.message : String(error)}`);
8309
- }
8310
- if (!isRecord(parsed)) throw new BusinessRuleError(ReportErrorCode.RtInvalidConfigStructure, "Config must be a YAML object");
8311
- const obj = parsed;
8290
+ const obj = parseYamlConfig(rawText, {
8291
+ emptyConfigText: ReportErrorCode.RtEmptyConfigText,
8292
+ invalidConfigYaml: ReportErrorCode.RtInvalidConfigYaml,
8293
+ invalidConfigStructure: ReportErrorCode.RtInvalidConfigStructure
8294
+ }, "Report");
8312
8295
  if (!isRecord(obj.reports)) throw new BusinessRuleError(ReportErrorCode.RtInvalidConfigStructure, "Config must have a \"reports\" object");
8313
8296
  const rawReports = obj.reports;
8314
8297
  const reports = {};
@@ -8462,29 +8445,26 @@ function compareReports(local, remote) {
8462
8445
  return diffs;
8463
8446
  }
8464
8447
  const ReportDiffDetector = { detect: (local, remote) => {
8465
- const entries = [];
8466
- for (const [name, localReport] of Object.entries(local.reports)) {
8467
- const remoteReport = remote.reports[name];
8468
- if (!remoteReport) entries.push({
8448
+ return buildDiffResult(detectRecordDiff(local.reports, remote.reports, {
8449
+ onAdded: (name, localReport) => ({
8469
8450
  type: "added",
8470
8451
  reportName: name,
8471
8452
  details: `chartType: ${localReport.chartType}`
8472
- });
8473
- else {
8453
+ }),
8454
+ onModified: (name, localReport, remoteReport) => {
8474
8455
  const diffs = compareReports(localReport, remoteReport);
8475
- if (diffs.length > 0) entries.push({
8456
+ if (diffs.length > 0) return {
8476
8457
  type: "modified",
8477
8458
  reportName: name,
8478
8459
  details: diffs.join(", ")
8479
- });
8480
- }
8481
- }
8482
- for (const [name, remoteReport] of Object.entries(remote.reports)) if (!local.reports[name]) entries.push({
8483
- type: "deleted",
8484
- reportName: name,
8485
- details: `chartType: ${remoteReport.chartType}`
8486
- });
8487
- return buildDiffResult(entries);
8460
+ };
8461
+ },
8462
+ onDeleted: (name, remoteReport) => ({
8463
+ type: "deleted",
8464
+ reportName: name,
8465
+ details: `chartType: ${remoteReport.chartType}`
8466
+ })
8467
+ }));
8488
8468
  } };
8489
8469
 
8490
8470
  //#endregion
@@ -10245,6 +10225,11 @@ var seed_default = define({
10245
10225
 
10246
10226
  //#endregion
10247
10227
  //#region src/core/domain/generalSettings/services/configParser.ts
10228
+ function parseOptionalBoolean(parsed, fieldName) {
10229
+ const value = parsed[fieldName];
10230
+ if (value === void 0 || value === null) return void 0;
10231
+ return parseStrictBoolean(value, fieldName, "Config", GeneralSettingsErrorCode.GsInvalidBooleanField);
10232
+ }
10248
10233
  const VALID_THEMES = new Set([
10249
10234
  "WHITE",
10250
10235
  "RED",
@@ -10266,86 +10251,70 @@ const VALID_ROUNDING_MODES = new Set([
10266
10251
  ]);
10267
10252
  function parseIcon(raw) {
10268
10253
  if (!isRecord(raw)) throw new BusinessRuleError(GeneralSettingsErrorCode.GsInvalidConfigStructure, "icon must be an object with \"type\" and \"key\" properties");
10269
- const obj = raw;
10270
- if (typeof obj.type !== "string" || !VALID_ICON_TYPES.has(obj.type)) throw new BusinessRuleError(GeneralSettingsErrorCode.GsInvalidIconType, `icon.type must be PRESET or FILE, got: ${String(obj.type)}`);
10271
- if (typeof obj.key !== "string" || obj.key.length === 0) throw new BusinessRuleError(GeneralSettingsErrorCode.GsInvalidConfigStructure, "icon must have a non-empty \"key\" property");
10254
+ if (typeof raw.key !== "string" || raw.key.length === 0) throw new BusinessRuleError(GeneralSettingsErrorCode.GsInvalidConfigStructure, "icon must have a non-empty \"key\" property");
10272
10255
  return {
10273
- type: obj.type,
10274
- key: obj.key
10256
+ type: parseEnum(raw.type, VALID_ICON_TYPES, GeneralSettingsErrorCode.GsInvalidIconType, `icon.type must be PRESET or FILE, got: ${String(raw.type)}`),
10257
+ key: raw.key
10275
10258
  };
10276
10259
  }
10277
10260
  function parseTitleField(raw) {
10278
10261
  if (!isRecord(raw)) throw new BusinessRuleError(GeneralSettingsErrorCode.GsInvalidConfigStructure, "titleField must be an object with \"selectionMode\" property");
10279
- const obj = raw;
10280
- if (typeof obj.selectionMode !== "string" || !VALID_SELECTION_MODES.has(obj.selectionMode)) throw new BusinessRuleError(GeneralSettingsErrorCode.GsInvalidConfigStructure, `titleField.selectionMode must be AUTO or MANUAL, got: ${String(obj.selectionMode)}`);
10281
- const result = { selectionMode: obj.selectionMode };
10282
- if (obj.code !== void 0 && obj.code !== null) {
10283
- if (typeof obj.code !== "string") throw new BusinessRuleError(GeneralSettingsErrorCode.GsInvalidConfigStructure, "titleField.code must be a string");
10262
+ const result = { selectionMode: parseEnum(raw.selectionMode, VALID_SELECTION_MODES, GeneralSettingsErrorCode.GsInvalidConfigStructure, `titleField.selectionMode must be AUTO or MANUAL, got: ${String(raw.selectionMode)}`) };
10263
+ if (raw.code !== void 0 && raw.code !== null) {
10264
+ if (typeof raw.code !== "string") throw new BusinessRuleError(GeneralSettingsErrorCode.GsInvalidConfigStructure, "titleField.code must be a string");
10284
10265
  return {
10285
10266
  ...result,
10286
- code: obj.code
10267
+ code: raw.code
10287
10268
  };
10288
10269
  }
10289
10270
  return result;
10290
10271
  }
10291
10272
  function parseNumberPrecision(raw) {
10292
10273
  if (!isRecord(raw)) throw new BusinessRuleError(GeneralSettingsErrorCode.GsInvalidConfigStructure, "numberPrecision must be an object");
10293
- const obj = raw;
10294
- if (typeof obj.digits !== "number") throw new BusinessRuleError(GeneralSettingsErrorCode.GsInvalidConfigStructure, "numberPrecision.digits must be a number");
10295
- if (typeof obj.decimalPlaces !== "number") throw new BusinessRuleError(GeneralSettingsErrorCode.GsInvalidConfigStructure, "numberPrecision.decimalPlaces must be a number");
10296
- if (typeof obj.roundingMode !== "string" || !VALID_ROUNDING_MODES.has(obj.roundingMode)) throw new BusinessRuleError(GeneralSettingsErrorCode.GsInvalidConfigStructure, `numberPrecision.roundingMode must be HALF_EVEN, UP, or DOWN, got: ${String(obj.roundingMode)}`);
10274
+ if (typeof raw.digits !== "number") throw new BusinessRuleError(GeneralSettingsErrorCode.GsInvalidConfigStructure, "numberPrecision.digits must be a number");
10275
+ if (!Number.isInteger(raw.digits) || raw.digits < 0) throw new BusinessRuleError(GeneralSettingsErrorCode.GsInvalidNumberPrecision, `numberPrecision.digits must be a non-negative integer, got: ${raw.digits}`);
10276
+ if (typeof raw.decimalPlaces !== "number") throw new BusinessRuleError(GeneralSettingsErrorCode.GsInvalidConfigStructure, "numberPrecision.decimalPlaces must be a number");
10277
+ if (!Number.isInteger(raw.decimalPlaces) || raw.decimalPlaces < 0) throw new BusinessRuleError(GeneralSettingsErrorCode.GsInvalidNumberPrecision, `numberPrecision.decimalPlaces must be a non-negative integer, got: ${raw.decimalPlaces}`);
10297
10278
  return {
10298
- digits: obj.digits,
10299
- decimalPlaces: obj.decimalPlaces,
10300
- roundingMode: obj.roundingMode
10279
+ digits: raw.digits,
10280
+ decimalPlaces: raw.decimalPlaces,
10281
+ roundingMode: parseEnum(raw.roundingMode, VALID_ROUNDING_MODES, GeneralSettingsErrorCode.GsInvalidConfigStructure, `numberPrecision.roundingMode must be HALF_EVEN, UP, or DOWN, got: ${String(raw.roundingMode)}`)
10301
10282
  };
10302
10283
  }
10303
10284
  const GeneralSettingsConfigParser = { parse: (rawText) => {
10304
- if (rawText.trim().length === 0) throw new BusinessRuleError(GeneralSettingsErrorCode.GsEmptyConfigText, "General settings config text is empty");
10305
- let parsed;
10306
- try {
10307
- parsed = parse(rawText);
10308
- } catch (error) {
10309
- throw new BusinessRuleError(GeneralSettingsErrorCode.GsInvalidConfigYaml, `Failed to parse YAML: ${error instanceof Error ? error.message : String(error)}`);
10310
- }
10311
- if (!isRecord(parsed)) throw new BusinessRuleError(GeneralSettingsErrorCode.GsInvalidConfigStructure, "Config must be a YAML object");
10312
- const obj = parsed;
10285
+ const parsed = parseYamlConfig(rawText, {
10286
+ emptyConfigText: GeneralSettingsErrorCode.GsEmptyConfigText,
10287
+ invalidConfigYaml: GeneralSettingsErrorCode.GsInvalidConfigYaml,
10288
+ invalidConfigStructure: GeneralSettingsErrorCode.GsInvalidConfigStructure
10289
+ }, "General settings");
10313
10290
  let name;
10314
- if (obj.name !== void 0 && obj.name !== null) {
10315
- if (typeof obj.name !== "string") throw new BusinessRuleError(GeneralSettingsErrorCode.GsInvalidConfigStructure, "name must be a string");
10316
- name = obj.name;
10291
+ if (parsed.name !== void 0 && parsed.name !== null) {
10292
+ if (typeof parsed.name !== "string") throw new BusinessRuleError(GeneralSettingsErrorCode.GsInvalidConfigStructure, "name must be a string");
10293
+ name = parsed.name;
10317
10294
  }
10318
10295
  let description;
10319
- if (obj.description !== void 0 && obj.description !== null) {
10320
- if (typeof obj.description !== "string") throw new BusinessRuleError(GeneralSettingsErrorCode.GsInvalidConfigStructure, "description must be a string");
10321
- description = obj.description;
10296
+ if (parsed.description !== void 0 && parsed.description !== null) {
10297
+ if (typeof parsed.description !== "string") throw new BusinessRuleError(GeneralSettingsErrorCode.GsInvalidConfigStructure, "description must be a string");
10298
+ description = parsed.description;
10322
10299
  }
10323
10300
  let icon;
10324
- if (obj.icon !== void 0 && obj.icon !== null) icon = parseIcon(obj.icon);
10301
+ if (parsed.icon !== void 0 && parsed.icon !== null) icon = parseIcon(parsed.icon);
10325
10302
  let theme;
10326
- if (obj.theme !== void 0 && obj.theme !== null) {
10327
- if (typeof obj.theme !== "string" || !VALID_THEMES.has(obj.theme)) throw new BusinessRuleError(GeneralSettingsErrorCode.GsInvalidTheme, `theme must be WHITE, RED, GREEN, BLUE, YELLOW, BLACK, CLIPBOARD, BINDER, PENCIL, or CLIPS, got: ${String(obj.theme)}`);
10328
- theme = obj.theme;
10329
- }
10303
+ 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)}`);
10330
10304
  let titleField;
10331
- if (obj.titleField !== void 0 && obj.titleField !== null) titleField = parseTitleField(obj.titleField);
10332
- let enableThumbnails;
10333
- if (obj.enableThumbnails !== void 0 && obj.enableThumbnails !== null) enableThumbnails = Boolean(obj.enableThumbnails);
10334
- let enableBulkDeletion;
10335
- if (obj.enableBulkDeletion !== void 0 && obj.enableBulkDeletion !== null) enableBulkDeletion = Boolean(obj.enableBulkDeletion);
10336
- let enableComments;
10337
- if (obj.enableComments !== void 0 && obj.enableComments !== null) enableComments = Boolean(obj.enableComments);
10338
- let enableDuplicateRecord;
10339
- if (obj.enableDuplicateRecord !== void 0 && obj.enableDuplicateRecord !== null) enableDuplicateRecord = Boolean(obj.enableDuplicateRecord);
10340
- let enableInlineRecordEditing;
10341
- if (obj.enableInlineRecordEditing !== void 0 && obj.enableInlineRecordEditing !== null) enableInlineRecordEditing = Boolean(obj.enableInlineRecordEditing);
10305
+ if (parsed.titleField !== void 0 && parsed.titleField !== null) titleField = parseTitleField(parsed.titleField);
10306
+ const enableThumbnails = parseOptionalBoolean(parsed, "enableThumbnails");
10307
+ const enableBulkDeletion = parseOptionalBoolean(parsed, "enableBulkDeletion");
10308
+ const enableComments = parseOptionalBoolean(parsed, "enableComments");
10309
+ const enableDuplicateRecord = parseOptionalBoolean(parsed, "enableDuplicateRecord");
10310
+ const enableInlineRecordEditing = parseOptionalBoolean(parsed, "enableInlineRecordEditing");
10342
10311
  let numberPrecision;
10343
- if (obj.numberPrecision !== void 0 && obj.numberPrecision !== null) numberPrecision = parseNumberPrecision(obj.numberPrecision);
10312
+ if (parsed.numberPrecision !== void 0 && parsed.numberPrecision !== null) numberPrecision = parseNumberPrecision(parsed.numberPrecision);
10344
10313
  let firstMonthOfFiscalYear;
10345
- if (obj.firstMonthOfFiscalYear !== void 0 && obj.firstMonthOfFiscalYear !== null) {
10346
- if (typeof obj.firstMonthOfFiscalYear !== "number") throw new BusinessRuleError(GeneralSettingsErrorCode.GsInvalidConfigStructure, "firstMonthOfFiscalYear must be a number");
10347
- if (obj.firstMonthOfFiscalYear < 1 || obj.firstMonthOfFiscalYear > 12 || !Number.isInteger(obj.firstMonthOfFiscalYear)) throw new BusinessRuleError(GeneralSettingsErrorCode.GsInvalidConfigStructure, `firstMonthOfFiscalYear must be an integer between 1 and 12, got: ${obj.firstMonthOfFiscalYear}`);
10348
- firstMonthOfFiscalYear = obj.firstMonthOfFiscalYear;
10314
+ if (parsed.firstMonthOfFiscalYear !== void 0 && parsed.firstMonthOfFiscalYear !== null) {
10315
+ if (typeof parsed.firstMonthOfFiscalYear !== "number") throw new BusinessRuleError(GeneralSettingsErrorCode.GsInvalidConfigStructure, "firstMonthOfFiscalYear must be a number");
10316
+ 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}`);
10317
+ firstMonthOfFiscalYear = parsed.firstMonthOfFiscalYear;
10349
10318
  }
10350
10319
  return {
10351
10320
  ...name !== void 0 ? { name } : {},
@@ -10527,11 +10496,14 @@ function compareConfigs(local, remote) {
10527
10496
  details: `${rv} -> ${lv}`
10528
10497
  });
10529
10498
  }
10499
+ function formatValue(v) {
10500
+ return v === void 0 ? "(none)" : JSON.stringify(v);
10501
+ }
10530
10502
  function compareDeepEqual(field, l, r) {
10531
10503
  if (!deepEqual(l, r)) entries.push({
10532
10504
  type: "modified",
10533
10505
  field,
10534
- details: `${field} changed`
10506
+ details: `${formatValue(r)} -> ${formatValue(l)}`
10535
10507
  });
10536
10508
  }
10537
10509
  compareString("name", local.name, remote.name, DEFAULT_STRING);
@@ -10593,39 +10565,43 @@ var settings_default = define({
10593
10565
 
10594
10566
  //#endregion
10595
10567
  //#region src/core/domain/view/services/configParser.ts
10568
+ function parseDeviceType(name, raw) {
10569
+ if (raw === void 0 || raw === null) return void 0;
10570
+ const deviceStr = String(raw);
10571
+ if (!isDeviceType(deviceStr)) throw new BusinessRuleError(ViewErrorCode.VwInvalidDeviceType, `View "${name}" has invalid device type: ${deviceStr}. Must be DESKTOP or ANY`);
10572
+ return deviceStr;
10573
+ }
10596
10574
  function parseViewConfig(name, raw) {
10597
10575
  if (!isRecord(raw)) throw new BusinessRuleError(ViewErrorCode.VwInvalidConfigStructure, `View "${name}" must be an object`);
10598
- const obj = raw;
10599
- if (typeof obj.type !== "string" || !VALID_VIEW_TYPES.has(obj.type)) throw new BusinessRuleError(ViewErrorCode.VwInvalidViewType, `View "${name}" has invalid type: ${String(obj.type)}. Must be LIST, CALENDAR, or CUSTOM`);
10576
+ if (typeof raw.type !== "string" || !isViewType(raw.type)) throw new BusinessRuleError(ViewErrorCode.VwInvalidViewType, `View "${name}" has invalid type: ${String(raw.type)}. Must be LIST, CALENDAR, or CUSTOM`);
10577
+ if (raw.index !== void 0 && (typeof raw.index !== "number" || !Number.isInteger(raw.index) || raw.index < 0)) throw new BusinessRuleError(ViewErrorCode.VwInvalidIndex, `View "${name}" has invalid index: ${String(raw.index)}. Must be a non-negative integer`);
10578
+ if (raw.fields !== void 0 && !Array.isArray(raw.fields)) throw new BusinessRuleError(ViewErrorCode.VwInvalidConfigStructure, `View "${name}" has invalid fields: must be an array`);
10579
+ if (raw.pager !== void 0 && typeof raw.pager !== "boolean") throw new BusinessRuleError(ViewErrorCode.VwInvalidConfigStructure, `View "${name}" has invalid pager: must be a boolean`);
10580
+ const device = parseDeviceType(name, raw.device);
10600
10581
  return {
10601
- type: obj.type,
10602
- index: typeof obj.index === "number" ? obj.index : 0,
10582
+ type: raw.type,
10583
+ index: typeof raw.index === "number" ? raw.index : 0,
10603
10584
  name,
10604
- ...obj.builtinType !== void 0 && { builtinType: String(obj.builtinType) },
10605
- ...Array.isArray(obj.fields) && { fields: obj.fields.map(String) },
10606
- ...obj.date !== void 0 && { date: String(obj.date) },
10607
- ...obj.title !== void 0 && { title: String(obj.title) },
10608
- ...obj.html !== void 0 && { html: String(obj.html) },
10609
- ...obj.pager !== void 0 && { pager: Boolean(obj.pager) },
10610
- ...obj.device !== void 0 && { device: (() => {
10611
- const deviceStr = String(obj.device);
10612
- if (!VALID_DEVICE_TYPES.has(deviceStr)) throw new BusinessRuleError(ViewErrorCode.VwInvalidDeviceType, `View "${name}" has invalid device type: ${deviceStr}. Must be DESKTOP or ANY`);
10613
- return deviceStr;
10614
- })() },
10615
- ...obj.filterCond !== void 0 && { filterCond: String(obj.filterCond) },
10616
- ...obj.sort !== void 0 && { sort: String(obj.sort) }
10585
+ ...raw.builtinType !== void 0 && { builtinType: String(raw.builtinType) },
10586
+ ...Array.isArray(raw.fields) && { fields: raw.fields.map((f, i) => {
10587
+ if (typeof f !== "string") throw new BusinessRuleError(ViewErrorCode.VwInvalidConfigStructure, `View "${name}" has non-string field at index ${i}: ${String(f)}`);
10588
+ return f;
10589
+ }) },
10590
+ ...raw.date !== void 0 && { date: String(raw.date) },
10591
+ ...raw.title !== void 0 && { title: String(raw.title) },
10592
+ ...raw.html !== void 0 && { html: String(raw.html) },
10593
+ ...raw.pager !== void 0 && { pager: raw.pager },
10594
+ ...device !== void 0 && { device },
10595
+ ...raw.filterCond !== void 0 && { filterCond: String(raw.filterCond) },
10596
+ ...raw.sort !== void 0 && { sort: String(raw.sort) }
10617
10597
  };
10618
10598
  }
10619
10599
  const ViewConfigParser = { parse: (rawText) => {
10620
- if (rawText.trim().length === 0) throw new BusinessRuleError(ViewErrorCode.VwEmptyConfigText, "View config text is empty");
10621
- let parsed;
10622
- try {
10623
- parsed = parse(rawText);
10624
- } catch (error) {
10625
- throw new BusinessRuleError(ViewErrorCode.VwInvalidConfigYaml, `Failed to parse YAML: ${error instanceof Error ? error.message : String(error)}`);
10626
- }
10627
- if (!isRecord(parsed)) throw new BusinessRuleError(ViewErrorCode.VwInvalidConfigStructure, "Config must be a YAML object");
10628
- const obj = parsed;
10600
+ const obj = parseYamlConfig(rawText, {
10601
+ emptyConfigText: ViewErrorCode.VwEmptyConfigText,
10602
+ invalidConfigYaml: ViewErrorCode.VwInvalidConfigYaml,
10603
+ invalidConfigStructure: ViewErrorCode.VwInvalidConfigStructure
10604
+ }, "View");
10629
10605
  if (!isRecord(obj.views)) throw new BusinessRuleError(ViewErrorCode.VwInvalidConfigStructure, "Config must have a \"views\" object");
10630
10606
  const viewsObj = obj.views;
10631
10607
  const views = {};
@@ -10777,7 +10753,6 @@ var capture_default = define({
10777
10753
  //#region src/core/domain/view/services/diffDetector.ts
10778
10754
  function describeChanges(local, remote) {
10779
10755
  const changes = [];
10780
- if (local.name !== remote.name) changes.push(`name: "${remote.name}" -> "${local.name}"`);
10781
10756
  if (local.type !== remote.type) changes.push(`type: ${remote.type} -> ${local.type}`);
10782
10757
  if ((local.builtinType ?? "") !== (remote.builtinType ?? "")) changes.push("builtinType changed");
10783
10758
  if (local.index !== remote.index) changes.push(`index: ${remote.index} -> ${local.index}`);
@@ -10792,29 +10767,26 @@ function describeChanges(local, remote) {
10792
10767
  return changes;
10793
10768
  }
10794
10769
  const ViewDiffDetector = { detect: (localViews, remoteViews) => {
10795
- const entries = [];
10796
- for (const [name, localView] of Object.entries(localViews)) {
10797
- const remoteView = remoteViews[name];
10798
- if (remoteView === void 0) entries.push({
10770
+ return buildDiffResult(detectRecordDiff(localViews, remoteViews, {
10771
+ onAdded: (name) => ({
10799
10772
  type: "added",
10800
10773
  viewName: name,
10801
10774
  details: "new view"
10802
- });
10803
- else {
10775
+ }),
10776
+ onModified: (name, localView, remoteView) => {
10804
10777
  const changes = describeChanges(localView, remoteView);
10805
- if (changes.length > 0) entries.push({
10778
+ if (changes.length > 0) return {
10806
10779
  type: "modified",
10807
10780
  viewName: name,
10808
10781
  details: changes.join(", ")
10809
- });
10810
- }
10811
- }
10812
- for (const name of Object.keys(remoteViews)) if (localViews[name] === void 0) entries.push({
10813
- type: "deleted",
10814
- viewName: name,
10815
- details: "removed"
10816
- });
10817
- return buildDiffResult(entries);
10782
+ };
10783
+ },
10784
+ onDeleted: (name) => ({
10785
+ type: "deleted",
10786
+ viewName: name,
10787
+ details: "removed"
10788
+ })
10789
+ }));
10818
10790
  } };
10819
10791
 
10820
10792
  //#endregion