kintone-migrator 0.24.0 → 0.24.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/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
@@ -76,13 +77,15 @@ const FieldPermissionErrorCode = {
76
77
  //#region src/core/domain/formSchema/errorCode.ts
77
78
  const FormSchemaErrorCode = {
78
79
  FsEmptyFieldCode: "FS_EMPTY_FIELD_CODE",
80
+ FsInvalidFieldCode: "FS_INVALID_FIELD_CODE",
79
81
  FsEmptySchemaText: "FS_EMPTY_SCHEMA_TEXT",
80
82
  FsInvalidSchemaFormat: "FS_INVALID_SCHEMA_FORMAT",
81
83
  FsInvalidSchemaStructure: "FS_INVALID_SCHEMA_STRUCTURE",
82
84
  FsDuplicateFieldCode: "FS_DUPLICATE_FIELD_CODE",
83
85
  FsInvalidFieldType: "FS_INVALID_FIELD_TYPE",
84
86
  FsInvalidLayoutStructure: "FS_INVALID_LAYOUT_STRUCTURE",
85
- FsInvalidDecorationElement: "FS_INVALID_DECORATION_ELEMENT"
87
+ FsInvalidDecorationElement: "FS_INVALID_DECORATION_ELEMENT",
88
+ FsEmptyFields: "FS_EMPTY_FIELDS"
86
89
  };
87
90
 
88
91
  //#endregion
@@ -104,7 +107,9 @@ const NotificationErrorCode = {
104
107
  NtInvalidEntityType: "NT_INVALID_ENTITY_TYPE",
105
108
  NtEmptyEntityCode: "NT_EMPTY_ENTITY_CODE",
106
109
  NtMissingRequiredField: "NT_MISSING_REQUIRED_FIELD",
107
- NtConflictingTimingFields: "NT_CONFLICTING_TIMING_FIELDS"
110
+ NtConflictingTimingFields: "NT_CONFLICTING_TIMING_FIELDS",
111
+ NtInvalidHoursLater: "NT_INVALID_HOURS_LATER",
112
+ NtInvalidDaysLater: "NT_INVALID_DAYS_LATER"
108
113
  };
109
114
 
110
115
  //#endregion
@@ -113,7 +118,8 @@ const PluginErrorCode = {
113
118
  PlEmptyConfigText: "PL_EMPTY_CONFIG_TEXT",
114
119
  PlInvalidConfigYaml: "PL_INVALID_CONFIG_YAML",
115
120
  PlInvalidConfigStructure: "PL_INVALID_CONFIG_STRUCTURE",
116
- PlEmptyPluginId: "PL_EMPTY_PLUGIN_ID"
121
+ PlEmptyPluginId: "PL_EMPTY_PLUGIN_ID",
122
+ PlDuplicatePluginId: "PL_DUPLICATE_PLUGIN_ID"
117
123
  };
118
124
 
119
125
  //#endregion
@@ -135,6 +141,7 @@ const ProjectConfigErrorCode = {
135
141
  PcEmptyApps: "PC_EMPTY_APPS",
136
142
  PcEmptyAppId: "PC_EMPTY_APP_ID",
137
143
  PcEmptyAppName: "PC_EMPTY_APP_NAME",
144
+ PcInvalidAppName: "PC_INVALID_APP_NAME",
138
145
  PcInvalidConfigStructure: "PC_INVALID_CONFIG_STRUCTURE",
139
146
  PcInvalidAuthConfig: "PC_INVALID_AUTH_CONFIG"
140
147
  };
@@ -181,7 +188,8 @@ const ViewErrorCode = {
181
188
  VwInvalidConfigStructure: "VW_INVALID_CONFIG_STRUCTURE",
182
189
  VwInvalidViewType: "VW_INVALID_VIEW_TYPE",
183
190
  VwInvalidDeviceType: "VW_INVALID_DEVICE_TYPE",
184
- VwEmptyViewName: "VW_EMPTY_VIEW_NAME"
191
+ VwEmptyViewName: "VW_EMPTY_VIEW_NAME",
192
+ VwInvalidIndex: "VW_INVALID_INDEX"
185
193
  };
186
194
 
187
195
  //#endregion
@@ -329,7 +337,7 @@ function isSystemError(error) {
329
337
  * Excludes built-in types (Date, RegExp, Map, Set) that are technically
330
338
  * objects but should not be treated as string-keyed records.
331
339
  */
332
- function isRecord$1(value) {
340
+ function isRecord(value) {
333
341
  return typeof value === "object" && value !== null && !Array.isArray(value) && !(value instanceof Date) && !(value instanceof RegExp) && !(value instanceof Map) && !(value instanceof Set);
334
342
  }
335
343
 
@@ -343,79 +351,100 @@ function isRecord$1(value) {
343
351
  * Narrows `unknown` to `{ code: string }`.
344
352
  */
345
353
  function hasCode(value) {
346
- return isRecord$1(value) && typeof value.code === "string";
354
+ return isRecord(value) && typeof value.code === "string";
347
355
  }
348
356
  /**
349
357
  * Narrows `unknown` to `{ value: Record<string, { value: unknown }> }`.
350
358
  * Used when processing kintone subtable rows.
351
359
  */
352
360
  function isKintoneSubtableRow(value) {
353
- if (!isRecord$1(value)) return false;
354
- if (!isRecord$1(value.value)) return false;
355
- return Object.values(value.value).every((cell) => isRecord$1(cell) && "value" in cell);
361
+ if (!isRecord(value)) return false;
362
+ if (!isRecord(value.value)) return false;
363
+ return Object.values(value.value).every((cell) => isRecord(cell) && "value" in cell);
356
364
  }
357
365
  /**
358
366
  * Narrows `unknown` to `{ type?: string }`.
359
367
  */
360
368
  function hasOptionalType(value) {
361
- if (!isRecord$1(value)) return false;
369
+ if (!isRecord(value)) return false;
362
370
  return value.type === void 0 || typeof value.type === "string";
363
371
  }
364
372
 
373
+ //#endregion
374
+ //#region src/core/domain/services/yamlConfigParser.ts
375
+ function parseYamlConfig(rawText, errorCodes, domainLabel) {
376
+ if (rawText.trim().length === 0) throw new BusinessRuleError(errorCodes.emptyConfigText, `${domainLabel} config text is empty`);
377
+ let parsed;
378
+ try {
379
+ parsed = parse(rawText);
380
+ } catch (error) {
381
+ throw new BusinessRuleError(errorCodes.invalidConfigYaml, `Failed to parse ${domainLabel} YAML: ${error instanceof Error ? error.message : String(error)}`, error);
382
+ }
383
+ if (!isRecord(parsed)) throw new BusinessRuleError(errorCodes.invalidConfigStructure, `${domainLabel} config must be a YAML object`);
384
+ return parsed;
385
+ }
386
+
365
387
  //#endregion
366
388
  //#region src/core/domain/action/valueObject.ts
367
- const VALID_SRC_TYPES = new Set(["FIELD", "RECORD_URL"]);
368
- const VALID_ENTITY_TYPES$10 = new Set([
389
+ const SRC_TYPES = ["FIELD", "RECORD_URL"];
390
+ const VALID_SRC_TYPES = new Set(SRC_TYPES);
391
+ function isActionMappingSrcType(value) {
392
+ return VALID_SRC_TYPES.has(value);
393
+ }
394
+ const ENTITY_TYPES$1 = [
369
395
  "USER",
370
396
  "GROUP",
371
397
  "ORGANIZATION"
372
- ]);
398
+ ];
399
+ const VALID_ENTITY_TYPES$9 = new Set(ENTITY_TYPES$1);
400
+ function isActionEntityType(value) {
401
+ return VALID_ENTITY_TYPES$9.has(value);
402
+ }
373
403
 
374
404
  //#endregion
375
405
  //#region src/core/domain/action/services/configParser.ts
376
406
  function parseDestApp(raw, actionName) {
377
- if (!isRecord$1(raw)) throw new BusinessRuleError(ActionErrorCode.AcInvalidConfigStructure, `Action "${actionName}" destApp must be an object`);
378
- const obj = raw;
407
+ if (!isRecord(raw)) throw new BusinessRuleError(ActionErrorCode.AcInvalidConfigStructure, `Action "${actionName}" destApp must be an object`);
379
408
  const result = {};
380
- if (obj.app !== void 0 && obj.app !== null) result.app = String(obj.app);
381
- if (obj.code !== void 0 && obj.code !== null) result.code = String(obj.code);
409
+ if (raw.app !== void 0 && raw.app !== null) result.app = String(raw.app);
410
+ if (raw.code !== void 0 && raw.code !== null) result.code = String(raw.code);
411
+ 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`);
382
412
  return result;
383
413
  }
384
414
  function parseMapping(raw, index, actionName) {
385
- if (!isRecord$1(raw)) throw new BusinessRuleError(ActionErrorCode.AcInvalidConfigStructure, `Action "${actionName}" mapping at index ${index} must be an object`);
386
- const obj = raw;
387
- 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`);
388
- 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`);
389
- return {
390
- srcType: obj.srcType,
391
- destField: obj.destField,
392
- ...obj.srcField !== void 0 && obj.srcField !== null && typeof obj.srcField === "string" ? { srcField: obj.srcField } : {}
415
+ if (!isRecord(raw)) throw new BusinessRuleError(ActionErrorCode.AcInvalidConfigStructure, `Action "${actionName}" mapping at index ${index} must be an object`);
416
+ 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`);
417
+ 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`);
418
+ const result = {
419
+ srcType: raw.srcType,
420
+ destField: raw.destField,
421
+ ...raw.srcField !== void 0 && raw.srcField !== null && typeof raw.srcField === "string" ? { srcField: raw.srcField } : {}
393
422
  };
423
+ 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`);
424
+ return result;
394
425
  }
395
426
  function parseEntity$4(raw, index, actionName) {
396
- if (!isRecord$1(raw)) throw new BusinessRuleError(ActionErrorCode.AcInvalidConfigStructure, `Action "${actionName}" entity at index ${index} must be an object`);
397
- const obj = raw;
398
- 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`);
399
- 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`);
427
+ if (!isRecord(raw)) throw new BusinessRuleError(ActionErrorCode.AcInvalidConfigStructure, `Action "${actionName}" entity at index ${index} must be an object`);
428
+ 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`);
429
+ 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`);
400
430
  return {
401
- type: obj.type,
402
- code: obj.code
431
+ type: raw.type,
432
+ code: raw.code
403
433
  };
404
434
  }
405
435
  function parseActionConfig(raw, actionName) {
406
- if (!isRecord$1(raw)) throw new BusinessRuleError(ActionErrorCode.AcInvalidConfigStructure, `Action "${actionName}" must be an object`);
407
- const obj = raw;
408
436
  if (actionName.length === 0) throw new BusinessRuleError(ActionErrorCode.AcEmptyActionName, "Action name (key) must not be empty");
409
- if (typeof obj.index !== "number") throw new BusinessRuleError(ActionErrorCode.AcInvalidConfigStructure, `Action "${actionName}" must have a numeric "index" property`);
410
- if (!isRecord$1(obj.destApp)) throw new BusinessRuleError(ActionErrorCode.AcInvalidConfigStructure, `Action "${actionName}" must have a "destApp" object`);
411
- const destApp = parseDestApp(obj.destApp, actionName);
412
- if (!Array.isArray(obj.mappings)) throw new BusinessRuleError(ActionErrorCode.AcInvalidConfigStructure, `Action "${actionName}" must have a "mappings" array`);
413
- const mappings = obj.mappings.map((item, i) => parseMapping(item, i, actionName));
414
- if (!Array.isArray(obj.entities)) throw new BusinessRuleError(ActionErrorCode.AcInvalidConfigStructure, `Action "${actionName}" must have an "entities" array`);
415
- const entities = obj.entities.map((item, i) => parseEntity$4(item, i, actionName));
416
- const filterCond = typeof obj.filterCond === "string" ? obj.filterCond : "";
417
- return {
418
- index: obj.index,
437
+ if (!isRecord(raw)) throw new BusinessRuleError(ActionErrorCode.AcInvalidConfigStructure, `Action "${actionName}" must be an object`);
438
+ 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`);
439
+ if (!isRecord(raw.destApp)) throw new BusinessRuleError(ActionErrorCode.AcInvalidConfigStructure, `Action "${actionName}" must have a "destApp" object`);
440
+ const destApp = parseDestApp(raw.destApp, actionName);
441
+ if (!Array.isArray(raw.mappings)) throw new BusinessRuleError(ActionErrorCode.AcInvalidConfigStructure, `Action "${actionName}" must have a "mappings" array`);
442
+ const mappings = raw.mappings.map((item, i) => parseMapping(item, i, actionName));
443
+ if (!Array.isArray(raw.entities)) throw new BusinessRuleError(ActionErrorCode.AcInvalidConfigStructure, `Action "${actionName}" must have an "entities" array`);
444
+ const entities = raw.entities.map((item, i) => parseEntity$4(item, i, actionName));
445
+ const filterCond = typeof raw.filterCond === "string" ? raw.filterCond : "";
446
+ return {
447
+ index: raw.index,
419
448
  name: actionName,
420
449
  destApp,
421
450
  mappings,
@@ -424,19 +453,20 @@ function parseActionConfig(raw, actionName) {
424
453
  };
425
454
  }
426
455
  const ActionConfigParser = { parse: (rawText) => {
427
- if (rawText.trim().length === 0) throw new BusinessRuleError(ActionErrorCode.AcEmptyConfigText, "Action config text is empty");
428
- let parsed;
429
- try {
430
- parsed = parse(rawText);
431
- } catch (error) {
432
- throw new BusinessRuleError(ActionErrorCode.AcInvalidConfigYaml, `Failed to parse YAML: ${error instanceof Error ? error.message : String(error)}`);
433
- }
434
- if (!isRecord$1(parsed)) throw new BusinessRuleError(ActionErrorCode.AcInvalidConfigStructure, "Config must be a YAML object");
435
- const obj = parsed;
436
- if (!isRecord$1(obj.actions)) throw new BusinessRuleError(ActionErrorCode.AcInvalidConfigStructure, "Config must have an \"actions\" object");
456
+ const obj = parseYamlConfig(rawText, {
457
+ emptyConfigText: ActionErrorCode.AcEmptyConfigText,
458
+ invalidConfigYaml: ActionErrorCode.AcInvalidConfigYaml,
459
+ invalidConfigStructure: ActionErrorCode.AcInvalidConfigStructure
460
+ }, "Action");
461
+ if (!isRecord(obj.actions)) throw new BusinessRuleError(ActionErrorCode.AcInvalidConfigStructure, "Config must have an \"actions\" object");
437
462
  const rawActions = obj.actions;
438
463
  const actions = {};
439
464
  for (const [key, value] of Object.entries(rawActions)) actions[key] = parseActionConfig(value, key);
465
+ const seenIndices = /* @__PURE__ */ new Set();
466
+ for (const [key, action] of Object.entries(actions)) {
467
+ if (seenIndices.has(action.index)) throw new BusinessRuleError(ActionErrorCode.AcDuplicateIndex, `Duplicate action index ${action.index} found in action "${key}"`);
468
+ seenIndices.add(action.index);
469
+ }
440
470
  return { actions };
441
471
  } };
442
472
 
@@ -468,7 +498,7 @@ function fromKintoneDestApp(raw) {
468
498
  return result;
469
499
  }
470
500
  function fromKintoneMapping(raw) {
471
- if (!VALID_SRC_TYPES.has(raw.srcType)) throw new SystemError(SystemErrorCode.ExternalApiError, `Unexpected srcType value from kintone API: ${raw.srcType}`);
501
+ if (!isActionMappingSrcType(raw.srcType)) throw new SystemError(SystemErrorCode.ExternalApiError, `Unexpected srcType value from kintone API: ${raw.srcType}`);
472
502
  return {
473
503
  srcType: raw.srcType,
474
504
  destField: raw.destField,
@@ -476,15 +506,17 @@ function fromKintoneMapping(raw) {
476
506
  };
477
507
  }
478
508
  function fromKintoneEntity$4(raw) {
479
- if (!VALID_ENTITY_TYPES$10.has(raw.type)) throw new SystemError(SystemErrorCode.ExternalApiError, `Unexpected entity type from kintone API: ${raw.type}`);
509
+ if (!isActionEntityType(raw.type)) throw new SystemError(SystemErrorCode.ExternalApiError, `Unexpected entity type from kintone API: ${raw.type}`);
480
510
  return {
481
511
  type: raw.type,
482
512
  code: raw.code
483
513
  };
484
514
  }
485
515
  function fromKintoneAction$1(raw) {
516
+ const index = Number(raw.index);
517
+ if (!Number.isFinite(index)) throw new SystemError(SystemErrorCode.ExternalApiError, `Unexpected non-numeric index from kintone API: ${raw.index}`);
486
518
  return {
487
- index: Number(raw.index),
519
+ index,
488
520
  name: raw.name,
489
521
  destApp: fromKintoneDestApp(raw.destApp),
490
522
  mappings: raw.mappings.map(fromKintoneMapping),
@@ -662,25 +694,42 @@ function createLocalFileActionStorage(filePath) {
662
694
  }
663
695
 
664
696
  //#endregion
665
- //#region src/core/adapters/kintone/customizationConfigurator.ts
666
- const VALID_SCOPES$1 = new Set([
697
+ //#region src/core/domain/customization/valueObject.ts
698
+ const SCOPES = [
667
699
  "ALL",
668
700
  "ADMIN",
669
701
  "NONE"
670
- ]);
702
+ ];
703
+ const VALID_SCOPES = new Set(SCOPES);
704
+ function isCustomizationScope(value) {
705
+ return VALID_SCOPES.has(value);
706
+ }
707
+ const RESOURCE_TYPES = ["FILE", "URL"];
708
+ const VALID_RESOURCE_TYPES = new Set(RESOURCE_TYPES);
709
+ function isResourceType(value) {
710
+ return VALID_RESOURCE_TYPES.has(value);
711
+ }
712
+ const DEFAULT_CUSTOMIZATION_SCOPE = "ALL";
713
+
714
+ //#endregion
715
+ //#region src/core/adapters/kintone/customizationConfigurator.ts
671
716
  function fromKintoneResource(raw) {
672
- if (raw.type === "FILE" && raw.file) return {
673
- type: "FILE",
674
- file: {
675
- fileKey: raw.file.fileKey,
676
- name: raw.file.name,
677
- contentType: raw.file.contentType,
678
- size: raw.file.size
679
- }
680
- };
717
+ if (raw.type === "FILE") {
718
+ if (!raw.file) throw new SystemError(SystemErrorCode.ExternalApiError, "FILE resource from kintone API is missing file metadata");
719
+ return {
720
+ type: "FILE",
721
+ file: {
722
+ fileKey: raw.file.fileKey,
723
+ name: raw.file.name,
724
+ contentType: raw.file.contentType,
725
+ size: raw.file.size
726
+ }
727
+ };
728
+ }
729
+ if (!raw.url) throw new SystemError(SystemErrorCode.ExternalApiError, "URL resource from kintone API is missing url property");
681
730
  return {
682
731
  type: "URL",
683
- url: raw.url ?? ""
732
+ url: raw.url
684
733
  };
685
734
  }
686
735
  function fromKintoneResourceList(raw) {
@@ -711,7 +760,7 @@ var KintoneCustomizationConfigurator = class {
711
760
  preview: true
712
761
  });
713
762
  const rawScope = String(response.scope);
714
- if (!VALID_SCOPES$1.has(rawScope)) throw new SystemError(SystemErrorCode.ExternalApiError, `Unexpected scope value from kintone API: ${rawScope}`);
763
+ if (!isCustomizationScope(rawScope)) throw new SystemError(SystemErrorCode.ExternalApiError, `Unexpected scope value from kintone API: ${rawScope}`);
715
764
  return {
716
765
  scope: rawScope,
717
766
  desktop: {
@@ -791,10 +840,40 @@ var KintoneFileUploader = class {
791
840
  }
792
841
  };
793
842
 
843
+ //#endregion
844
+ //#region src/lib/charValidation.ts
845
+ /** Returns true if the string contains any control characters (0x00–0x1f or 0x7f). */
846
+ function hasControlChars(s) {
847
+ for (let i = 0; i < s.length; i++) {
848
+ const ch = s.charCodeAt(i);
849
+ if (ch <= 31 || ch === 127) return true;
850
+ }
851
+ return false;
852
+ }
853
+ /** Replaces control characters (0x00–0x1f, 0x7f) with escaped `\\xNN` representation for safe display. */
854
+ function sanitizeForDisplay(s) {
855
+ let result = "";
856
+ for (let i = 0; i < s.length; i++) {
857
+ const ch = s.charCodeAt(i);
858
+ if (ch <= 31 || ch === 127) result += `\\x${ch.toString(16).padStart(2, "0")}`;
859
+ else result += s[i];
860
+ }
861
+ return result;
862
+ }
863
+
794
864
  //#endregion
795
865
  //#region src/core/domain/formSchema/valueObject.ts
866
+ function hasInvalidFieldCodeChars(code) {
867
+ if (hasControlChars(code)) return true;
868
+ for (let i = 0; i < code.length; i++) {
869
+ const c = code[i];
870
+ if (c === "/" || c === "\\") return true;
871
+ }
872
+ return false;
873
+ }
796
874
  const FieldCode = { create: (code) => {
797
875
  if (code.length === 0) throw new BusinessRuleError(FormSchemaErrorCode.FsEmptyFieldCode, "Field code cannot be empty");
876
+ if (hasInvalidFieldCodeChars(code)) throw new BusinessRuleError(FormSchemaErrorCode.FsInvalidFieldCode, `Field code "${sanitizeForDisplay(code)}" contains invalid characters`);
798
877
  return code;
799
878
  } };
800
879
 
@@ -1648,16 +1727,31 @@ async function executeMultiApp(plan, executor) {
1648
1727
 
1649
1728
  //#endregion
1650
1729
  //#region src/core/domain/projectConfig/valueObject.ts
1730
+ const INVALID_APP_NAME_CHARS = new Set([
1731
+ "/",
1732
+ "\\",
1733
+ ":",
1734
+ "*",
1735
+ "?",
1736
+ "\"",
1737
+ "<",
1738
+ ">",
1739
+ "|"
1740
+ ]);
1741
+ function hasInvalidAppNameChars(name) {
1742
+ if (hasControlChars(name)) return true;
1743
+ for (let i = 0; i < name.length; i++) if (INVALID_APP_NAME_CHARS.has(name[i])) return true;
1744
+ return false;
1745
+ }
1651
1746
  const AppName = { create: (name) => {
1652
1747
  if (name.length === 0) throw new BusinessRuleError(ProjectConfigErrorCode.PcEmptyAppName, "App name cannot be empty");
1748
+ if (hasInvalidAppNameChars(name)) throw new BusinessRuleError(ProjectConfigErrorCode.PcInvalidAppName, `App name "${sanitizeForDisplay(name)}" contains invalid characters (path separators or control characters are not allowed)`);
1749
+ if (name === "." || name === "..") throw new BusinessRuleError(ProjectConfigErrorCode.PcInvalidAppName, `App name "${name}" is not allowed (reserved path component)`);
1653
1750
  return name;
1654
1751
  } };
1655
1752
 
1656
1753
  //#endregion
1657
1754
  //#region src/core/domain/projectConfig/services/configParser.ts
1658
- function isRecord(value) {
1659
- return typeof value === "object" && value !== null && !Array.isArray(value);
1660
- }
1661
1755
  function asOptionalString(value) {
1662
1756
  return typeof value === "string" ? value : void 0;
1663
1757
  }
@@ -1731,25 +1825,27 @@ function parseAuth(raw) {
1731
1825
  if (!raw) return void 0;
1732
1826
  const apiToken = asOptionalString(raw.apiToken);
1733
1827
  if (apiToken !== void 0) {
1734
- if (apiToken.trim().length === 0) throw new BusinessRuleError(ProjectConfigErrorCode.PcInvalidAuthConfig, "apiToken must not be empty");
1828
+ const trimmed = apiToken.trim();
1829
+ if (trimmed.length === 0) throw new BusinessRuleError(ProjectConfigErrorCode.PcInvalidAuthConfig, "apiToken must not be empty");
1735
1830
  return {
1736
1831
  type: "apiToken",
1737
- apiToken
1832
+ apiToken: trimmed
1738
1833
  };
1739
1834
  }
1740
1835
  const username = asOptionalString(raw.username);
1741
1836
  const password = asOptionalString(raw.password);
1742
1837
  if (username !== void 0 && password !== void 0) {
1743
- if (username.trim().length === 0 || password.trim().length === 0) throw new BusinessRuleError(ProjectConfigErrorCode.PcInvalidAuthConfig, "username and password must not be empty");
1838
+ const trimmedUsername = username.trim();
1839
+ if (trimmedUsername.length === 0 || password.length === 0) throw new BusinessRuleError(ProjectConfigErrorCode.PcInvalidAuthConfig, "username and password must not be empty");
1744
1840
  return {
1745
1841
  type: "password",
1746
- username,
1842
+ username: trimmedUsername,
1747
1843
  password
1748
1844
  };
1749
1845
  }
1750
1846
  throw new BusinessRuleError(ProjectConfigErrorCode.PcInvalidAuthConfig, "Auth must have either apiToken or username/password");
1751
1847
  }
1752
- const ConfigParser$1 = { parse: parseProjectConfig };
1848
+ const ConfigParser = { parse: parseProjectConfig };
1753
1849
 
1754
1850
  //#endregion
1755
1851
  //#region src/core/application/projectConfig/loadProjectConfig.ts
@@ -1765,7 +1861,7 @@ function loadProjectConfig(input) {
1765
1861
  } catch (cause) {
1766
1862
  throw new ValidationError(ValidationErrorCode.InvalidInput, "Invalid YAML syntax in config file", cause);
1767
1863
  }
1768
- return ConfigParser$1.parse(raw);
1864
+ return ConfigParser.parse(raw);
1769
1865
  }
1770
1866
 
1771
1867
  //#endregion
@@ -1804,7 +1900,8 @@ function resolveExecutionOrder(apps) {
1804
1900
  queue.push(...nextBatch);
1805
1901
  }
1806
1902
  if (orderedNames.length !== apps.size) {
1807
- const cycleNodes = [...apps.keys()].filter((name) => !orderedNames.includes(name));
1903
+ const orderedSet = new Set(orderedNames);
1904
+ const cycleNodes = [...apps.keys()].filter((name) => !orderedSet.has(name));
1808
1905
  throw new BusinessRuleError(ProjectConfigErrorCode.PcCircularDependency, `Circular dependency detected among: ${cycleNodes.join(", ")}`);
1809
1906
  }
1810
1907
  return { orderedApps: orderedNames.flatMap((name) => {
@@ -2346,6 +2443,20 @@ var apply_default$12 = define({
2346
2443
  }
2347
2444
  });
2348
2445
 
2446
+ //#endregion
2447
+ //#region src/core/domain/services/yamlConfigSerializer.ts
2448
+ function serializeToYaml(data) {
2449
+ try {
2450
+ return stringify(data, {
2451
+ lineWidth: 0,
2452
+ defaultKeyType: "PLAIN",
2453
+ defaultStringType: "PLAIN"
2454
+ });
2455
+ } catch (error) {
2456
+ throw new BusinessRuleError(BusinessRuleErrorCode.AcInvalidConfigStructure, `Failed to serialize config to YAML: ${error instanceof Error ? error.message : String(error)}`, error);
2457
+ }
2458
+ }
2459
+
2349
2460
  //#endregion
2350
2461
  //#region src/core/domain/action/services/configSerializer.ts
2351
2462
  function serializeDestApp(destApp) {
@@ -2382,11 +2493,7 @@ const ActionConfigSerializer = { serialize: (config) => {
2382
2493
  const actions = {};
2383
2494
  for (const [key, value] of Object.entries(config.actions)) actions[key] = serializeActionConfig(value);
2384
2495
  serialized.actions = actions;
2385
- return stringify(serialized, {
2386
- lineWidth: 0,
2387
- defaultKeyType: "PLAIN",
2388
- defaultStringType: "PLAIN"
2389
- });
2496
+ return serializeToYaml(serialized);
2390
2497
  } };
2391
2498
 
2392
2499
  //#endregion
@@ -2505,7 +2612,7 @@ function deepEqualInner(a, b, seen) {
2505
2612
  if (objB instanceof Map) return false;
2506
2613
  if (objA instanceof Set) return isSetEqual(objA, objB, seen);
2507
2614
  if (objB instanceof Set) return false;
2508
- if (isRecord$1(objA) && isRecord$1(objB)) return isRecordEqual(objA, objB, seen);
2615
+ if (isRecord(objA) && isRecord(objB)) return isRecordEqual(objA, objB, seen);
2509
2616
  return false;
2510
2617
  }
2511
2618
  /**
@@ -2528,7 +2635,7 @@ function deepEqualInner(a, b, seen) {
2528
2635
  * (e.g. `a.self = a` vs `b.self = b`) return `false`. This is a conservative
2529
2636
  * approach that prevents infinite recursion but may produce false negatives.
2530
2637
  */
2531
- function deepEqual$1(a, b) {
2638
+ function deepEqual(a, b) {
2532
2639
  return deepEqualInner(a, b, /* @__PURE__ */ new WeakSet());
2533
2640
  }
2534
2641
 
@@ -2560,43 +2667,55 @@ function buildDiffResult(entries, warnings = []) {
2560
2667
  };
2561
2668
  }
2562
2669
 
2670
+ //#endregion
2671
+ //#region src/core/domain/services/recordDiffDetector.ts
2672
+ function detectRecordDiff(localRecord, remoteRecord, callbacks) {
2673
+ const entries = [];
2674
+ for (const [key, localValue] of Object.entries(localRecord)) if (!Object.hasOwn(remoteRecord, key)) entries.push(callbacks.onAdded(key, localValue));
2675
+ else {
2676
+ const entry = callbacks.onModified(key, localValue, remoteRecord[key]);
2677
+ if (entry !== void 0) entries.push(entry);
2678
+ }
2679
+ for (const [key, remoteValue] of Object.entries(remoteRecord)) if (!Object.hasOwn(localRecord, key)) entries.push(callbacks.onDeleted(key, remoteValue));
2680
+ return entries;
2681
+ }
2682
+
2563
2683
  //#endregion
2564
2684
  //#region src/core/domain/action/services/diffDetector.ts
2565
2685
  function compareActions$1(local, remote) {
2566
2686
  const diffs = [];
2567
2687
  if (local.index !== remote.index) diffs.push(`index: ${remote.index} -> ${local.index}`);
2568
- if (local.name !== remote.name) diffs.push(`name: "${remote.name}" -> "${local.name}"`);
2569
- if (!deepEqual$1(local.destApp, remote.destApp)) diffs.push("destApp changed");
2688
+ if (!deepEqual(local.destApp, remote.destApp)) diffs.push("destApp changed");
2570
2689
  if (local.filterCond !== remote.filterCond) diffs.push("filterCond changed");
2571
- if (!deepEqual$1(local.mappings, remote.mappings)) if (local.mappings.length !== remote.mappings.length) diffs.push(`mappings: ${remote.mappings.length} -> ${local.mappings.length}`);
2690
+ if (!deepEqual(local.mappings, remote.mappings)) if (local.mappings.length !== remote.mappings.length) diffs.push(`mappings: ${remote.mappings.length} -> ${local.mappings.length}`);
2572
2691
  else diffs.push("mappings changed");
2573
- if (!deepEqual$1(local.entities, remote.entities)) diffs.push("entities changed");
2692
+ if (!deepEqual(local.entities, remote.entities)) diffs.push("entities changed");
2574
2693
  return diffs;
2575
2694
  }
2695
+ function destAppLabel(action) {
2696
+ return `dest: ${action.destApp.app ?? action.destApp.code ?? "(unspecified)"}`;
2697
+ }
2576
2698
  const ActionDiffDetector = { detect: (local, remote) => {
2577
- const entries = [];
2578
- for (const [name, localAction] of Object.entries(local.actions)) {
2579
- const remoteAction = remote.actions[name];
2580
- if (!remoteAction) entries.push({
2699
+ return buildDiffResult(detectRecordDiff(local.actions, remote.actions, {
2700
+ onAdded: (name, localAction) => ({
2581
2701
  type: "added",
2582
2702
  actionName: name,
2583
- details: `dest: ${localAction.destApp.app ?? localAction.destApp.code ?? "(unspecified)"}`
2584
- });
2585
- else {
2703
+ details: destAppLabel(localAction)
2704
+ }),
2705
+ onModified: (name, localAction, remoteAction) => {
2586
2706
  const diffs = compareActions$1(localAction, remoteAction);
2587
- if (diffs.length > 0) entries.push({
2707
+ if (diffs.length > 0) return {
2588
2708
  type: "modified",
2589
2709
  actionName: name,
2590
2710
  details: diffs.join(", ")
2591
- });
2592
- }
2593
- }
2594
- for (const [name, remoteAction] of Object.entries(remote.actions)) if (!local.actions[name]) entries.push({
2595
- type: "deleted",
2596
- actionName: name,
2597
- details: `dest: ${remoteAction.destApp.app ?? remoteAction.destApp.code ?? "(unspecified)"}`
2598
- });
2599
- return buildDiffResult(entries);
2711
+ };
2712
+ },
2713
+ onDeleted: (name, remoteAction) => ({
2714
+ type: "deleted",
2715
+ actionName: name,
2716
+ details: destAppLabel(remoteAction)
2717
+ })
2718
+ }));
2600
2719
  } };
2601
2720
 
2602
2721
  //#endregion
@@ -2703,7 +2822,7 @@ const AdminNotesConfigParser = { parse: (rawText) => {
2703
2822
  } catch (error) {
2704
2823
  throw new BusinessRuleError(AdminNotesErrorCode.AnInvalidConfigYaml, `Failed to parse YAML: ${error instanceof Error ? error.message : String(error)}`);
2705
2824
  }
2706
- if (!isRecord$1(parsed)) throw new BusinessRuleError(AdminNotesErrorCode.AnInvalidConfigStructure, "Config must be a YAML object");
2825
+ if (!isRecord(parsed)) throw new BusinessRuleError(AdminNotesErrorCode.AnInvalidConfigStructure, "Config must be a YAML object");
2707
2826
  const obj = parsed;
2708
2827
  if (typeof obj.content !== "string") throw new BusinessRuleError(AdminNotesErrorCode.AnInvalidConfigStructure, "Config must have a \"content\" string property");
2709
2828
  if (typeof obj.includeInTemplateAndDuplicates !== "boolean") throw new BusinessRuleError(AdminNotesErrorCode.AnInvalidConfigStructure, "Config must have an \"includeInTemplateAndDuplicates\" boolean property");
@@ -2997,16 +3116,16 @@ var admin_notes_default = define({
2997
3116
 
2998
3117
  //#endregion
2999
3118
  //#region src/core/domain/appPermission/services/configParser.ts
3000
- const VALID_ENTITY_TYPES$9 = new Set([
3119
+ const VALID_ENTITY_TYPES$8 = new Set([
3001
3120
  "USER",
3002
3121
  "GROUP",
3003
3122
  "ORGANIZATION",
3004
3123
  "CREATOR"
3005
3124
  ]);
3006
3125
  function parseEntity$3(raw, index) {
3007
- if (!isRecord$1(raw)) throw new BusinessRuleError(AppPermissionErrorCode.ApInvalidConfigStructure, `Entity at index ${index} must be an object`);
3126
+ if (!isRecord(raw)) throw new BusinessRuleError(AppPermissionErrorCode.ApInvalidConfigStructure, `Entity at index ${index} must be an object`);
3008
3127
  const obj = raw;
3009
- 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`);
3128
+ if (typeof obj.type !== "string" || !VALID_ENTITY_TYPES$8.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`);
3010
3129
  const type = obj.type;
3011
3130
  if (type === "CREATOR") return {
3012
3131
  type,
@@ -3024,7 +3143,7 @@ function parseBooleanField$1(obj, field, index) {
3024
3143
  return value;
3025
3144
  }
3026
3145
  function parseAppRight(raw, index) {
3027
- if (!isRecord$1(raw)) throw new BusinessRuleError(AppPermissionErrorCode.ApInvalidConfigStructure, `App right at index ${index} must be an object`);
3146
+ if (!isRecord(raw)) throw new BusinessRuleError(AppPermissionErrorCode.ApInvalidConfigStructure, `App right at index ${index} must be an object`);
3028
3147
  const obj = raw;
3029
3148
  return {
3030
3149
  entity: parseEntity$3(obj.entity, index),
@@ -3046,7 +3165,7 @@ const AppPermissionConfigParser = { parse: (rawText) => {
3046
3165
  } catch (error) {
3047
3166
  throw new BusinessRuleError(AppPermissionErrorCode.ApInvalidConfigYaml, `Failed to parse YAML: ${error instanceof Error ? error.message : String(error)}`);
3048
3167
  }
3049
- if (!isRecord$1(parsed)) throw new BusinessRuleError(AppPermissionErrorCode.ApInvalidConfigStructure, "Config must be a YAML object");
3168
+ if (!isRecord(parsed)) throw new BusinessRuleError(AppPermissionErrorCode.ApInvalidConfigStructure, "Config must be a YAML object");
3050
3169
  const obj = parsed;
3051
3170
  if (!Array.isArray(obj.rights)) throw new BusinessRuleError(AppPermissionErrorCode.ApInvalidConfigStructure, "Config must have a \"rights\" array");
3052
3171
  const rights = obj.rights.map((item, i) => parseAppRight(item, i));
@@ -3080,14 +3199,14 @@ async function applyAppPermission({ container }) {
3080
3199
 
3081
3200
  //#endregion
3082
3201
  //#region src/core/adapters/kintone/appPermissionConfigurator.ts
3083
- const VALID_ENTITY_TYPES$8 = new Set([
3202
+ const VALID_ENTITY_TYPES$7 = new Set([
3084
3203
  "USER",
3085
3204
  "GROUP",
3086
3205
  "ORGANIZATION",
3087
3206
  "CREATOR"
3088
3207
  ]);
3089
3208
  function fromKintoneRight$2(raw) {
3090
- if (!VALID_ENTITY_TYPES$8.has(raw.entity.type)) throw new SystemError(SystemErrorCode.ExternalApiError, `Unexpected entity type from kintone API: ${raw.entity.type}`);
3209
+ if (!VALID_ENTITY_TYPES$7.has(raw.entity.type)) throw new SystemError(SystemErrorCode.ExternalApiError, `Unexpected entity type from kintone API: ${raw.entity.type}`);
3091
3210
  const type = raw.entity.type;
3092
3211
  let code;
3093
3212
  if (type === "CREATOR") code = raw.entity.code ?? "";
@@ -3458,28 +3577,21 @@ const ResourceMerger = {
3458
3577
 
3459
3578
  //#endregion
3460
3579
  //#region src/core/domain/customization/services/configParser.ts
3461
- const VALID_SCOPES = new Set([
3462
- "ALL",
3463
- "ADMIN",
3464
- "NONE"
3465
- ]);
3466
- const VALID_RESOURCE_TYPES = new Set(["FILE", "URL"]);
3467
3580
  function parseResource(raw, index) {
3468
- if (!isRecord$1(raw)) throw new BusinessRuleError(CustomizationErrorCode.CzInvalidConfigStructure, `Resource at index ${index} must be an object`);
3469
- const obj = raw;
3470
- const type = obj.type;
3471
- 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`);
3581
+ if (!isRecord(raw)) throw new BusinessRuleError(CustomizationErrorCode.CzInvalidConfigStructure, `Resource at index ${index} must be an object`);
3582
+ const type = raw.type;
3583
+ 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`);
3472
3584
  if (type === "FILE") {
3473
- 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`);
3585
+ 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`);
3474
3586
  return {
3475
3587
  type: "FILE",
3476
- path: obj.path
3588
+ path: raw.path
3477
3589
  };
3478
3590
  }
3479
- 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`);
3591
+ 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`);
3480
3592
  return {
3481
3593
  type: "URL",
3482
- url: obj.url
3594
+ url: raw.url
3483
3595
  };
3484
3596
  }
3485
3597
  function parseResourceList(raw) {
@@ -3487,26 +3599,21 @@ function parseResourceList(raw) {
3487
3599
  return raw.map((item, index) => parseResource(item, index));
3488
3600
  }
3489
3601
  function parsePlatform(raw) {
3490
- if (!isRecord$1(raw)) throw new BusinessRuleError(CustomizationErrorCode.CzInvalidConfigStructure, "Platform configuration must be an object");
3491
- const obj = raw;
3602
+ if (!isRecord(raw)) throw new BusinessRuleError(CustomizationErrorCode.CzInvalidConfigStructure, "Platform configuration must be an object");
3492
3603
  return {
3493
- js: obj.js === void 0 || obj.js === null ? [] : parseResourceList(obj.js),
3494
- css: obj.css === void 0 || obj.css === null ? [] : parseResourceList(obj.css)
3604
+ js: raw.js === void 0 || raw.js === null ? [] : parseResourceList(raw.js),
3605
+ css: raw.css === void 0 || raw.css === null ? [] : parseResourceList(raw.css)
3495
3606
  };
3496
3607
  }
3497
- const ConfigParser = { parse: (rawText) => {
3498
- if (rawText.trim().length === 0) throw new BusinessRuleError(CustomizationErrorCode.CzEmptyConfigText, "Customization config text is empty");
3499
- let parsed;
3500
- try {
3501
- parsed = parse(rawText);
3502
- } catch (error) {
3503
- throw new BusinessRuleError(CustomizationErrorCode.CzInvalidConfigYaml, `Failed to parse YAML: ${error instanceof Error ? error.message : String(error)}`);
3504
- }
3505
- if (!isRecord$1(parsed)) throw new BusinessRuleError(CustomizationErrorCode.CzInvalidConfigStructure, "Config must be a YAML object");
3506
- const obj = parsed;
3608
+ const CustomizationConfigParser = { parse: (rawText) => {
3609
+ const obj = parseYamlConfig(rawText, {
3610
+ emptyConfigText: CustomizationErrorCode.CzEmptyConfigText,
3611
+ invalidConfigYaml: CustomizationErrorCode.CzInvalidConfigYaml,
3612
+ invalidConfigStructure: CustomizationErrorCode.CzInvalidConfigStructure
3613
+ }, "Customization");
3507
3614
  let scope;
3508
3615
  if (obj.scope !== void 0 && obj.scope !== null) {
3509
- 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`);
3616
+ if (typeof obj.scope !== "string" || !isCustomizationScope(obj.scope)) throw new BusinessRuleError(CustomizationErrorCode.CzInvalidScope, `Invalid scope: ${String(obj.scope)}. Must be ALL, ADMIN, or NONE`);
3510
3617
  scope = obj.scope;
3511
3618
  }
3512
3619
  const desktop = obj.desktop === void 0 || obj.desktop === null ? {
@@ -3527,7 +3634,7 @@ const ConfigParser = { parse: (rawText) => {
3527
3634
  //#endregion
3528
3635
  //#region src/core/application/customization/parseConfig.ts
3529
3636
  function parseConfigText(rawText) {
3530
- return wrapBusinessRuleError(() => ConfigParser.parse(rawText));
3637
+ return wrapBusinessRuleError(() => CustomizationConfigParser.parse(rawText));
3531
3638
  }
3532
3639
 
3533
3640
  //#endregion
@@ -3630,14 +3737,10 @@ function serializePlatform(platform) {
3630
3737
  };
3631
3738
  }
3632
3739
  const CustomizationConfigSerializer = { serialize: (config) => {
3633
- return stringify({
3740
+ return serializeToYaml({
3634
3741
  ...config.scope !== void 0 ? { scope: config.scope } : {},
3635
3742
  ...hasPlatformResources(config.desktop) ? { desktop: serializePlatform(config.desktop) } : {},
3636
3743
  ...hasPlatformResources(config.mobile) ? { mobile: serializePlatform(config.mobile) } : {}
3637
- }, {
3638
- lineWidth: 0,
3639
- defaultKeyType: "PLAIN",
3640
- defaultStringType: "PLAIN"
3641
3744
  });
3642
3745
  } };
3643
3746
 
@@ -3899,15 +4002,11 @@ var apply_default$9 = define({
3899
4002
  }
3900
4003
  });
3901
4004
 
3902
- //#endregion
3903
- //#region src/core/domain/customization/valueObject.ts
3904
- const DEFAULT_CUSTOMIZATION_SCOPE = "ALL";
3905
-
3906
4005
  //#endregion
3907
4006
  //#region src/core/domain/customization/services/diffDetector.ts
3908
4007
  function resourceName(resource) {
3909
4008
  if (resource.type === "URL") return resource.url;
3910
- const parts = resource.path.replace(/\\/g, "/").split("/");
4009
+ const parts = resource.path.replace(/\\/g, "/").split("/").filter(Boolean);
3911
4010
  return parts[parts.length - 1];
3912
4011
  }
3913
4012
  function remoteResourceName(resource) {
@@ -4018,7 +4117,7 @@ const VALID_ACCESSIBILITIES$1 = new Set([
4018
4117
  "WRITE",
4019
4118
  "NONE"
4020
4119
  ]);
4021
- const VALID_ENTITY_TYPES$7 = new Set([
4120
+ const VALID_ENTITY_TYPES$6 = new Set([
4022
4121
  "USER",
4023
4122
  "GROUP",
4024
4123
  "ORGANIZATION",
@@ -4026,7 +4125,7 @@ const VALID_ENTITY_TYPES$7 = new Set([
4026
4125
  ]);
4027
4126
  function fromKintoneEntity$3(raw) {
4028
4127
  if (!VALID_ACCESSIBILITIES$1.has(raw.accessibility)) throw new SystemError(SystemErrorCode.ExternalApiError, `Unexpected accessibility value from kintone API: ${raw.accessibility}`);
4029
- if (!VALID_ENTITY_TYPES$7.has(raw.entity.type)) throw new SystemError(SystemErrorCode.ExternalApiError, `Unexpected entity type from kintone API: ${raw.entity.type}`);
4128
+ if (!VALID_ENTITY_TYPES$6.has(raw.entity.type)) throw new SystemError(SystemErrorCode.ExternalApiError, `Unexpected entity type from kintone API: ${raw.entity.type}`);
4030
4129
  const result = {
4031
4130
  accessibility: raw.accessibility,
4032
4131
  entity: {
@@ -4128,16 +4227,16 @@ const VALID_ACCESSIBILITIES = new Set([
4128
4227
  "WRITE",
4129
4228
  "NONE"
4130
4229
  ]);
4131
- const VALID_ENTITY_TYPES$6 = new Set([
4230
+ const VALID_ENTITY_TYPES$5 = new Set([
4132
4231
  "USER",
4133
4232
  "GROUP",
4134
4233
  "ORGANIZATION",
4135
4234
  "FIELD_ENTITY"
4136
4235
  ]);
4137
4236
  function parseEntity$2(raw, index) {
4138
- if (!isRecord$1(raw)) throw new BusinessRuleError(FieldPermissionErrorCode.FpInvalidConfigStructure, `Entity at index ${index} must be an object`);
4237
+ if (!isRecord(raw)) throw new BusinessRuleError(FieldPermissionErrorCode.FpInvalidConfigStructure, `Entity at index ${index} must be an object`);
4139
4238
  const obj = raw;
4140
- 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`);
4239
+ if (typeof obj.type !== "string" || !VALID_ENTITY_TYPES$5.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`);
4141
4240
  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`);
4142
4241
  return {
4143
4242
  type: obj.type,
@@ -4145,7 +4244,7 @@ function parseEntity$2(raw, index) {
4145
4244
  };
4146
4245
  }
4147
4246
  function parseFieldRightEntity(raw, index) {
4148
- if (!isRecord$1(raw)) throw new BusinessRuleError(FieldPermissionErrorCode.FpInvalidConfigStructure, `Field right entity at index ${index} must be an object`);
4247
+ if (!isRecord(raw)) throw new BusinessRuleError(FieldPermissionErrorCode.FpInvalidConfigStructure, `Field right entity at index ${index} must be an object`);
4149
4248
  const obj = raw;
4150
4249
  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`);
4151
4250
  const entity = parseEntity$2(obj.entity, index);
@@ -4160,7 +4259,7 @@ function parseFieldRightEntity(raw, index) {
4160
4259
  return result;
4161
4260
  }
4162
4261
  function parseFieldRight(raw, index) {
4163
- if (!isRecord$1(raw)) throw new BusinessRuleError(FieldPermissionErrorCode.FpInvalidConfigStructure, `Field right at index ${index} must be an object`);
4262
+ if (!isRecord(raw)) throw new BusinessRuleError(FieldPermissionErrorCode.FpInvalidConfigStructure, `Field right at index ${index} must be an object`);
4164
4263
  const obj = raw;
4165
4264
  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`);
4166
4265
  if (!Array.isArray(obj.entities)) throw new BusinessRuleError(FieldPermissionErrorCode.FpInvalidConfigStructure, `Field right at index ${index} must have an "entities" array`);
@@ -4178,7 +4277,7 @@ const FieldPermissionConfigParser = { parse: (rawText) => {
4178
4277
  } catch (error) {
4179
4278
  throw new BusinessRuleError(FieldPermissionErrorCode.FpInvalidConfigYaml, `Failed to parse YAML: ${error instanceof Error ? error.message : String(error)}`);
4180
4279
  }
4181
- if (!isRecord$1(parsed)) throw new BusinessRuleError(FieldPermissionErrorCode.FpInvalidConfigStructure, "Config must be a YAML object");
4280
+ if (!isRecord(parsed)) throw new BusinessRuleError(FieldPermissionErrorCode.FpInvalidConfigStructure, "Config must be a YAML object");
4182
4281
  const obj = parsed;
4183
4282
  if (!Array.isArray(obj.rights)) throw new BusinessRuleError(FieldPermissionErrorCode.FpInvalidConfigStructure, "Config must have a \"rights\" array");
4184
4283
  const rights = obj.rights.map((item, i) => parseFieldRight(item, i));
@@ -4363,7 +4462,7 @@ var capture_default$9 = define({
4363
4462
  //#endregion
4364
4463
  //#region src/core/domain/fieldPermission/services/diffDetector.ts
4365
4464
  function areEntitiesEqual(a, b) {
4366
- return deepEqual$1(a.entities.map((e) => ({
4465
+ return deepEqual(a.entities.map((e) => ({
4367
4466
  accessibility: e.accessibility,
4368
4467
  type: e.entity.type,
4369
4468
  code: e.entity.code,
@@ -4598,18 +4697,22 @@ function createGeneralSettingsCliContainer(config) {
4598
4697
  }
4599
4698
 
4600
4699
  //#endregion
4601
- //#region src/core/adapters/kintone/notificationConfigurator.ts
4602
- const VALID_ENTITY_TYPES$5 = new Set([
4700
+ //#region src/core/domain/notification/valueObject.ts
4701
+ const ENTITY_TYPES = [
4603
4702
  "USER",
4604
4703
  "GROUP",
4605
4704
  "ORGANIZATION",
4606
4705
  "FIELD_ENTITY"
4607
- ]);
4608
- function validateEntityType(type, context) {
4609
- if (!VALID_ENTITY_TYPES$5.has(type)) throw new SystemError(SystemErrorCode.ExternalApiError, `Unexpected entity type from kintone API (${context}): ${type}`);
4706
+ ];
4707
+ const VALID_ENTITY_TYPES$4 = new Set(ENTITY_TYPES);
4708
+ function isNotificationEntityType(value) {
4709
+ return VALID_ENTITY_TYPES$4.has(value);
4610
4710
  }
4711
+
4712
+ //#endregion
4713
+ //#region src/core/adapters/kintone/notificationConfigurator.ts
4611
4714
  function fromKintoneEntity$2(raw) {
4612
- validateEntityType(raw.type, "entity");
4715
+ if (!isNotificationEntityType(raw.type)) throw new SystemError(SystemErrorCode.ExternalApiError, `Unexpected entity type from kintone API (entity): ${raw.type}`);
4613
4716
  return {
4614
4717
  type: raw.type,
4615
4718
  code: raw.code
@@ -4630,7 +4733,7 @@ function fromKintoneGeneralNotification(raw) {
4630
4733
  };
4631
4734
  return result;
4632
4735
  }
4633
- function fromKintonePerRecordTarget(raw) {
4736
+ function fromKintoneTarget(raw) {
4634
4737
  const result = { entity: fromKintoneEntity$2(raw.entity) };
4635
4738
  if (raw.includeSubs !== void 0) return {
4636
4739
  ...result,
@@ -4642,29 +4745,27 @@ function fromKintonePerRecordNotification(raw) {
4642
4745
  return {
4643
4746
  filterCond: raw.filterCond,
4644
4747
  title: raw.title,
4645
- targets: raw.targets.map(fromKintonePerRecordTarget)
4748
+ targets: raw.targets.map(fromKintoneTarget)
4646
4749
  };
4647
4750
  }
4648
- function fromKintoneReminderTarget(raw) {
4649
- const result = { entity: fromKintoneEntity$2(raw.entity) };
4650
- if (raw.includeSubs !== void 0) return {
4651
- ...result,
4652
- includeSubs: raw.includeSubs
4653
- };
4654
- return result;
4655
- }
4656
4751
  function fromKintoneReminderNotification(raw) {
4752
+ const daysLater = Number(raw.timing.daysLater);
4753
+ if (!Number.isFinite(daysLater)) throw new SystemError(SystemErrorCode.ExternalApiError, `Unexpected non-numeric daysLater from kintone API: ${raw.timing.daysLater}`);
4657
4754
  const result = {
4658
4755
  code: raw.timing.code,
4659
- daysLater: Number(raw.timing.daysLater),
4756
+ daysLater,
4660
4757
  filterCond: raw.filterCond,
4661
4758
  title: raw.title,
4662
- targets: raw.targets.map(fromKintoneReminderTarget)
4663
- };
4664
- if ("hoursLater" in raw.timing) return {
4665
- ...result,
4666
- hoursLater: Number(raw.timing.hoursLater)
4759
+ targets: raw.targets.map(fromKintoneTarget)
4667
4760
  };
4761
+ if ("hoursLater" in raw.timing) {
4762
+ const hoursLater = Number(raw.timing.hoursLater);
4763
+ if (!Number.isFinite(hoursLater)) throw new SystemError(SystemErrorCode.ExternalApiError, `Unexpected non-numeric hoursLater from kintone API: ${raw.timing.hoursLater}`);
4764
+ return {
4765
+ ...result,
4766
+ hoursLater
4767
+ };
4768
+ }
4668
4769
  if ("time" in raw.timing) return {
4669
4770
  ...result,
4670
4771
  time: raw.timing.time
@@ -4689,7 +4790,7 @@ function toKintoneGeneralNotification(notification) {
4689
4790
  if (notification.includeSubs !== void 0) result.includeSubs = notification.includeSubs;
4690
4791
  return result;
4691
4792
  }
4692
- function toKintonePerRecordTarget(target) {
4793
+ function toKintoneTarget(target) {
4693
4794
  const result = { entity: toKintoneEntity$2(target.entity) };
4694
4795
  if (target.includeSubs !== void 0) result.includeSubs = target.includeSubs;
4695
4796
  return result;
@@ -4698,14 +4799,9 @@ function toKintonePerRecordNotification(notification) {
4698
4799
  return {
4699
4800
  filterCond: notification.filterCond,
4700
4801
  title: notification.title,
4701
- targets: notification.targets.map(toKintonePerRecordTarget)
4802
+ targets: notification.targets.map(toKintoneTarget)
4702
4803
  };
4703
4804
  }
4704
- function toKintoneReminderTarget(target) {
4705
- const result = { entity: toKintoneEntity$2(target.entity) };
4706
- if (target.includeSubs !== void 0) result.includeSubs = target.includeSubs;
4707
- return result;
4708
- }
4709
4805
  function toKintoneReminderNotification(notification) {
4710
4806
  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"`);
4711
4807
  const timing = {
@@ -4718,7 +4814,7 @@ function toKintoneReminderNotification(notification) {
4718
4814
  timing,
4719
4815
  filterCond: notification.filterCond,
4720
4816
  title: notification.title,
4721
- targets: notification.targets.map(toKintoneReminderTarget)
4817
+ targets: notification.targets.map(toKintoneTarget)
4722
4818
  };
4723
4819
  }
4724
4820
  var KintoneNotificationConfigurator = class {
@@ -4915,7 +5011,7 @@ const VALID_ASSIGNEE_TYPES$1 = new Set([
4915
5011
  "ALL",
4916
5012
  "ANY"
4917
5013
  ]);
4918
- const VALID_ENTITY_TYPES$4 = new Set([
5014
+ const VALID_ENTITY_TYPES$3 = new Set([
4919
5015
  "USER",
4920
5016
  "GROUP",
4921
5017
  "ORGANIZATION",
@@ -4925,7 +5021,7 @@ const VALID_ENTITY_TYPES$4 = new Set([
4925
5021
  ]);
4926
5022
  const VALID_ACTION_TYPES$1 = new Set(["PRIMARY", "SECONDARY"]);
4927
5023
  function fromKintoneEntity$1(raw) {
4928
- if (!VALID_ENTITY_TYPES$4.has(raw.entity.type)) throw new SystemError(SystemErrorCode.ExternalApiError, `Unexpected entity type from kintone API: ${raw.entity.type}`);
5024
+ if (!VALID_ENTITY_TYPES$3.has(raw.entity.type)) throw new SystemError(SystemErrorCode.ExternalApiError, `Unexpected entity type from kintone API: ${raw.entity.type}`);
4929
5025
  const result = { type: raw.entity.type };
4930
5026
  const withCode = raw.entity.code !== void 0 ? {
4931
5027
  ...result,
@@ -5067,14 +5163,14 @@ function createProcessManagementCliContainer(config) {
5067
5163
 
5068
5164
  //#endregion
5069
5165
  //#region src/core/adapters/kintone/recordPermissionConfigurator.ts
5070
- const VALID_ENTITY_TYPES$3 = new Set([
5166
+ const VALID_ENTITY_TYPES$2 = new Set([
5071
5167
  "USER",
5072
5168
  "GROUP",
5073
5169
  "ORGANIZATION",
5074
5170
  "FIELD_ENTITY"
5075
5171
  ]);
5076
5172
  function fromKintoneEntity(raw) {
5077
- if (!VALID_ENTITY_TYPES$3.has(raw.entity.type)) throw new SystemError(SystemErrorCode.ExternalApiError, `Unexpected entity type from kintone API: ${raw.entity.type}`);
5173
+ if (!VALID_ENTITY_TYPES$2.has(raw.entity.type)) throw new SystemError(SystemErrorCode.ExternalApiError, `Unexpected entity type from kintone API: ${raw.entity.type}`);
5078
5174
  return {
5079
5175
  entity: {
5080
5176
  type: raw.entity.type,
@@ -5169,8 +5265,8 @@ function createRecordPermissionCliContainer(config) {
5169
5265
  }
5170
5266
 
5171
5267
  //#endregion
5172
- //#region src/core/adapters/kintone/reportConfigurator.ts
5173
- const VALID_CHART_TYPES$1 = new Set([
5268
+ //#region src/core/domain/report/valueObject.ts
5269
+ const CHART_TYPES = [
5174
5270
  "BAR",
5175
5271
  "COLUMN",
5176
5272
  "PIE",
@@ -5180,13 +5276,21 @@ const VALID_CHART_TYPES$1 = new Set([
5180
5276
  "AREA",
5181
5277
  "SPLINE",
5182
5278
  "SPLINE_AREA"
5183
- ]);
5184
- const VALID_CHART_MODES$1 = new Set([
5279
+ ];
5280
+ const VALID_CHART_TYPES = new Set(CHART_TYPES);
5281
+ function isChartType(value) {
5282
+ return VALID_CHART_TYPES.has(value);
5283
+ }
5284
+ const CHART_MODES = [
5185
5285
  "NORMAL",
5186
5286
  "STACKED",
5187
5287
  "PERCENTAGE"
5188
- ]);
5189
- const VALID_GROUP_PERS = new Set([
5288
+ ];
5289
+ const VALID_CHART_MODES = new Set(CHART_MODES);
5290
+ function isChartMode(value) {
5291
+ return VALID_CHART_MODES.has(value);
5292
+ }
5293
+ const GROUP_PERS = [
5190
5294
  "YEAR",
5191
5295
  "QUARTER",
5192
5296
  "MONTH",
@@ -5194,38 +5298,78 @@ const VALID_GROUP_PERS = new Set([
5194
5298
  "DAY",
5195
5299
  "HOUR",
5196
5300
  "MINUTE"
5197
- ]);
5198
- const VALID_AGGREGATION_TYPES$1 = new Set([
5301
+ ];
5302
+ const VALID_GROUP_PERS = new Set(GROUP_PERS);
5303
+ function isGroupPer(value) {
5304
+ return VALID_GROUP_PERS.has(value);
5305
+ }
5306
+ const AGGREGATION_TYPES = [
5199
5307
  "COUNT",
5200
5308
  "SUM",
5201
5309
  "AVERAGE",
5202
5310
  "MAX",
5203
5311
  "MIN"
5204
- ]);
5205
- const VALID_SORT_BYS = new Set([
5312
+ ];
5313
+ const VALID_AGGREGATION_TYPES = new Set(AGGREGATION_TYPES);
5314
+ function isAggregationType(value) {
5315
+ return VALID_AGGREGATION_TYPES.has(value);
5316
+ }
5317
+ const SORT_BYS = [
5206
5318
  "TOTAL",
5207
5319
  "GROUP1",
5208
5320
  "GROUP2",
5209
5321
  "GROUP3"
5210
- ]);
5211
- const VALID_SORT_ORDERS = new Set(["ASC", "DESC"]);
5212
- const VALID_PERIODIC_REPORT_EVERYS = new Set([
5322
+ ];
5323
+ const VALID_SORT_BYS = new Set(SORT_BYS);
5324
+ function isSortBy(value) {
5325
+ return VALID_SORT_BYS.has(value);
5326
+ }
5327
+ const SORT_ORDERS = ["ASC", "DESC"];
5328
+ const VALID_SORT_ORDERS = new Set(SORT_ORDERS);
5329
+ function isSortOrder(value) {
5330
+ return VALID_SORT_ORDERS.has(value);
5331
+ }
5332
+ const PERIODIC_REPORT_EVERYS = [
5213
5333
  "YEAR",
5214
5334
  "QUARTER",
5215
5335
  "MONTH",
5216
5336
  "WEEK",
5217
5337
  "DAY",
5218
5338
  "HOUR"
5219
- ]);
5220
- const VALID_PERIODIC_REPORT_PATTERNS = new Set([
5339
+ ];
5340
+ const VALID_PERIODIC_REPORT_EVERYS = new Set(PERIODIC_REPORT_EVERYS);
5341
+ function isPeriodicReportEvery(value) {
5342
+ return VALID_PERIODIC_REPORT_EVERYS.has(value);
5343
+ }
5344
+ const PERIODIC_REPORT_PATTERNS = [
5221
5345
  "JAN_APR_JUL_OCT",
5222
5346
  "FEB_MAY_AUG_NOV",
5223
5347
  "MAR_JUN_SEP_DEC"
5224
- ]);
5348
+ ];
5349
+ const VALID_PERIODIC_REPORT_PATTERNS = new Set(PERIODIC_REPORT_PATTERNS);
5350
+ function isPeriodicReportPattern(value) {
5351
+ return VALID_PERIODIC_REPORT_PATTERNS.has(value);
5352
+ }
5353
+ const DAYS_OF_WEEK = [
5354
+ "SUNDAY",
5355
+ "MONDAY",
5356
+ "TUESDAY",
5357
+ "WEDNESDAY",
5358
+ "THURSDAY",
5359
+ "FRIDAY",
5360
+ "SATURDAY"
5361
+ ];
5362
+ const VALID_DAYS_OF_WEEK = new Set(DAYS_OF_WEEK);
5363
+ function isDayOfWeek(value) {
5364
+ return VALID_DAYS_OF_WEEK.has(value);
5365
+ }
5366
+
5367
+ //#endregion
5368
+ //#region src/core/adapters/kintone/reportConfigurator.ts
5225
5369
  function fromKintoneGroup(raw) {
5226
5370
  const result = { code: raw.code };
5227
5371
  if (raw.per !== void 0) {
5228
- if (!VALID_GROUP_PERS.has(raw.per)) throw new SystemError(SystemErrorCode.ExternalApiError, `Unexpected group per value from kintone API: ${raw.per}`);
5372
+ if (!isGroupPer(raw.per)) throw new SystemError(SystemErrorCode.ExternalApiError, `Unexpected group per value from kintone API: ${raw.per}`);
5229
5373
  return {
5230
5374
  ...result,
5231
5375
  per: raw.per
@@ -5234,7 +5378,7 @@ function fromKintoneGroup(raw) {
5234
5378
  return result;
5235
5379
  }
5236
5380
  function fromKintoneAggregation(raw) {
5237
- if (!VALID_AGGREGATION_TYPES$1.has(raw.type)) throw new SystemError(SystemErrorCode.ExternalApiError, `Unexpected aggregation type value from kintone API: ${raw.type}`);
5381
+ if (!isAggregationType(raw.type)) throw new SystemError(SystemErrorCode.ExternalApiError, `Unexpected aggregation type value from kintone API: ${raw.type}`);
5238
5382
  const result = { type: raw.type };
5239
5383
  if (raw.code !== void 0) return {
5240
5384
  ...result,
@@ -5243,30 +5387,37 @@ function fromKintoneAggregation(raw) {
5243
5387
  return result;
5244
5388
  }
5245
5389
  function fromKintoneSort(raw) {
5246
- if (!VALID_SORT_BYS.has(raw.by)) throw new SystemError(SystemErrorCode.ExternalApiError, `Unexpected sort by value from kintone API: ${raw.by}`);
5247
- if (!VALID_SORT_ORDERS.has(raw.order)) throw new SystemError(SystemErrorCode.ExternalApiError, `Unexpected sort order value from kintone API: ${raw.order}`);
5390
+ if (!isSortBy(raw.by)) throw new SystemError(SystemErrorCode.ExternalApiError, `Unexpected sort by value from kintone API: ${raw.by}`);
5391
+ if (!isSortOrder(raw.order)) throw new SystemError(SystemErrorCode.ExternalApiError, `Unexpected sort order value from kintone API: ${raw.order}`);
5248
5392
  return {
5249
5393
  by: raw.by,
5250
5394
  order: raw.order
5251
5395
  };
5252
5396
  }
5253
5397
  function fromKintonePeriodicReportPeriod(raw) {
5254
- if (!VALID_PERIODIC_REPORT_EVERYS.has(raw.every)) throw new SystemError(SystemErrorCode.ExternalApiError, `Unexpected periodicReport every value from kintone API: ${raw.every}`);
5255
- 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}`);
5398
+ if (!isPeriodicReportEvery(raw.every)) throw new SystemError(SystemErrorCode.ExternalApiError, `Unexpected periodicReport every value from kintone API: ${raw.every}`);
5399
+ if (raw.pattern !== void 0 && !isPeriodicReportPattern(raw.pattern)) throw new SystemError(SystemErrorCode.ExternalApiError, `Unexpected periodicReport pattern value from kintone API: ${raw.pattern}`);
5256
5400
  let dayOfMonth;
5257
5401
  if (raw.dayOfMonth !== void 0) if (String(raw.dayOfMonth) === "END_OF_MONTH") dayOfMonth = "END_OF_MONTH";
5258
5402
  else {
5259
5403
  const parsed = Number(raw.dayOfMonth);
5260
- if (Number.isNaN(parsed)) throw new SystemError(SystemErrorCode.ExternalApiError, `Unexpected periodicReport dayOfMonth value from kintone API: ${String(raw.dayOfMonth)}`);
5404
+ if (!Number.isFinite(parsed) || !Number.isInteger(parsed)) throw new SystemError(SystemErrorCode.ExternalApiError, `Unexpected periodicReport dayOfMonth value from kintone API: ${String(raw.dayOfMonth)}`);
5261
5405
  dayOfMonth = parsed;
5262
5406
  }
5263
5407
  return {
5264
5408
  every: raw.every,
5265
- ...raw.month !== void 0 ? { month: Number(raw.month) } : {},
5266
- ...raw.pattern !== void 0 ? { pattern: raw.pattern } : {},
5409
+ ...raw.month !== void 0 ? { month: (() => {
5410
+ const m = Number(raw.month);
5411
+ if (!Number.isFinite(m)) throw new SystemError(SystemErrorCode.ExternalApiError, `Unexpected non-numeric month from kintone API: ${raw.month}`);
5412
+ return m;
5413
+ })() } : {},
5414
+ ...raw.pattern !== void 0 && isPeriodicReportPattern(raw.pattern) ? { pattern: raw.pattern } : {},
5267
5415
  ...dayOfMonth !== void 0 ? { dayOfMonth } : {},
5268
5416
  ...raw.time !== void 0 ? { time: raw.time } : {},
5269
- ...raw.dayOfWeek !== void 0 ? { dayOfWeek: raw.dayOfWeek } : {},
5417
+ ...raw.dayOfWeek !== void 0 ? (() => {
5418
+ if (!isDayOfWeek(raw.dayOfWeek)) throw new SystemError(SystemErrorCode.ExternalApiError, `Unexpected periodicReport dayOfWeek value from kintone API: ${raw.dayOfWeek}`);
5419
+ return { dayOfWeek: raw.dayOfWeek };
5420
+ })() : {},
5270
5421
  ...raw.minute !== void 0 ? { minute: raw.minute } : {}
5271
5422
  };
5272
5423
  }
@@ -5277,12 +5428,16 @@ function fromKintonePeriodicReport(raw) {
5277
5428
  };
5278
5429
  }
5279
5430
  function fromKintoneReportConfig(raw) {
5280
- if (!VALID_CHART_TYPES$1.has(raw.chartType)) throw new SystemError(SystemErrorCode.ExternalApiError, `Unexpected chartType value from kintone API: ${raw.chartType}`);
5281
- 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}`);
5431
+ if (!isChartType(raw.chartType)) throw new SystemError(SystemErrorCode.ExternalApiError, `Unexpected chartType value from kintone API: ${raw.chartType}`);
5432
+ if (raw.chartMode !== void 0 && raw.chartMode !== "" && !isChartMode(raw.chartMode)) throw new SystemError(SystemErrorCode.ExternalApiError, `Unexpected chartMode value from kintone API: ${raw.chartMode}`);
5282
5433
  const result = {
5283
5434
  chartType: raw.chartType,
5284
- ...raw.chartMode !== void 0 && raw.chartMode !== "" && VALID_CHART_MODES$1.has(raw.chartMode) ? { chartMode: raw.chartMode } : {},
5285
- index: Number(raw.index),
5435
+ ...raw.chartMode !== void 0 && raw.chartMode !== "" && isChartMode(raw.chartMode) ? { chartMode: raw.chartMode } : {},
5436
+ index: (() => {
5437
+ const idx = Number(raw.index);
5438
+ if (!Number.isFinite(idx)) throw new SystemError(SystemErrorCode.ExternalApiError, `Unexpected non-numeric index from kintone API: ${raw.index}`);
5439
+ return idx;
5440
+ })(),
5286
5441
  name: raw.name,
5287
5442
  groups: raw.groups.map(fromKintoneGroup),
5288
5443
  aggregations: raw.aggregations.map(fromKintoneAggregation),
@@ -5315,7 +5470,7 @@ function toKintonePeriodicReportPeriod(period) {
5315
5470
  const result = { every: period.every };
5316
5471
  if (period.month !== void 0) result.month = String(period.month);
5317
5472
  if (period.pattern !== void 0) result.pattern = period.pattern;
5318
- if (period.dayOfMonth !== void 0) result.dayOfMonth = period.dayOfMonth;
5473
+ if (period.dayOfMonth !== void 0) result.dayOfMonth = typeof period.dayOfMonth === "number" ? String(period.dayOfMonth) : period.dayOfMonth;
5319
5474
  if (period.time !== void 0) result.time = period.time;
5320
5475
  if (period.dayOfWeek !== void 0) result.dayOfWeek = period.dayOfWeek;
5321
5476
  if (period.minute !== void 0) result.minute = period.minute;
@@ -5412,16 +5567,31 @@ const VIEW_TYPES = [
5412
5567
  "CUSTOM"
5413
5568
  ];
5414
5569
  const VALID_VIEW_TYPES = new Set(VIEW_TYPES);
5570
+ function isViewType(value) {
5571
+ return VALID_VIEW_TYPES.has(value);
5572
+ }
5415
5573
  const DEVICE_TYPES = ["DESKTOP", "ANY"];
5416
5574
  const VALID_DEVICE_TYPES = new Set(DEVICE_TYPES);
5575
+ function isDeviceType(value) {
5576
+ return VALID_DEVICE_TYPES.has(value);
5577
+ }
5417
5578
 
5418
5579
  //#endregion
5419
5580
  //#region src/core/adapters/kintone/viewConfigurator.ts
5420
5581
  function fromKintoneView(name, raw) {
5421
- if (!VALID_VIEW_TYPES.has(raw.type)) throw new SystemError(SystemErrorCode.ExternalApiError, `Unexpected view type from kintone API: ${raw.type}`);
5582
+ if (!isViewType(raw.type)) throw new SystemError(SystemErrorCode.ExternalApiError, `Unexpected view type from kintone API: ${raw.type}`);
5583
+ let device;
5584
+ if (raw.device !== void 0) {
5585
+ if (!isDeviceType(raw.device)) throw new SystemError(SystemErrorCode.ExternalApiError, `Unexpected device type from kintone API: ${raw.device}`);
5586
+ device = raw.device;
5587
+ }
5422
5588
  return {
5423
5589
  type: raw.type,
5424
- index: typeof raw.index === "string" ? Number(raw.index) : raw.index,
5590
+ index: (() => {
5591
+ const idx = typeof raw.index === "string" ? Number(raw.index) : raw.index;
5592
+ if (!Number.isFinite(idx)) throw new SystemError(SystemErrorCode.ExternalApiError, `Unexpected non-numeric index from kintone API: ${raw.index}`);
5593
+ return idx;
5594
+ })(),
5425
5595
  name,
5426
5596
  ...raw.builtinType !== void 0 && { builtinType: raw.builtinType },
5427
5597
  ...raw.fields !== void 0 && { fields: raw.fields },
@@ -5429,10 +5599,7 @@ function fromKintoneView(name, raw) {
5429
5599
  ...raw.title !== void 0 && { title: raw.title },
5430
5600
  ...raw.html !== void 0 && { html: raw.html },
5431
5601
  ...raw.pager !== void 0 && { pager: raw.pager },
5432
- ...raw.device !== void 0 && { device: (() => {
5433
- if (!VALID_DEVICE_TYPES.has(raw.device)) throw new SystemError(SystemErrorCode.ExternalApiError, `Unexpected device type from kintone API: ${raw.device}`);
5434
- return raw.device;
5435
- })() },
5602
+ ...device !== void 0 && { device },
5436
5603
  ...raw.filterCond !== void 0 && { filterCond: raw.filterCond },
5437
5604
  ...raw.sort !== void 0 && { sort: raw.sort }
5438
5605
  };
@@ -5907,7 +6074,7 @@ function serializeGeneralNotification(notification) {
5907
6074
  if (notification.includeSubs !== void 0) result.includeSubs = notification.includeSubs;
5908
6075
  return result;
5909
6076
  }
5910
- function serializePerRecordTarget(target) {
6077
+ function serializeTarget(target) {
5911
6078
  const result = { entity: serializeEntity$1(target.entity) };
5912
6079
  if (target.includeSubs !== void 0) result.includeSubs = target.includeSubs;
5913
6080
  return result;
@@ -5916,21 +6083,16 @@ function serializePerRecordNotification(notification) {
5916
6083
  return {
5917
6084
  filterCond: notification.filterCond,
5918
6085
  title: notification.title,
5919
- targets: notification.targets.map(serializePerRecordTarget)
6086
+ targets: notification.targets.map(serializeTarget)
5920
6087
  };
5921
6088
  }
5922
- function serializeReminderTarget(target) {
5923
- const result = { entity: serializeEntity$1(target.entity) };
5924
- if (target.includeSubs !== void 0) result.includeSubs = target.includeSubs;
5925
- return result;
5926
- }
5927
6089
  function serializeReminderNotification(notification) {
5928
6090
  const result = {
5929
6091
  code: notification.code,
5930
6092
  daysLater: notification.daysLater,
5931
6093
  filterCond: notification.filterCond,
5932
6094
  title: notification.title,
5933
- targets: notification.targets.map(serializeReminderTarget)
6095
+ targets: notification.targets.map(serializeTarget)
5934
6096
  };
5935
6097
  if (notification.hoursLater !== void 0) result.hoursLater = notification.hoursLater;
5936
6098
  if (notification.time !== void 0) result.time = notification.time;
@@ -5947,11 +6109,7 @@ const NotificationConfigSerializer = { serialize: (config) => {
5947
6109
  timezone: config.reminder.timezone,
5948
6110
  notifications: config.reminder.notifications.map(serializeReminderNotification)
5949
6111
  };
5950
- return stringify(serialized, {
5951
- lineWidth: 0,
5952
- defaultKeyType: "PLAIN",
5953
- defaultStringType: "PLAIN"
5954
- });
6112
+ return serializeToYaml(serialized);
5955
6113
  } };
5956
6114
 
5957
6115
  //#endregion
@@ -5986,15 +6144,11 @@ async function saveNotification({ container, input }) {
5986
6144
  //#endregion
5987
6145
  //#region src/core/domain/plugin/services/configSerializer.ts
5988
6146
  const PluginConfigSerializer = { serialize: (config) => {
5989
- return stringify({ plugins: config.plugins.map((plugin) => ({
6147
+ return serializeToYaml({ plugins: config.plugins.map((plugin) => ({
5990
6148
  id: plugin.id,
5991
6149
  name: plugin.name,
5992
6150
  enabled: plugin.enabled
5993
- })) }, {
5994
- lineWidth: 0,
5995
- defaultKeyType: "PLAIN",
5996
- defaultStringType: "PLAIN"
5997
- });
6151
+ })) });
5998
6152
  } };
5999
6153
 
6000
6154
  //#endregion
@@ -6160,11 +6314,7 @@ const ReportConfigSerializer = { serialize: (config) => {
6160
6314
  const reports = {};
6161
6315
  for (const [name, reportConfig] of Object.entries(config.reports)) reports[name] = serializeReportConfig(reportConfig);
6162
6316
  serialized.reports = reports;
6163
- return stringify(serialized, {
6164
- lineWidth: 0,
6165
- defaultKeyType: "PLAIN",
6166
- defaultStringType: "PLAIN"
6167
- });
6317
+ return serializeToYaml(serialized);
6168
6318
  } };
6169
6319
 
6170
6320
  //#endregion
@@ -6186,11 +6336,7 @@ async function saveReport({ container, input }) {
6186
6336
  //#endregion
6187
6337
  //#region src/core/domain/seedData/services/seedSerializer.ts
6188
6338
  const SeedSerializer = { serialize: (seedData) => {
6189
- const records = seedData.records.map((record) => {
6190
- const plain = {};
6191
- for (const [key, value] of Object.entries(record)) plain[key] = value;
6192
- return plain;
6193
- });
6339
+ const records = seedData.records.map((record) => ({ ...record }));
6194
6340
  return stringify(seedData.key !== null ? {
6195
6341
  key: seedData.key,
6196
6342
  records
@@ -6252,11 +6398,7 @@ function serializeViewConfig(config) {
6252
6398
  const ViewConfigSerializer = { serialize: (config) => {
6253
6399
  const serialized = {};
6254
6400
  for (const [name, viewConfig] of Object.entries(config.views)) serialized[name] = serializeViewConfig(viewConfig);
6255
- return stringify({ views: serialized }, {
6256
- lineWidth: 0,
6257
- defaultKeyType: "PLAIN",
6258
- defaultStringType: "PLAIN"
6259
- });
6401
+ return serializeToYaml({ views: serialized });
6260
6402
  } };
6261
6403
 
6262
6404
  //#endregion
@@ -6613,68 +6755,57 @@ var init_default = define({
6613
6755
 
6614
6756
  //#endregion
6615
6757
  //#region src/core/domain/notification/services/configParser.ts
6616
- const VALID_ENTITY_TYPES$2 = new Set([
6617
- "USER",
6618
- "GROUP",
6619
- "ORGANIZATION",
6620
- "FIELD_ENTITY"
6621
- ]);
6622
6758
  function parseEntity$1(raw, context) {
6623
- if (!isRecord$1(raw)) throw new BusinessRuleError(NotificationErrorCode.NtInvalidConfigStructure, `${context}: entity must be an object`);
6624
- const obj = raw;
6625
- 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`);
6626
- if (typeof obj.code !== "string" || obj.code.length === 0) throw new BusinessRuleError(NotificationErrorCode.NtEmptyEntityCode, `${context}: entity must have a non-empty "code" property`);
6759
+ if (!isRecord(raw)) throw new BusinessRuleError(NotificationErrorCode.NtInvalidConfigStructure, `${context}: entity must be an object`);
6760
+ 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`);
6761
+ if (typeof raw.code !== "string" || raw.code.length === 0) throw new BusinessRuleError(NotificationErrorCode.NtEmptyEntityCode, `${context}: entity must have a non-empty "code" property`);
6627
6762
  return {
6628
- type: obj.type,
6629
- code: obj.code
6763
+ type: raw.type,
6764
+ code: raw.code
6630
6765
  };
6631
6766
  }
6632
6767
  function parseGeneralNotification(raw, index) {
6633
- if (!isRecord$1(raw)) throw new BusinessRuleError(NotificationErrorCode.NtInvalidConfigStructure, `General notification at index ${index} must be an object`);
6634
- const obj = raw;
6768
+ if (!isRecord(raw)) throw new BusinessRuleError(NotificationErrorCode.NtInvalidConfigStructure, `General notification at index ${index} must be an object`);
6635
6769
  const result = {
6636
- entity: parseEntity$1(obj.entity, `General notification at index ${index}`),
6637
- recordAdded: Boolean(obj.recordAdded),
6638
- recordEdited: Boolean(obj.recordEdited),
6639
- commentAdded: Boolean(obj.commentAdded),
6640
- statusChanged: Boolean(obj.statusChanged),
6641
- fileImported: Boolean(obj.fileImported)
6642
- };
6643
- if (obj.includeSubs !== void 0 && obj.includeSubs !== null) return {
6770
+ entity: parseEntity$1(raw.entity, `General notification at index ${index}`),
6771
+ recordAdded: Boolean(raw.recordAdded),
6772
+ recordEdited: Boolean(raw.recordEdited),
6773
+ commentAdded: Boolean(raw.commentAdded),
6774
+ statusChanged: Boolean(raw.statusChanged),
6775
+ fileImported: Boolean(raw.fileImported)
6776
+ };
6777
+ if (raw.includeSubs !== void 0 && raw.includeSubs !== null) return {
6644
6778
  ...result,
6645
- includeSubs: Boolean(obj.includeSubs)
6779
+ includeSubs: Boolean(raw.includeSubs)
6646
6780
  };
6647
6781
  return result;
6648
6782
  }
6649
6783
  function parseGeneralConfig(raw) {
6650
- if (!isRecord$1(raw)) throw new BusinessRuleError(NotificationErrorCode.NtInvalidConfigStructure, "\"general\" must be an object");
6651
- const obj = raw;
6652
- if (!Array.isArray(obj.notifications)) throw new BusinessRuleError(NotificationErrorCode.NtInvalidConfigStructure, "\"general\" must have a \"notifications\" array");
6653
- const notifications = obj.notifications.map((item, i) => parseGeneralNotification(item, i));
6784
+ if (!isRecord(raw)) throw new BusinessRuleError(NotificationErrorCode.NtInvalidConfigStructure, "\"general\" must be an object");
6785
+ if (!Array.isArray(raw.notifications)) throw new BusinessRuleError(NotificationErrorCode.NtInvalidConfigStructure, "\"general\" must have a \"notifications\" array");
6786
+ const notifications = raw.notifications.map((item, i) => parseGeneralNotification(item, i));
6654
6787
  return {
6655
- notifyToCommenter: Boolean(obj.notifyToCommenter),
6788
+ notifyToCommenter: Boolean(raw.notifyToCommenter),
6656
6789
  notifications
6657
6790
  };
6658
6791
  }
6659
- function parsePerRecordTarget(raw, index) {
6660
- if (!isRecord$1(raw)) throw new BusinessRuleError(NotificationErrorCode.NtInvalidConfigStructure, `Per-record target at index ${index} must be an object`);
6661
- const obj = raw;
6662
- const result = { entity: parseEntity$1(obj.entity, `Per-record target at index ${index}`) };
6663
- if (obj.includeSubs !== void 0 && obj.includeSubs !== null) return {
6792
+ function parseTarget(raw, index, context) {
6793
+ if (!isRecord(raw)) throw new BusinessRuleError(NotificationErrorCode.NtInvalidConfigStructure, `${context} target at index ${index} must be an object`);
6794
+ const result = { entity: parseEntity$1(raw.entity, `${context} target at index ${index}`) };
6795
+ if (raw.includeSubs !== void 0 && raw.includeSubs !== null) return {
6664
6796
  ...result,
6665
- includeSubs: Boolean(obj.includeSubs)
6797
+ includeSubs: Boolean(raw.includeSubs)
6666
6798
  };
6667
6799
  return result;
6668
6800
  }
6669
6801
  function parsePerRecordNotification(raw, index) {
6670
- if (!isRecord$1(raw)) throw new BusinessRuleError(NotificationErrorCode.NtInvalidConfigStructure, `Per-record notification at index ${index} must be an object`);
6671
- const obj = raw;
6672
- if (!Array.isArray(obj.targets)) throw new BusinessRuleError(NotificationErrorCode.NtInvalidConfigStructure, `Per-record notification at index ${index} must have a "targets" array`);
6673
- const targets = obj.targets.map((item, i) => parsePerRecordTarget(item, i));
6674
- if (typeof obj.filterCond !== "string") throw new BusinessRuleError(NotificationErrorCode.NtMissingRequiredField, `Per-record notification at index ${index} must have a "filterCond" string property`);
6802
+ if (!isRecord(raw)) throw new BusinessRuleError(NotificationErrorCode.NtInvalidConfigStructure, `Per-record notification at index ${index} must be an object`);
6803
+ if (!Array.isArray(raw.targets)) throw new BusinessRuleError(NotificationErrorCode.NtInvalidConfigStructure, `Per-record notification at index ${index} must have a "targets" array`);
6804
+ const targets = raw.targets.map((item, i) => parseTarget(item, i, "Per-record"));
6805
+ if (typeof raw.filterCond !== "string") throw new BusinessRuleError(NotificationErrorCode.NtMissingRequiredField, `Per-record notification at index ${index} must have a "filterCond" string property`);
6675
6806
  return {
6676
- filterCond: obj.filterCond,
6677
- title: typeof obj.title === "string" ? obj.title : "",
6807
+ filterCond: raw.filterCond,
6808
+ title: typeof raw.title === "string" ? raw.title : "",
6678
6809
  targets
6679
6810
  };
6680
6811
  }
@@ -6682,63 +6813,50 @@ function parsePerRecordConfig(raw) {
6682
6813
  if (!Array.isArray(raw)) throw new BusinessRuleError(NotificationErrorCode.NtInvalidConfigStructure, "\"perRecord\" must be an array");
6683
6814
  return raw.map((item, i) => parsePerRecordNotification(item, i));
6684
6815
  }
6685
- function parseReminderTarget(raw, index) {
6686
- if (!isRecord$1(raw)) throw new BusinessRuleError(NotificationErrorCode.NtInvalidConfigStructure, `Reminder target at index ${index} must be an object`);
6687
- const obj = raw;
6688
- const result = { entity: parseEntity$1(obj.entity, `Reminder target at index ${index}`) };
6689
- if (obj.includeSubs !== void 0 && obj.includeSubs !== null) return {
6690
- ...result,
6691
- includeSubs: Boolean(obj.includeSubs)
6692
- };
6693
- return result;
6694
- }
6695
6816
  function parseReminderNotification(raw, index) {
6696
- if (!isRecord$1(raw)) throw new BusinessRuleError(NotificationErrorCode.NtInvalidConfigStructure, `Reminder notification at index ${index} must be an object`);
6697
- const obj = raw;
6698
- 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`);
6699
- if (!Array.isArray(obj.targets)) throw new BusinessRuleError(NotificationErrorCode.NtInvalidConfigStructure, `Reminder notification at index ${index} must have a "targets" array`);
6700
- const targets = obj.targets.map((item, i) => parseReminderTarget(item, i));
6701
- if (typeof obj.daysLater !== "number") throw new BusinessRuleError(NotificationErrorCode.NtMissingRequiredField, `Reminder notification at index ${index} must have a "daysLater" number property`);
6702
- const hasHoursLater = obj.hoursLater !== void 0 && obj.hoursLater !== null;
6703
- const hasTime = obj.time !== void 0 && obj.time !== null;
6817
+ if (!isRecord(raw)) throw new BusinessRuleError(NotificationErrorCode.NtInvalidConfigStructure, `Reminder notification at index ${index} must be an object`);
6818
+ 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`);
6819
+ if (!Array.isArray(raw.targets)) throw new BusinessRuleError(NotificationErrorCode.NtInvalidConfigStructure, `Reminder notification at index ${index} must have a "targets" array`);
6820
+ const targets = raw.targets.map((item, i) => parseTarget(item, i, "Reminder"));
6821
+ if (typeof raw.daysLater !== "number") throw new BusinessRuleError(NotificationErrorCode.NtMissingRequiredField, `Reminder notification at index ${index} must have a "daysLater" property`);
6822
+ 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`);
6823
+ const hasHoursLater = raw.hoursLater !== void 0 && raw.hoursLater !== null;
6824
+ const hasTime = raw.time !== void 0 && raw.time !== null;
6704
6825
  if (hasHoursLater && hasTime) throw new BusinessRuleError(NotificationErrorCode.NtConflictingTimingFields, `Reminder notification at index ${index} must not have both "hoursLater" and "time"`);
6826
+ 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`);
6827
+ if (hasTime && typeof raw.time !== "string") throw new BusinessRuleError(NotificationErrorCode.NtInvalidConfigStructure, `Reminder notification at index ${index} has non-string "time": ${String(raw.time)}`);
6705
6828
  const result = {
6706
- code: obj.code,
6707
- daysLater: obj.daysLater,
6708
- filterCond: typeof obj.filterCond === "string" ? obj.filterCond : "",
6709
- title: typeof obj.title === "string" ? obj.title : "",
6829
+ code: raw.code,
6830
+ daysLater: raw.daysLater,
6831
+ filterCond: typeof raw.filterCond === "string" ? raw.filterCond : "",
6832
+ title: typeof raw.title === "string" ? raw.title : "",
6710
6833
  targets
6711
6834
  };
6712
- if (hasHoursLater) return {
6835
+ if (hasHoursLater && typeof raw.hoursLater === "number") return {
6713
6836
  ...result,
6714
- hoursLater: typeof obj.hoursLater === "number" ? obj.hoursLater : 0
6837
+ hoursLater: raw.hoursLater
6715
6838
  };
6716
6839
  if (hasTime) return {
6717
6840
  ...result,
6718
- time: typeof obj.time === "string" ? obj.time : ""
6841
+ time: typeof raw.time === "string" ? raw.time : ""
6719
6842
  };
6720
6843
  return result;
6721
6844
  }
6722
6845
  function parseReminderConfig(raw) {
6723
- if (!isRecord$1(raw)) throw new BusinessRuleError(NotificationErrorCode.NtInvalidConfigStructure, "\"reminder\" must be an object");
6724
- const obj = raw;
6725
- if (!Array.isArray(obj.notifications)) throw new BusinessRuleError(NotificationErrorCode.NtInvalidConfigStructure, "\"reminder\" must have a \"notifications\" array");
6726
- const notifications = obj.notifications.map((item, i) => parseReminderNotification(item, i));
6846
+ if (!isRecord(raw)) throw new BusinessRuleError(NotificationErrorCode.NtInvalidConfigStructure, "\"reminder\" must be an object");
6847
+ if (!Array.isArray(raw.notifications)) throw new BusinessRuleError(NotificationErrorCode.NtInvalidConfigStructure, "\"reminder\" must have a \"notifications\" array");
6848
+ const notifications = raw.notifications.map((item, i) => parseReminderNotification(item, i));
6727
6849
  return {
6728
- timezone: typeof obj.timezone === "string" ? obj.timezone : "Asia/Tokyo",
6850
+ timezone: typeof raw.timezone === "string" ? raw.timezone : "Asia/Tokyo",
6729
6851
  notifications
6730
6852
  };
6731
6853
  }
6732
6854
  const NotificationConfigParser = { parse: (rawText) => {
6733
- if (rawText.trim().length === 0) throw new BusinessRuleError(NotificationErrorCode.NtEmptyConfigText, "Notification config text is empty");
6734
- let parsed;
6735
- try {
6736
- parsed = parse(rawText);
6737
- } catch (error) {
6738
- throw new BusinessRuleError(NotificationErrorCode.NtInvalidConfigYaml, `Failed to parse YAML: ${error instanceof Error ? error.message : String(error)}`);
6739
- }
6740
- if (!isRecord$1(parsed)) throw new BusinessRuleError(NotificationErrorCode.NtInvalidConfigStructure, "Config must be a YAML object");
6741
- const obj = parsed;
6855
+ const obj = parseYamlConfig(rawText, {
6856
+ emptyConfigText: NotificationErrorCode.NtEmptyConfigText,
6857
+ invalidConfigYaml: NotificationErrorCode.NtInvalidConfigYaml,
6858
+ invalidConfigStructure: NotificationErrorCode.NtInvalidConfigStructure
6859
+ }, "Notification");
6742
6860
  const config = {};
6743
6861
  if (obj.general !== void 0) config.general = parseGeneralConfig(obj.general);
6744
6862
  if (obj.perRecord !== void 0) config.perRecord = parsePerRecordConfig(obj.perRecord);
@@ -6979,7 +7097,7 @@ function perRecordLabel(notif) {
6979
7097
  function describePerRecordChanges(local, remote) {
6980
7098
  const diffs = [];
6981
7099
  if (local.title !== remote.title) diffs.push("title changed");
6982
- if (!deepEqual$1(local.targets, remote.targets)) diffs.push("targets changed");
7100
+ if (!deepEqual(local.targets, remote.targets)) diffs.push("targets changed");
6983
7101
  return diffs.length > 0 ? diffs.join(", ") : "changed";
6984
7102
  }
6985
7103
  function describeReminderChanges(local, remote) {
@@ -6989,7 +7107,7 @@ function describeReminderChanges(local, remote) {
6989
7107
  if ((local.hoursLater ?? 0) !== (remote.hoursLater ?? 0)) diffs.push("hoursLater changed");
6990
7108
  if ((local.time ?? "") !== (remote.time ?? "")) diffs.push("time changed");
6991
7109
  if (local.filterCond !== remote.filterCond) diffs.push("filterCond changed");
6992
- if (!deepEqual$1(local.targets, remote.targets)) diffs.push("targets changed");
7110
+ if (!deepEqual(local.targets, remote.targets)) diffs.push("targets changed");
6993
7111
  return diffs.length > 0 ? diffs.join(", ") : "changed";
6994
7112
  }
6995
7113
  function comparePerRecordSection(local, remote) {
@@ -7014,7 +7132,7 @@ function comparePerRecordSection(local, remote) {
7014
7132
  name: perRecordLabel(remoteNotif),
7015
7133
  details: "removed"
7016
7134
  });
7017
- else if (localNotif && remoteNotif && !deepEqual$1(localNotif, remoteNotif)) {
7135
+ else if (localNotif && remoteNotif && !deepEqual(localNotif, remoteNotif)) {
7018
7136
  const diffs = describePerRecordChanges(localNotif, remoteNotif);
7019
7137
  entries.push({
7020
7138
  type: "modified",
@@ -7045,7 +7163,7 @@ function compareReminderSection(local, remote) {
7045
7163
  name: code,
7046
7164
  details: `"${localNotif.title}"`
7047
7165
  });
7048
- else if (!deepEqual$1(localNotif, remoteNotif)) entries.push({
7166
+ else if (!deepEqual(localNotif, remoteNotif)) entries.push({
7049
7167
  type: "modified",
7050
7168
  section: "reminder",
7051
7169
  name: code,
@@ -7156,27 +7274,29 @@ var notification_default = define({
7156
7274
  //#endregion
7157
7275
  //#region src/core/domain/plugin/services/configParser.ts
7158
7276
  function parsePluginEntry(raw, index) {
7159
- if (!isRecord$1(raw)) throw new BusinessRuleError(PluginErrorCode.PlInvalidConfigStructure, `Plugin at index ${index} must be an object`);
7160
- const obj = raw;
7161
- 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`);
7277
+ if (!isRecord(raw)) throw new BusinessRuleError(PluginErrorCode.PlInvalidConfigStructure, `Plugin at index ${index} must be an object`);
7278
+ 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`);
7279
+ 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`);
7162
7280
  return {
7163
- id: obj.id,
7164
- name: typeof obj.name === "string" ? obj.name : "",
7165
- enabled: typeof obj.enabled === "boolean" ? obj.enabled : true
7281
+ id: raw.id,
7282
+ name: typeof raw.name === "string" ? raw.name : "",
7283
+ enabled: typeof raw.enabled === "boolean" ? raw.enabled : true
7166
7284
  };
7167
7285
  }
7168
7286
  const PluginConfigParser = { parse: (rawText) => {
7169
- if (rawText.trim().length === 0) throw new BusinessRuleError(PluginErrorCode.PlEmptyConfigText, "Plugin config text is empty");
7170
- let parsed;
7171
- try {
7172
- parsed = parse(rawText);
7173
- } catch (error) {
7174
- throw new BusinessRuleError(PluginErrorCode.PlInvalidConfigYaml, `Failed to parse YAML: ${error instanceof Error ? error.message : String(error)}`);
7175
- }
7176
- if (!isRecord$1(parsed)) throw new BusinessRuleError(PluginErrorCode.PlInvalidConfigStructure, "Config must be a YAML object");
7177
- const obj = parsed;
7287
+ const obj = parseYamlConfig(rawText, {
7288
+ emptyConfigText: PluginErrorCode.PlEmptyConfigText,
7289
+ invalidConfigYaml: PluginErrorCode.PlInvalidConfigYaml,
7290
+ invalidConfigStructure: PluginErrorCode.PlInvalidConfigStructure
7291
+ }, "Plugin");
7178
7292
  if (!Array.isArray(obj.plugins)) throw new BusinessRuleError(PluginErrorCode.PlInvalidConfigStructure, "Config must have a \"plugins\" array");
7179
- return { plugins: obj.plugins.map((item, i) => parsePluginEntry(item, i)) };
7293
+ const plugins = obj.plugins.map((item, i) => parsePluginEntry(item, i));
7294
+ const seenIds = /* @__PURE__ */ new Set();
7295
+ for (const plugin of plugins) {
7296
+ if (seenIds.has(plugin.id)) throw new BusinessRuleError(PluginErrorCode.PlDuplicatePluginId, `Duplicate plugin ID: "${plugin.id}"`);
7297
+ seenIds.add(plugin.id);
7298
+ }
7299
+ return { plugins };
7180
7300
  } };
7181
7301
 
7182
7302
  //#endregion
@@ -7399,7 +7519,7 @@ const VALID_ENTITY_TYPES$1 = new Set([
7399
7519
  ]);
7400
7520
  const VALID_ACTION_TYPES = new Set(["PRIMARY", "SECONDARY"]);
7401
7521
  function parseProcessEntity(raw, index) {
7402
- if (!isRecord$1(raw)) throw new BusinessRuleError(ProcessManagementErrorCode.PmInvalidConfigStructure, `Entity at index ${index} must be an object`);
7522
+ if (!isRecord(raw)) throw new BusinessRuleError(ProcessManagementErrorCode.PmInvalidConfigStructure, `Entity at index ${index} must be an object`);
7403
7523
  const obj = raw;
7404
7524
  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`);
7405
7525
  const result = { type: obj.type };
@@ -7414,7 +7534,7 @@ function parseProcessEntity(raw, index) {
7414
7534
  return withCode;
7415
7535
  }
7416
7536
  function parseAssignee(raw, stateName) {
7417
- if (!isRecord$1(raw)) throw new BusinessRuleError(ProcessManagementErrorCode.PmInvalidConfigStructure, `Assignee for state "${stateName}" must be an object`);
7537
+ if (!isRecord(raw)) throw new BusinessRuleError(ProcessManagementErrorCode.PmInvalidConfigStructure, `Assignee for state "${stateName}" must be an object`);
7418
7538
  const obj = raw;
7419
7539
  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`);
7420
7540
  if (!Array.isArray(obj.entities)) throw new BusinessRuleError(ProcessManagementErrorCode.PmInvalidConfigStructure, `Assignee for state "${stateName}" must have an "entities" array`);
@@ -7425,7 +7545,7 @@ function parseAssignee(raw, stateName) {
7425
7545
  };
7426
7546
  }
7427
7547
  function parseState(raw, stateName) {
7428
- if (!isRecord$1(raw)) throw new BusinessRuleError(ProcessManagementErrorCode.PmInvalidConfigStructure, `State "${stateName}" must be an object`);
7548
+ if (!isRecord(raw)) throw new BusinessRuleError(ProcessManagementErrorCode.PmInvalidConfigStructure, `State "${stateName}" must be an object`);
7429
7549
  const obj = raw;
7430
7550
  if (typeof obj.index !== "number") throw new BusinessRuleError(ProcessManagementErrorCode.PmInvalidConfigStructure, `State "${stateName}" must have a numeric "index" property`);
7431
7551
  if (obj.assignee === void 0 || obj.assignee === null) throw new BusinessRuleError(ProcessManagementErrorCode.PmInvalidConfigStructure, `State "${stateName}" must have an "assignee" property`);
@@ -7436,13 +7556,13 @@ function parseState(raw, stateName) {
7436
7556
  };
7437
7557
  }
7438
7558
  function parseExecutableUser(raw, actionIndex) {
7439
- if (!isRecord$1(raw)) throw new BusinessRuleError(ProcessManagementErrorCode.PmInvalidConfigStructure, `Action at index ${actionIndex}: executableUser must be an object`);
7559
+ if (!isRecord(raw)) throw new BusinessRuleError(ProcessManagementErrorCode.PmInvalidConfigStructure, `Action at index ${actionIndex}: executableUser must be an object`);
7440
7560
  const obj = raw;
7441
7561
  if (!Array.isArray(obj.entities)) throw new BusinessRuleError(ProcessManagementErrorCode.PmInvalidConfigStructure, `Action at index ${actionIndex}: executableUser must have an "entities" array`);
7442
7562
  return { entities: obj.entities.map((item, i) => parseProcessEntity(item, i)) };
7443
7563
  }
7444
7564
  function parseAction(raw, index) {
7445
- if (!isRecord$1(raw)) throw new BusinessRuleError(ProcessManagementErrorCode.PmInvalidConfigStructure, `Action at index ${index} must be an object`);
7565
+ if (!isRecord(raw)) throw new BusinessRuleError(ProcessManagementErrorCode.PmInvalidConfigStructure, `Action at index ${index} must be an object`);
7446
7566
  const obj = raw;
7447
7567
  if (typeof obj.name !== "string") throw new BusinessRuleError(ProcessManagementErrorCode.PmInvalidConfigStructure, `Action at index ${index} must have a "name" string property`);
7448
7568
  if (typeof obj.from !== "string") throw new BusinessRuleError(ProcessManagementErrorCode.PmInvalidConfigStructure, `Action at index ${index} must have a "from" string property`);
@@ -7470,11 +7590,11 @@ const ProcessManagementConfigParser = { parse: (rawText) => {
7470
7590
  } catch (error) {
7471
7591
  throw new BusinessRuleError(ProcessManagementErrorCode.PmInvalidConfigYaml, `Failed to parse YAML: ${error instanceof Error ? error.message : String(error)}`);
7472
7592
  }
7473
- if (!isRecord$1(parsed)) throw new BusinessRuleError(ProcessManagementErrorCode.PmInvalidConfigStructure, "Config must be a YAML object");
7593
+ if (!isRecord(parsed)) throw new BusinessRuleError(ProcessManagementErrorCode.PmInvalidConfigStructure, "Config must be a YAML object");
7474
7594
  const obj = parsed;
7475
7595
  const enable = obj.enable !== void 0 && obj.enable !== null ? Boolean(obj.enable) : false;
7476
- if (obj.states !== void 0 && obj.states !== null && !isRecord$1(obj.states)) throw new BusinessRuleError(ProcessManagementErrorCode.PmInvalidConfigStructure, "Config \"states\" must be an object (map of state name to state definition)");
7477
- const rawStates = isRecord$1(obj.states) ? obj.states : {};
7596
+ 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)");
7597
+ const rawStates = isRecord(obj.states) ? obj.states : {};
7478
7598
  const states = {};
7479
7599
  for (const [name, value] of Object.entries(rawStates)) states[name] = parseState(value, name);
7480
7600
  if (!Array.isArray(obj.actions) && obj.actions !== void 0) throw new BusinessRuleError(ProcessManagementErrorCode.PmInvalidConfigStructure, "Config \"actions\" must be an array");
@@ -7775,7 +7895,7 @@ function parseBooleanField(value, fieldName, context) {
7775
7895
  throw new BusinessRuleError(RecordPermissionErrorCode.RpInvalidPermissionValue, `${context} has invalid "${fieldName}" value: ${String(value)}. Must be a boolean`);
7776
7896
  }
7777
7897
  function parseEntity(raw, index) {
7778
- if (!isRecord$1(raw)) throw new BusinessRuleError(RecordPermissionErrorCode.RpInvalidConfigStructure, `Entity at index ${index} must be an object`);
7898
+ if (!isRecord(raw)) throw new BusinessRuleError(RecordPermissionErrorCode.RpInvalidConfigStructure, `Entity at index ${index} must be an object`);
7779
7899
  const obj = raw;
7780
7900
  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`);
7781
7901
  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`);
@@ -7785,7 +7905,7 @@ function parseEntity(raw, index) {
7785
7905
  };
7786
7906
  }
7787
7907
  function parseRecordRightEntity(raw, index) {
7788
- if (!isRecord$1(raw)) throw new BusinessRuleError(RecordPermissionErrorCode.RpInvalidConfigStructure, `Record right entity at index ${index} must be an object`);
7908
+ if (!isRecord(raw)) throw new BusinessRuleError(RecordPermissionErrorCode.RpInvalidConfigStructure, `Record right entity at index ${index} must be an object`);
7789
7909
  const obj = raw;
7790
7910
  const entity = parseEntity(obj.entity, index);
7791
7911
  const context = `Record right entity at index ${index}`;
@@ -7798,7 +7918,7 @@ function parseRecordRightEntity(raw, index) {
7798
7918
  };
7799
7919
  }
7800
7920
  function parseRecordRight(raw, index) {
7801
- if (!isRecord$1(raw)) throw new BusinessRuleError(RecordPermissionErrorCode.RpInvalidConfigStructure, `Record right at index ${index} must be an object`);
7921
+ if (!isRecord(raw)) throw new BusinessRuleError(RecordPermissionErrorCode.RpInvalidConfigStructure, `Record right at index ${index} must be an object`);
7802
7922
  const obj = raw;
7803
7923
  if (!Array.isArray(obj.entities)) throw new BusinessRuleError(RecordPermissionErrorCode.RpInvalidConfigStructure, `Record right at index ${index} must have an "entities" array`);
7804
7924
  const entities = obj.entities.map((item, i) => parseRecordRightEntity(item, i));
@@ -7815,7 +7935,7 @@ const RecordPermissionConfigParser = { parse: (rawText) => {
7815
7935
  } catch (error) {
7816
7936
  throw new BusinessRuleError(RecordPermissionErrorCode.RpInvalidConfigYaml, `Failed to parse YAML: ${error instanceof Error ? error.message : String(error)}`);
7817
7937
  }
7818
- if (!isRecord$1(parsed)) throw new BusinessRuleError(RecordPermissionErrorCode.RpInvalidConfigStructure, "Config must be a YAML object");
7938
+ if (!isRecord(parsed)) throw new BusinessRuleError(RecordPermissionErrorCode.RpInvalidConfigStructure, "Config must be a YAML object");
7819
7939
  const obj = parsed;
7820
7940
  if (!Array.isArray(obj.rights)) throw new BusinessRuleError(RecordPermissionErrorCode.RpInvalidConfigStructure, "Config must have a \"rights\" array");
7821
7941
  return { rights: obj.rights.map((item, i) => parseRecordRight(item, i)) };
@@ -7954,7 +8074,7 @@ var capture_default$5 = define({
7954
8074
  //#endregion
7955
8075
  //#region src/core/domain/recordPermission/services/diffDetector.ts
7956
8076
  function areRightsEqual(a, b) {
7957
- return deepEqual$1(a.entities.map((e) => ({
8077
+ return deepEqual(a.entities.map((e) => ({
7958
8078
  type: e.entity.type,
7959
8079
  code: e.entity.code,
7960
8080
  viewable: e.viewable,
@@ -8064,140 +8184,76 @@ var record_acl_default = define({
8064
8184
 
8065
8185
  //#endregion
8066
8186
  //#region src/core/domain/report/services/configParser.ts
8067
- const VALID_CHART_TYPES = new Set([
8068
- "BAR",
8069
- "COLUMN",
8070
- "PIE",
8071
- "LINE",
8072
- "PIVOT_TABLE",
8073
- "TABLE",
8074
- "AREA",
8075
- "SPLINE",
8076
- "SPLINE_AREA"
8077
- ]);
8078
- const VALID_CHART_MODES = new Set([
8079
- "NORMAL",
8080
- "STACKED",
8081
- "PERCENTAGE"
8082
- ]);
8083
- const VALID_GROUP_PER = new Set([
8084
- "YEAR",
8085
- "QUARTER",
8086
- "MONTH",
8087
- "WEEK",
8088
- "DAY",
8089
- "HOUR",
8090
- "MINUTE"
8091
- ]);
8092
- const VALID_AGGREGATION_TYPES = new Set([
8093
- "COUNT",
8094
- "SUM",
8095
- "AVERAGE",
8096
- "MAX",
8097
- "MIN"
8098
- ]);
8099
- const VALID_SORT_BY = new Set([
8100
- "TOTAL",
8101
- "GROUP1",
8102
- "GROUP2",
8103
- "GROUP3"
8104
- ]);
8105
- const VALID_DAY_OF_WEEK = new Set([
8106
- "SUNDAY",
8107
- "MONDAY",
8108
- "TUESDAY",
8109
- "WEDNESDAY",
8110
- "THURSDAY",
8111
- "FRIDAY",
8112
- "SATURDAY"
8113
- ]);
8114
- const VALID_PERIODIC_PATTERN = new Set([
8115
- "JAN_APR_JUL_OCT",
8116
- "FEB_MAY_AUG_NOV",
8117
- "MAR_JUN_SEP_DEC"
8118
- ]);
8119
- const VALID_SORT_ORDER = new Set(["ASC", "DESC"]);
8120
- const VALID_PERIODIC_EVERY = new Set([
8121
- "YEAR",
8122
- "QUARTER",
8123
- "MONTH",
8124
- "WEEK",
8125
- "DAY",
8126
- "HOUR"
8127
- ]);
8128
8187
  function parseGroup(raw, index) {
8129
- if (!isRecord$1(raw)) throw new BusinessRuleError(ReportErrorCode.RtInvalidConfigStructure, `Group at index ${index} must be an object`);
8130
- const obj = raw;
8131
- 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`);
8132
- const result = { code: obj.code };
8133
- if (obj.per !== void 0 && obj.per !== null) {
8134
- 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`);
8188
+ if (!isRecord(raw)) throw new BusinessRuleError(ReportErrorCode.RtInvalidConfigStructure, `Group at index ${index} must be an object`);
8189
+ 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`);
8190
+ const result = { code: raw.code };
8191
+ if (raw.per !== void 0 && raw.per !== null) {
8192
+ 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`);
8135
8193
  return {
8136
8194
  ...result,
8137
- per: obj.per
8195
+ per: raw.per
8138
8196
  };
8139
8197
  }
8140
8198
  return result;
8141
8199
  }
8142
8200
  function parseAggregation(raw, index) {
8143
- if (!isRecord$1(raw)) throw new BusinessRuleError(ReportErrorCode.RtInvalidConfigStructure, `Aggregation at index ${index} must be an object`);
8144
- const obj = raw;
8145
- 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`);
8146
- const result = { type: obj.type };
8147
- if (obj.code !== void 0 && obj.code !== null) {
8148
- if (typeof obj.code !== "string") throw new BusinessRuleError(ReportErrorCode.RtInvalidConfigStructure, `Aggregation at index ${index} has invalid code: must be a string`);
8201
+ if (!isRecord(raw)) throw new BusinessRuleError(ReportErrorCode.RtInvalidConfigStructure, `Aggregation at index ${index} must be an object`);
8202
+ 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`);
8203
+ const result = { type: raw.type };
8204
+ if (raw.code !== void 0 && raw.code !== null) {
8205
+ if (typeof raw.code !== "string") throw new BusinessRuleError(ReportErrorCode.RtInvalidConfigStructure, `Aggregation at index ${index} has invalid code: must be a string`);
8149
8206
  return {
8150
8207
  ...result,
8151
- code: obj.code
8208
+ code: raw.code
8152
8209
  };
8153
8210
  }
8154
8211
  return result;
8155
8212
  }
8156
8213
  function parseSort(raw, index) {
8157
- if (!isRecord$1(raw)) throw new BusinessRuleError(ReportErrorCode.RtInvalidConfigStructure, `Sort at index ${index} must be an object`);
8158
- const obj = raw;
8159
- 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`);
8160
- 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`);
8214
+ if (!isRecord(raw)) throw new BusinessRuleError(ReportErrorCode.RtInvalidConfigStructure, `Sort at index ${index} must be an object`);
8215
+ 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`);
8216
+ 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`);
8161
8217
  return {
8162
- by: obj.by,
8163
- order: obj.order
8218
+ by: raw.by,
8219
+ order: raw.order
8164
8220
  };
8165
8221
  }
8166
8222
  function parsePeriodicReportPeriod(raw) {
8167
- if (!isRecord$1(raw)) throw new BusinessRuleError(ReportErrorCode.RtInvalidConfigStructure, "periodicReport.period must be an object");
8168
- const obj = raw;
8169
- 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`);
8170
- const every = obj.every;
8223
+ if (!isRecord(raw)) throw new BusinessRuleError(ReportErrorCode.RtInvalidConfigStructure, "periodicReport.period must be an object");
8224
+ 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`);
8225
+ const every = raw.every;
8171
8226
  let month;
8172
- if (obj.month !== void 0 && obj.month !== null) {
8173
- const parsed = Number(obj.month);
8174
- if (Number.isNaN(parsed)) throw new BusinessRuleError(ReportErrorCode.RtInvalidConfigStructure, `periodicReport.period has invalid month: ${String(obj.month)}. Must be a number`);
8227
+ if (raw.month !== void 0 && raw.month !== null) {
8228
+ const parsed = Number(raw.month);
8229
+ 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`);
8175
8230
  month = parsed;
8176
8231
  }
8177
8232
  let pattern;
8178
- if (obj.pattern !== void 0 && obj.pattern !== null) {
8179
- 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`);
8180
- pattern = obj.pattern;
8233
+ if (raw.pattern !== void 0 && raw.pattern !== null) {
8234
+ 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`);
8235
+ pattern = raw.pattern;
8181
8236
  }
8182
8237
  let dayOfMonth;
8183
- if (obj.dayOfMonth !== void 0 && obj.dayOfMonth !== null) if (obj.dayOfMonth === "END_OF_MONTH") dayOfMonth = "END_OF_MONTH";
8238
+ if (raw.dayOfMonth !== void 0 && raw.dayOfMonth !== null) if (raw.dayOfMonth === "END_OF_MONTH") dayOfMonth = "END_OF_MONTH";
8184
8239
  else {
8185
- const parsed = Number(obj.dayOfMonth);
8186
- 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"`);
8240
+ const parsed = Number(raw.dayOfMonth);
8241
+ 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"`);
8242
+ if (parsed < 1 || parsed > 31) throw new BusinessRuleError(ReportErrorCode.RtInvalidConfigStructure, `periodicReport.period has out-of-range dayOfMonth: ${parsed}. Must be 1-31`);
8187
8243
  dayOfMonth = parsed;
8188
8244
  }
8189
8245
  let time;
8190
- if (obj.time !== void 0 && obj.time !== null) time = String(obj.time);
8246
+ if (raw.time !== void 0 && raw.time !== null) time = String(raw.time);
8191
8247
  let dayOfWeek;
8192
- if (obj.dayOfWeek !== void 0 && obj.dayOfWeek !== null) {
8193
- const dayStr = String(obj.dayOfWeek);
8194
- 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`);
8248
+ if (raw.dayOfWeek !== void 0 && raw.dayOfWeek !== null) {
8249
+ const dayStr = String(raw.dayOfWeek);
8250
+ if (!isDayOfWeek(dayStr)) throw new BusinessRuleError(ReportErrorCode.RtInvalidConfigStructure, `periodicReport.period has invalid dayOfWeek: ${dayStr}. Must be SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, or SATURDAY`);
8195
8251
  dayOfWeek = dayStr;
8196
8252
  }
8197
8253
  let minute;
8198
- if (obj.minute !== void 0 && obj.minute !== null) {
8199
- const parsed = Number(obj.minute);
8200
- if (Number.isNaN(parsed)) throw new BusinessRuleError(ReportErrorCode.RtInvalidConfigStructure, `periodicReport.period has invalid minute: ${String(obj.minute)}. Must be a number`);
8254
+ if (raw.minute !== void 0 && raw.minute !== null) {
8255
+ const parsed = Number(raw.minute);
8256
+ 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)`);
8201
8257
  minute = parsed;
8202
8258
  }
8203
8259
  return {
@@ -8211,33 +8267,35 @@ function parsePeriodicReportPeriod(raw) {
8211
8267
  };
8212
8268
  }
8213
8269
  function parsePeriodicReport(raw) {
8214
- if (!isRecord$1(raw)) throw new BusinessRuleError(ReportErrorCode.RtInvalidConfigStructure, "periodicReport must be an object");
8215
- const obj = raw;
8216
- if (typeof obj.active !== "boolean") throw new BusinessRuleError(ReportErrorCode.RtInvalidConfigStructure, "periodicReport.active must be a boolean");
8217
- const period = parsePeriodicReportPeriod(obj.period);
8270
+ if (!isRecord(raw)) throw new BusinessRuleError(ReportErrorCode.RtInvalidConfigStructure, "periodicReport must be an object");
8271
+ const active = raw.active;
8272
+ if (typeof active !== "boolean") throw new BusinessRuleError(ReportErrorCode.RtInvalidConfigStructure, "periodicReport.active must be a boolean");
8218
8273
  return {
8219
- active: obj.active,
8220
- period
8274
+ active,
8275
+ period: parsePeriodicReportPeriod(raw.period)
8221
8276
  };
8222
8277
  }
8223
8278
  function parseReportConfig(raw, reportName) {
8224
- if (!isRecord$1(raw)) throw new BusinessRuleError(ReportErrorCode.RtInvalidConfigStructure, `Report "${reportName}" must be an object`);
8225
- const obj = raw;
8226
- 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`);
8279
+ if (reportName.length === 0) throw new BusinessRuleError(ReportErrorCode.RtEmptyReportName, "Report name (key) must not be empty");
8280
+ if (!isRecord(raw)) throw new BusinessRuleError(ReportErrorCode.RtInvalidConfigStructure, `Report "${reportName}" must be an object`);
8281
+ 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`);
8227
8282
  let chartMode;
8228
- if (obj.chartMode !== void 0 && obj.chartMode !== null) {
8229
- 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`);
8230
- chartMode = obj.chartMode;
8231
- }
8232
- const name = typeof obj.name === "string" && obj.name.length > 0 ? obj.name : reportName;
8233
- if (name.length === 0) throw new BusinessRuleError(ReportErrorCode.RtEmptyReportName, "Report must have a non-empty name");
8234
- const index = obj.index !== void 0 && obj.index !== null ? Number(obj.index) : 0;
8235
- const groups = Array.isArray(obj.groups) ? obj.groups.map((item, i) => parseGroup(item, i)) : [];
8236
- const aggregations = Array.isArray(obj.aggregations) ? obj.aggregations.map((item, i) => parseAggregation(item, i)) : [];
8237
- const filterCond = typeof obj.filterCond === "string" ? obj.filterCond : "";
8238
- const sorts = Array.isArray(obj.sorts) ? obj.sorts.map((item, i) => parseSort(item, i)) : [];
8283
+ if (raw.chartMode !== void 0 && raw.chartMode !== null) {
8284
+ 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`);
8285
+ chartMode = raw.chartMode;
8286
+ }
8287
+ const name = typeof raw.name === "string" && raw.name.length > 0 ? raw.name : reportName;
8288
+ 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`);
8289
+ const index = typeof raw.index === "number" ? raw.index : 0;
8290
+ if (raw.groups !== void 0 && !Array.isArray(raw.groups)) throw new BusinessRuleError(ReportErrorCode.RtInvalidConfigStructure, `Report "${reportName}" has invalid groups: must be an array`);
8291
+ const groups = Array.isArray(raw.groups) ? raw.groups.map((item, i) => parseGroup(item, i)) : [];
8292
+ if (raw.aggregations !== void 0 && !Array.isArray(raw.aggregations)) throw new BusinessRuleError(ReportErrorCode.RtInvalidConfigStructure, `Report "${reportName}" has invalid aggregations: must be an array`);
8293
+ const aggregations = Array.isArray(raw.aggregations) ? raw.aggregations.map((item, i) => parseAggregation(item, i)) : [];
8294
+ const filterCond = typeof raw.filterCond === "string" ? raw.filterCond : "";
8295
+ if (raw.sorts !== void 0 && !Array.isArray(raw.sorts)) throw new BusinessRuleError(ReportErrorCode.RtInvalidConfigStructure, `Report "${reportName}" has invalid sorts: must be an array`);
8296
+ const sorts = Array.isArray(raw.sorts) ? raw.sorts.map((item, i) => parseSort(item, i)) : [];
8239
8297
  const result = {
8240
- chartType: obj.chartType,
8298
+ chartType: raw.chartType,
8241
8299
  ...chartMode !== void 0 ? { chartMode } : {},
8242
8300
  index,
8243
8301
  name,
@@ -8246,23 +8304,19 @@ function parseReportConfig(raw, reportName) {
8246
8304
  filterCond,
8247
8305
  sorts
8248
8306
  };
8249
- if (obj.periodicReport !== void 0 && obj.periodicReport !== null) return {
8307
+ if (raw.periodicReport !== void 0 && raw.periodicReport !== null) return {
8250
8308
  ...result,
8251
- periodicReport: parsePeriodicReport(obj.periodicReport)
8309
+ periodicReport: parsePeriodicReport(raw.periodicReport)
8252
8310
  };
8253
8311
  return result;
8254
8312
  }
8255
8313
  const ReportConfigParser = { parse: (rawText) => {
8256
- if (rawText.trim().length === 0) throw new BusinessRuleError(ReportErrorCode.RtEmptyConfigText, "Report config text is empty");
8257
- let parsed;
8258
- try {
8259
- parsed = parse(rawText);
8260
- } catch (error) {
8261
- throw new BusinessRuleError(ReportErrorCode.RtInvalidConfigYaml, `Failed to parse YAML: ${error instanceof Error ? error.message : String(error)}`);
8262
- }
8263
- if (!isRecord$1(parsed)) throw new BusinessRuleError(ReportErrorCode.RtInvalidConfigStructure, "Config must be a YAML object");
8264
- const obj = parsed;
8265
- if (!isRecord$1(obj.reports)) throw new BusinessRuleError(ReportErrorCode.RtInvalidConfigStructure, "Config must have a \"reports\" object");
8314
+ const obj = parseYamlConfig(rawText, {
8315
+ emptyConfigText: ReportErrorCode.RtEmptyConfigText,
8316
+ invalidConfigYaml: ReportErrorCode.RtInvalidConfigYaml,
8317
+ invalidConfigStructure: ReportErrorCode.RtInvalidConfigStructure
8318
+ }, "Report");
8319
+ if (!isRecord(obj.reports)) throw new BusinessRuleError(ReportErrorCode.RtInvalidConfigStructure, "Config must have a \"reports\" object");
8266
8320
  const rawReports = obj.reports;
8267
8321
  const reports = {};
8268
8322
  for (const [name, value] of Object.entries(rawReports)) reports[name] = parseReportConfig(value, name);
@@ -8408,36 +8462,33 @@ function compareReports(local, remote) {
8408
8462
  if ((local.chartMode ?? "") !== (remote.chartMode ?? "")) diffs.push(`chartMode: ${remote.chartMode ?? "(unset)"} -> ${local.chartMode ?? "(unset)"}`);
8409
8463
  if (local.index !== remote.index) diffs.push(`index: ${remote.index} -> ${local.index}`);
8410
8464
  if (local.filterCond !== remote.filterCond) diffs.push("filterCond changed");
8411
- if (!deepEqual$1(local.groups, remote.groups)) diffs.push("groups changed");
8412
- if (!deepEqual$1(local.aggregations, remote.aggregations)) diffs.push("aggregations changed");
8413
- if (!deepEqual$1(local.sorts, remote.sorts)) diffs.push("sorts changed");
8414
- if (!deepEqual$1(local.periodicReport ?? null, remote.periodicReport ?? null)) diffs.push("periodicReport changed");
8465
+ if (!deepEqual(local.groups, remote.groups)) diffs.push("groups changed");
8466
+ if (!deepEqual(local.aggregations, remote.aggregations)) diffs.push("aggregations changed");
8467
+ if (!deepEqual(local.sorts, remote.sorts)) diffs.push("sorts changed");
8468
+ if (!deepEqual(local.periodicReport ?? null, remote.periodicReport ?? null)) diffs.push("periodicReport changed");
8415
8469
  return diffs;
8416
8470
  }
8417
8471
  const ReportDiffDetector = { detect: (local, remote) => {
8418
- const entries = [];
8419
- for (const [name, localReport] of Object.entries(local.reports)) {
8420
- const remoteReport = remote.reports[name];
8421
- if (!remoteReport) entries.push({
8472
+ return buildDiffResult(detectRecordDiff(local.reports, remote.reports, {
8473
+ onAdded: (name, localReport) => ({
8422
8474
  type: "added",
8423
8475
  reportName: name,
8424
8476
  details: `chartType: ${localReport.chartType}`
8425
- });
8426
- else {
8477
+ }),
8478
+ onModified: (name, localReport, remoteReport) => {
8427
8479
  const diffs = compareReports(localReport, remoteReport);
8428
- if (diffs.length > 0) entries.push({
8480
+ if (diffs.length > 0) return {
8429
8481
  type: "modified",
8430
8482
  reportName: name,
8431
8483
  details: diffs.join(", ")
8432
- });
8433
- }
8434
- }
8435
- for (const [name, remoteReport] of Object.entries(remote.reports)) if (!local.reports[name]) entries.push({
8436
- type: "deleted",
8437
- reportName: name,
8438
- details: `chartType: ${remoteReport.chartType}`
8439
- });
8440
- return buildDiffResult(entries);
8484
+ };
8485
+ },
8486
+ onDeleted: (name, remoteReport) => ({
8487
+ type: "deleted",
8488
+ reportName: name,
8489
+ details: `chartType: ${remoteReport.chartType}`
8490
+ })
8491
+ }));
8441
8492
  } };
8442
8493
 
8443
8494
  //#endregion
@@ -8539,26 +8590,26 @@ function isMapEqual(a, b) {
8539
8590
  }
8540
8591
  function isPropertiesEqual(a, b) {
8541
8592
  if (a.type === "SUBTABLE" && b.type === "SUBTABLE") return isMapEqual(a.properties.fields, b.properties.fields);
8542
- if (a.type === "REFERENCE_TABLE" && b.type === "REFERENCE_TABLE") return deepEqual$1(a.properties.referenceTable, b.properties.referenceTable);
8543
- return deepEqual$1(a.properties, b.properties);
8593
+ if (a.type === "REFERENCE_TABLE" && b.type === "REFERENCE_TABLE") return deepEqual(a.properties.referenceTable, b.properties.referenceTable);
8594
+ return deepEqual(a.properties, b.properties);
8544
8595
  }
8545
8596
  function isFieldEqual(a, b) {
8546
8597
  if (a.type !== b.type) return false;
8547
8598
  if (a.label !== b.label) return false;
8548
8599
  if (a.code !== b.code) return false;
8549
- if (Boolean(a.noLabel) !== Boolean(b.noLabel)) return false;
8600
+ if ((a.noLabel ?? false) !== (b.noLabel ?? false)) return false;
8550
8601
  return isPropertiesEqual(a, b);
8551
8602
  }
8552
8603
  function describeChanges$1(before, after) {
8553
8604
  const changes = [];
8554
8605
  if (before.type !== after.type) changes.push(`type: ${before.type} -> ${after.type}`);
8555
8606
  if (before.label !== after.label) changes.push(`label: ${before.label} -> ${after.label}`);
8556
- if (Boolean(before.noLabel) !== Boolean(after.noLabel)) changes.push(`noLabel: ${before.noLabel ?? false} -> ${after.noLabel ?? false}`);
8607
+ if ((before.noLabel ?? false) !== (after.noLabel ?? false)) changes.push(`noLabel: ${before.noLabel ?? false} -> ${after.noLabel ?? false}`);
8557
8608
  if (!isPropertiesEqual(before, after)) changes.push("properties changed");
8558
8609
  return changes.length > 0 ? changes.join(", ") : "no visible changes";
8559
8610
  }
8560
8611
  function isLayoutEqual(a, b) {
8561
- return deepEqual$1(a, b);
8612
+ return deepEqual(a, b);
8562
8613
  }
8563
8614
  const DiffDetector = {
8564
8615
  detectLayoutChanges: (schemaLayout, currentLayout) => {
@@ -8595,6 +8646,16 @@ const DiffDetector = {
8595
8646
  }
8596
8647
  };
8597
8648
 
8649
+ //#endregion
8650
+ //#region src/core/domain/formSchema/entity.ts
8651
+ const Schema = { create: (fields, layout) => {
8652
+ if (fields.size === 0) throw new BusinessRuleError(FormSchemaErrorCode.FsEmptyFields, "Schema must have at least one field");
8653
+ return {
8654
+ fields,
8655
+ layout
8656
+ };
8657
+ } };
8658
+
8598
8659
  //#endregion
8599
8660
  //#region src/core/domain/formSchema/services/schemaParser.ts
8600
8661
  const VALID_UNIT_POSITIONS = new Set(["BEFORE", "AFTER"]);
@@ -8614,7 +8675,7 @@ const VALID_LINK_PROTOCOLS = new Set([
8614
8675
  "MAIL"
8615
8676
  ]);
8616
8677
  function validateEnumProperty(fieldCode, propName, value, validValues) {
8617
- if (value !== void 0 && !validValues.has(value)) throw new BusinessRuleError(FormSchemaErrorCode.FsInvalidSchemaStructure, `Invalid ${propName} "${String(value)}" for field "${fieldCode}". Expected one of: ${[...validValues].join(", ")}`);
8678
+ if (value !== void 0 && (typeof value !== "string" || !validValues.has(value))) throw new BusinessRuleError(FormSchemaErrorCode.FsInvalidSchemaStructure, `Invalid ${propName} "${String(value)}" for field "${fieldCode}". Expected one of: ${[...validValues].join(", ")}`);
8618
8679
  }
8619
8680
  function validateFieldProperties(code, fieldType, properties) {
8620
8681
  switch (fieldType) {
@@ -8683,17 +8744,38 @@ const LAYOUT_ATTRIBUTES = new Set(["size"]);
8683
8744
  const DECORATION_ATTRIBUTES = new Set(["elementId"]);
8684
8745
  const GROUP_ATTRIBUTES = new Set(["openGroup", "layout"]);
8685
8746
  const SUBTABLE_ATTRIBUTES = new Set(["fields"]);
8747
+ /**
8748
+ * Parses a raw size object into an ElementSize.
8749
+ * Returns undefined when no size properties are present, treating an empty
8750
+ * object the same as absent — layout elements without explicit sizing should
8751
+ * not carry a redundant empty size object.
8752
+ */
8686
8753
  function parseSize(raw) {
8687
- if (!isRecord$1(raw)) return void 0;
8754
+ if (!isRecord(raw)) return void 0;
8688
8755
  const obj = raw;
8756
+ if (obj.width === void 0 && obj.height === void 0 && obj.innerHeight === void 0) return;
8689
8757
  return {
8690
8758
  ...obj.width !== void 0 ? { width: String(obj.width) } : {},
8691
8759
  ...obj.height !== void 0 ? { height: String(obj.height) } : {},
8692
8760
  ...obj.innerHeight !== void 0 ? { innerHeight: String(obj.innerHeight) } : {}
8693
8761
  };
8694
8762
  }
8695
- function normalizePropertyValue(value) {
8763
+ const BOOLEAN_PROPERTIES = new Set([
8764
+ "required",
8765
+ "unique",
8766
+ "digit",
8767
+ "hideExpression",
8768
+ "defaultNowValue",
8769
+ "openGroup"
8770
+ ]);
8771
+ function normalizePropertyValue(key, value) {
8772
+ if (typeof value === "boolean") return value;
8696
8773
  if (typeof value === "number") return String(value);
8774
+ if (BOOLEAN_PROPERTIES.has(key) && typeof value === "string") {
8775
+ if (value === "true") return true;
8776
+ if (value === "false") return false;
8777
+ throw new BusinessRuleError(FormSchemaErrorCode.FsInvalidSchemaStructure, `Invalid boolean string "${value}" for property "${key}". Expected "true" or "false"`);
8778
+ }
8697
8779
  return value;
8698
8780
  }
8699
8781
  function extractProperties(raw) {
@@ -8704,7 +8786,7 @@ function extractProperties(raw) {
8704
8786
  if (DECORATION_ATTRIBUTES.has(key)) continue;
8705
8787
  if (GROUP_ATTRIBUTES.has(key)) continue;
8706
8788
  if (SUBTABLE_ATTRIBUTES.has(key)) continue;
8707
- properties[key] = normalizePropertyValue(value);
8789
+ properties[key] = normalizePropertyValue(key, value);
8708
8790
  }
8709
8791
  return properties;
8710
8792
  }
@@ -8822,10 +8904,10 @@ function parseFieldDefinitionFromFlat(raw) {
8822
8904
  };
8823
8905
  }
8824
8906
  if (fieldType === "REFERENCE_TABLE") {
8825
- if (!isRecord$1(raw.referenceTable)) throw new BusinessRuleError(FormSchemaErrorCode.FsInvalidSchemaStructure, `Field "${code}" of type REFERENCE_TABLE must have a "referenceTable" property`);
8907
+ if (!isRecord(raw.referenceTable)) throw new BusinessRuleError(FormSchemaErrorCode.FsInvalidSchemaStructure, `Field "${code}" of type REFERENCE_TABLE must have a "referenceTable" property`);
8826
8908
  const refTable = raw.referenceTable;
8827
- if (!isRecord$1(refTable.relatedApp)) throw new BusinessRuleError(FormSchemaErrorCode.FsInvalidSchemaStructure, `Field "${code}" of type REFERENCE_TABLE must have "referenceTable.relatedApp"`);
8828
- if (!isRecord$1(refTable.condition)) throw new BusinessRuleError(FormSchemaErrorCode.FsInvalidSchemaStructure, `Field "${code}" of type REFERENCE_TABLE must have "referenceTable.condition"`);
8909
+ if (!isRecord(refTable.relatedApp)) throw new BusinessRuleError(FormSchemaErrorCode.FsInvalidSchemaStructure, `Field "${code}" of type REFERENCE_TABLE must have "referenceTable.relatedApp"`);
8910
+ if (!isRecord(refTable.condition)) throw new BusinessRuleError(FormSchemaErrorCode.FsInvalidSchemaStructure, `Field "${code}" of type REFERENCE_TABLE must have "referenceTable.condition"`);
8829
8911
  if (!Array.isArray(refTable.displayFields)) throw new BusinessRuleError(FormSchemaErrorCode.FsInvalidSchemaStructure, `Field "${code}" of type REFERENCE_TABLE must have "referenceTable.displayFields" array`);
8830
8912
  const condition = refTable.condition;
8831
8913
  const displayFields = refTable.displayFields.map((f) => FieldCode.create(f));
@@ -8900,6 +8982,7 @@ function parseLayoutElement(raw) {
8900
8982
  }
8901
8983
  function parseLayoutRow(raw) {
8902
8984
  if (raw.type !== "ROW") throw new BusinessRuleError(FormSchemaErrorCode.FsInvalidLayoutStructure, `Expected layout row type "ROW", got "${String(raw.type)}"`);
8985
+ if (raw.fields !== void 0 && !Array.isArray(raw.fields)) throw new BusinessRuleError(FormSchemaErrorCode.FsInvalidLayoutStructure, `ROW "fields" must be an array, got ${typeof raw.fields}`);
8903
8986
  return {
8904
8987
  type: "ROW",
8905
8988
  fields: (Array.isArray(raw.fields) ? raw.fields : []).map(parseLayoutElement)
@@ -8939,7 +9022,7 @@ function parseLayoutItem(raw) {
8939
9022
  const label = String(raw.label ?? "");
8940
9023
  const noLabel = typeof raw.noLabel === "boolean" ? raw.noLabel : void 0;
8941
9024
  const openGroup = typeof raw.openGroup === "boolean" ? raw.openGroup : void 0;
8942
- const rawLayout = Array.isArray(raw.layout) ? raw.layout.filter(isRecord$1) : [];
9025
+ const rawLayout = Array.isArray(raw.layout) ? raw.layout.filter(isRecord) : [];
8943
9026
  let groupFields = /* @__PURE__ */ new Map();
8944
9027
  const layout = [];
8945
9028
  for (const r of rawLayout) {
@@ -9024,22 +9107,21 @@ const SchemaParser = { parse: (rawText) => {
9024
9107
  } catch {
9025
9108
  throw new BusinessRuleError(FormSchemaErrorCode.FsInvalidSchemaFormat, "Schema text is not valid YAML/JSON");
9026
9109
  }
9027
- if (!isRecord$1(parsed)) throw new BusinessRuleError(FormSchemaErrorCode.FsInvalidSchemaStructure, "Schema must be an object");
9110
+ if (!isRecord(parsed)) throw new BusinessRuleError(FormSchemaErrorCode.FsInvalidSchemaStructure, "Schema must be an object");
9028
9111
  const obj = parsed;
9029
9112
  if ("fields" in obj && !("layout" in obj)) throw new BusinessRuleError(FormSchemaErrorCode.FsInvalidSchemaStructure, "\"fields\" key detected. Schema format has changed. Please use \"capture\" to generate a new format schema.");
9030
9113
  if (!("layout" in obj) || !Array.isArray(obj.layout)) throw new BusinessRuleError(FormSchemaErrorCode.FsInvalidLayoutStructure, "Schema must have a \"layout\" array");
9031
- const rawLayout = obj.layout.filter(isRecord$1);
9114
+ const rawLayout = obj.layout;
9032
9115
  let fieldMap = /* @__PURE__ */ new Map();
9033
9116
  const layout = [];
9034
- for (const rawItem of rawLayout) {
9117
+ for (let i = 0; i < rawLayout.length; i++) {
9118
+ const rawItem = rawLayout[i];
9119
+ if (!isRecord(rawItem)) throw new BusinessRuleError(FormSchemaErrorCode.FsInvalidLayoutStructure, `Layout item at index ${i} must be an object`);
9035
9120
  const result = parseLayoutItem(rawItem);
9036
9121
  layout.push(result.item);
9037
9122
  fieldMap = mergeFieldMaps(fieldMap, result.fields);
9038
9123
  }
9039
- return {
9040
- fields: fieldMap,
9041
- layout
9042
- };
9124
+ return Schema.create(fieldMap, layout);
9043
9125
  } };
9044
9126
 
9045
9127
  //#endregion
@@ -9863,17 +9945,17 @@ function normalizeValue(value) {
9863
9945
  if (typeof first === "string") return value;
9864
9946
  if (typeof first === "number") return value.map(String);
9865
9947
  if (typeof first === "object" && first !== null && "code" in first && Object.keys(first).length === 1) return value;
9866
- return value.filter(isRecord$1).map((row) => {
9948
+ return value.filter(isRecord).map((row) => {
9867
9949
  const normalized = {};
9868
9950
  for (const [k, v] of Object.entries(row)) if (Array.isArray(v)) normalized[k] = v.map(String);
9869
9951
  else normalized[k] = v === null || v === void 0 ? "" : String(v);
9870
9952
  return normalized;
9871
9953
  });
9872
9954
  }
9873
- return String(value);
9955
+ throw new BusinessRuleError(SeedDataErrorCode.SdInvalidSeedStructure, `Unsupported value type: ${typeof value}`);
9874
9956
  }
9875
9957
  function parseRecord(raw, index) {
9876
- if (!isRecord$1(raw)) throw new BusinessRuleError(SeedDataErrorCode.SdInvalidSeedStructure, `Record at index ${index} must be an object`);
9958
+ if (!isRecord(raw)) throw new BusinessRuleError(SeedDataErrorCode.SdInvalidSeedStructure, `Record at index ${index} must be an object`);
9877
9959
  const record = {};
9878
9960
  for (const [key, value] of Object.entries(raw)) record[key] = normalizeValue(value);
9879
9961
  return record;
@@ -9886,7 +9968,7 @@ const SeedParser = { parse: (rawText) => {
9886
9968
  } catch {
9887
9969
  throw new BusinessRuleError(SeedDataErrorCode.SdInvalidSeedYaml, "Seed text is not valid YAML");
9888
9970
  }
9889
- if (!isRecord$1(parsed)) throw new BusinessRuleError(SeedDataErrorCode.SdInvalidSeedStructure, "Seed data must be an object");
9971
+ if (!isRecord(parsed)) throw new BusinessRuleError(SeedDataErrorCode.SdInvalidSeedStructure, "Seed data must be an object");
9890
9972
  const obj = parsed;
9891
9973
  const key = "key" in obj && typeof obj.key === "string" ? UpsertKey.create(obj.key) : null;
9892
9974
  if (!("records" in obj) || !Array.isArray(obj.records)) throw new BusinessRuleError(SeedDataErrorCode.SdInvalidSeedStructure, "Seed data must have a \"records\" array");
@@ -9895,8 +9977,9 @@ const SeedParser = { parse: (rawText) => {
9895
9977
  for (let i = 0; i < obj.records.length; i++) {
9896
9978
  const record = parseRecord(obj.records[i], i);
9897
9979
  if (key !== null) {
9898
- if (!(key in record)) throw new BusinessRuleError(SeedDataErrorCode.SdMissingKeyField, `Record at index ${i} is missing key field "${key}"`);
9899
- const keyValue = record[key];
9980
+ const keyField = key;
9981
+ if (!(keyField in record)) throw new BusinessRuleError(SeedDataErrorCode.SdMissingKeyField, `Record at index ${i} is missing key field "${key}"`);
9982
+ const keyValue = record[keyField];
9900
9983
  if (typeof keyValue !== "string") throw new BusinessRuleError(SeedDataErrorCode.SdInvalidSeedStructure, `Key field "${key}" value at index ${i} must be a string`);
9901
9984
  if (seenKeys.has(keyValue)) throw new BusinessRuleError(SeedDataErrorCode.SdDuplicateKeyValue, `Duplicate key value "${keyValue}" at index ${i}`);
9902
9985
  seenKeys.add(keyValue);
@@ -9911,30 +9994,6 @@ const SeedParser = { parse: (rawText) => {
9911
9994
 
9912
9995
  //#endregion
9913
9996
  //#region src/core/domain/seedData/services/upsertPlanner.ts
9914
- function deepEqual(a, b) {
9915
- if (typeof a === "string" && typeof b === "string") return a === b;
9916
- if (Array.isArray(a) && Array.isArray(b)) {
9917
- if (a.length !== b.length) return false;
9918
- for (let i = 0; i < a.length; i++) {
9919
- const itemA = a[i];
9920
- const itemB = b[i];
9921
- if (typeof itemA === "string" && typeof itemB === "string") {
9922
- if (itemA !== itemB) return false;
9923
- continue;
9924
- }
9925
- if (isRecord$1(itemA) && isRecord$1(itemB)) {
9926
- const keysA = Object.keys(itemA);
9927
- const keysB = Object.keys(itemB);
9928
- if (keysA.length !== keysB.length) return false;
9929
- for (const key of keysA) if (String(itemA[key] ?? "") !== String(itemB[key] ?? "")) return false;
9930
- continue;
9931
- }
9932
- if (String(itemA) !== String(itemB)) return false;
9933
- }
9934
- return true;
9935
- }
9936
- return String(a) === String(b);
9937
- }
9938
9997
  function recordsEqual(seed, existing, keyField) {
9939
9998
  const seedKeys = Object.keys(seed).filter((k) => k !== keyField);
9940
9999
  for (const key of seedKeys) {
@@ -10210,7 +10269,7 @@ const VALID_ROUNDING_MODES = new Set([
10210
10269
  "DOWN"
10211
10270
  ]);
10212
10271
  function parseIcon(raw) {
10213
- if (!isRecord$1(raw)) throw new BusinessRuleError(GeneralSettingsErrorCode.GsInvalidConfigStructure, "icon must be an object with \"type\" and \"key\" properties");
10272
+ if (!isRecord(raw)) throw new BusinessRuleError(GeneralSettingsErrorCode.GsInvalidConfigStructure, "icon must be an object with \"type\" and \"key\" properties");
10214
10273
  const obj = raw;
10215
10274
  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)}`);
10216
10275
  if (typeof obj.key !== "string" || obj.key.length === 0) throw new BusinessRuleError(GeneralSettingsErrorCode.GsInvalidConfigStructure, "icon must have a non-empty \"key\" property");
@@ -10220,7 +10279,7 @@ function parseIcon(raw) {
10220
10279
  };
10221
10280
  }
10222
10281
  function parseTitleField(raw) {
10223
- if (!isRecord$1(raw)) throw new BusinessRuleError(GeneralSettingsErrorCode.GsInvalidConfigStructure, "titleField must be an object with \"selectionMode\" property");
10282
+ if (!isRecord(raw)) throw new BusinessRuleError(GeneralSettingsErrorCode.GsInvalidConfigStructure, "titleField must be an object with \"selectionMode\" property");
10224
10283
  const obj = raw;
10225
10284
  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)}`);
10226
10285
  const result = { selectionMode: obj.selectionMode };
@@ -10234,7 +10293,7 @@ function parseTitleField(raw) {
10234
10293
  return result;
10235
10294
  }
10236
10295
  function parseNumberPrecision(raw) {
10237
- if (!isRecord$1(raw)) throw new BusinessRuleError(GeneralSettingsErrorCode.GsInvalidConfigStructure, "numberPrecision must be an object");
10296
+ if (!isRecord(raw)) throw new BusinessRuleError(GeneralSettingsErrorCode.GsInvalidConfigStructure, "numberPrecision must be an object");
10238
10297
  const obj = raw;
10239
10298
  if (typeof obj.digits !== "number") throw new BusinessRuleError(GeneralSettingsErrorCode.GsInvalidConfigStructure, "numberPrecision.digits must be a number");
10240
10299
  if (typeof obj.decimalPlaces !== "number") throw new BusinessRuleError(GeneralSettingsErrorCode.GsInvalidConfigStructure, "numberPrecision.decimalPlaces must be a number");
@@ -10253,7 +10312,7 @@ const GeneralSettingsConfigParser = { parse: (rawText) => {
10253
10312
  } catch (error) {
10254
10313
  throw new BusinessRuleError(GeneralSettingsErrorCode.GsInvalidConfigYaml, `Failed to parse YAML: ${error instanceof Error ? error.message : String(error)}`);
10255
10314
  }
10256
- if (!isRecord$1(parsed)) throw new BusinessRuleError(GeneralSettingsErrorCode.GsInvalidConfigStructure, "Config must be a YAML object");
10315
+ if (!isRecord(parsed)) throw new BusinessRuleError(GeneralSettingsErrorCode.GsInvalidConfigStructure, "Config must be a YAML object");
10257
10316
  const obj = parsed;
10258
10317
  let name;
10259
10318
  if (obj.name !== void 0 && obj.name !== null) {
@@ -10473,7 +10532,7 @@ function compareConfigs(local, remote) {
10473
10532
  });
10474
10533
  }
10475
10534
  function compareDeepEqual(field, l, r) {
10476
- if (!deepEqual$1(l, r)) entries.push({
10535
+ if (!deepEqual(l, r)) entries.push({
10477
10536
  type: "modified",
10478
10537
  field,
10479
10538
  details: `${field} changed`
@@ -10538,40 +10597,44 @@ var settings_default = define({
10538
10597
 
10539
10598
  //#endregion
10540
10599
  //#region src/core/domain/view/services/configParser.ts
10600
+ function parseDeviceType(name, raw) {
10601
+ if (raw === void 0 || raw === null) return void 0;
10602
+ const deviceStr = String(raw);
10603
+ if (!isDeviceType(deviceStr)) throw new BusinessRuleError(ViewErrorCode.VwInvalidDeviceType, `View "${name}" has invalid device type: ${deviceStr}. Must be DESKTOP or ANY`);
10604
+ return deviceStr;
10605
+ }
10541
10606
  function parseViewConfig(name, raw) {
10542
- if (!isRecord$1(raw)) throw new BusinessRuleError(ViewErrorCode.VwInvalidConfigStructure, `View "${name}" must be an object`);
10543
- const obj = raw;
10544
- 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`);
10607
+ if (!isRecord(raw)) throw new BusinessRuleError(ViewErrorCode.VwInvalidConfigStructure, `View "${name}" must be an object`);
10608
+ 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`);
10609
+ 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`);
10610
+ if (raw.fields !== void 0 && !Array.isArray(raw.fields)) throw new BusinessRuleError(ViewErrorCode.VwInvalidConfigStructure, `View "${name}" has invalid fields: must be an array`);
10611
+ if (raw.pager !== void 0 && typeof raw.pager !== "boolean") throw new BusinessRuleError(ViewErrorCode.VwInvalidConfigStructure, `View "${name}" has invalid pager: must be a boolean`);
10612
+ const device = parseDeviceType(name, raw.device);
10545
10613
  return {
10546
- type: obj.type,
10547
- index: typeof obj.index === "number" ? obj.index : 0,
10614
+ type: raw.type,
10615
+ index: typeof raw.index === "number" ? raw.index : 0,
10548
10616
  name,
10549
- ...obj.builtinType !== void 0 && { builtinType: String(obj.builtinType) },
10550
- ...Array.isArray(obj.fields) && { fields: obj.fields.map(String) },
10551
- ...obj.date !== void 0 && { date: String(obj.date) },
10552
- ...obj.title !== void 0 && { title: String(obj.title) },
10553
- ...obj.html !== void 0 && { html: String(obj.html) },
10554
- ...obj.pager !== void 0 && { pager: Boolean(obj.pager) },
10555
- ...obj.device !== void 0 && { device: (() => {
10556
- const deviceStr = String(obj.device);
10557
- if (!VALID_DEVICE_TYPES.has(deviceStr)) throw new BusinessRuleError(ViewErrorCode.VwInvalidDeviceType, `View "${name}" has invalid device type: ${deviceStr}. Must be DESKTOP or ANY`);
10558
- return deviceStr;
10559
- })() },
10560
- ...obj.filterCond !== void 0 && { filterCond: String(obj.filterCond) },
10561
- ...obj.sort !== void 0 && { sort: String(obj.sort) }
10617
+ ...raw.builtinType !== void 0 && { builtinType: String(raw.builtinType) },
10618
+ ...Array.isArray(raw.fields) && { fields: raw.fields.map((f, i) => {
10619
+ if (typeof f !== "string") throw new BusinessRuleError(ViewErrorCode.VwInvalidConfigStructure, `View "${name}" has non-string field at index ${i}: ${String(f)}`);
10620
+ return f;
10621
+ }) },
10622
+ ...raw.date !== void 0 && { date: String(raw.date) },
10623
+ ...raw.title !== void 0 && { title: String(raw.title) },
10624
+ ...raw.html !== void 0 && { html: String(raw.html) },
10625
+ ...raw.pager !== void 0 && { pager: raw.pager },
10626
+ ...device !== void 0 && { device },
10627
+ ...raw.filterCond !== void 0 && { filterCond: String(raw.filterCond) },
10628
+ ...raw.sort !== void 0 && { sort: String(raw.sort) }
10562
10629
  };
10563
10630
  }
10564
10631
  const ViewConfigParser = { parse: (rawText) => {
10565
- if (rawText.trim().length === 0) throw new BusinessRuleError(ViewErrorCode.VwEmptyConfigText, "View config text is empty");
10566
- let parsed;
10567
- try {
10568
- parsed = parse(rawText);
10569
- } catch (error) {
10570
- throw new BusinessRuleError(ViewErrorCode.VwInvalidConfigYaml, `Failed to parse YAML: ${error instanceof Error ? error.message : String(error)}`);
10571
- }
10572
- if (!isRecord$1(parsed)) throw new BusinessRuleError(ViewErrorCode.VwInvalidConfigStructure, "Config must be a YAML object");
10573
- const obj = parsed;
10574
- if (!isRecord$1(obj.views)) throw new BusinessRuleError(ViewErrorCode.VwInvalidConfigStructure, "Config must have a \"views\" object");
10632
+ const obj = parseYamlConfig(rawText, {
10633
+ emptyConfigText: ViewErrorCode.VwEmptyConfigText,
10634
+ invalidConfigYaml: ViewErrorCode.VwInvalidConfigYaml,
10635
+ invalidConfigStructure: ViewErrorCode.VwInvalidConfigStructure
10636
+ }, "View");
10637
+ if (!isRecord(obj.views)) throw new BusinessRuleError(ViewErrorCode.VwInvalidConfigStructure, "Config must have a \"views\" object");
10575
10638
  const viewsObj = obj.views;
10576
10639
  const views = {};
10577
10640
  for (const [name, value] of Object.entries(viewsObj)) {
@@ -10722,7 +10785,6 @@ var capture_default = define({
10722
10785
  //#region src/core/domain/view/services/diffDetector.ts
10723
10786
  function describeChanges(local, remote) {
10724
10787
  const changes = [];
10725
- if (local.name !== remote.name) changes.push(`name: "${remote.name}" -> "${local.name}"`);
10726
10788
  if (local.type !== remote.type) changes.push(`type: ${remote.type} -> ${local.type}`);
10727
10789
  if ((local.builtinType ?? "") !== (remote.builtinType ?? "")) changes.push("builtinType changed");
10728
10790
  if (local.index !== remote.index) changes.push(`index: ${remote.index} -> ${local.index}`);
@@ -10733,33 +10795,30 @@ function describeChanges(local, remote) {
10733
10795
  if ((local.html ?? "") !== (remote.html ?? "")) changes.push("html changed");
10734
10796
  if ((local.pager ?? false) !== (remote.pager ?? false)) changes.push(`pager: ${String(remote.pager ?? false)} -> ${String(local.pager ?? false)}`);
10735
10797
  if ((local.device ?? "") !== (remote.device ?? "")) changes.push("device changed");
10736
- if (!deepEqual$1(local.fields ?? [], remote.fields ?? [])) changes.push("fields changed");
10798
+ if (!deepEqual(local.fields ?? [], remote.fields ?? [])) changes.push("fields changed");
10737
10799
  return changes;
10738
10800
  }
10739
10801
  const ViewDiffDetector = { detect: (localViews, remoteViews) => {
10740
- const entries = [];
10741
- for (const [name, localView] of Object.entries(localViews)) {
10742
- const remoteView = remoteViews[name];
10743
- if (remoteView === void 0) entries.push({
10802
+ return buildDiffResult(detectRecordDiff(localViews, remoteViews, {
10803
+ onAdded: (name) => ({
10744
10804
  type: "added",
10745
10805
  viewName: name,
10746
10806
  details: "new view"
10747
- });
10748
- else {
10807
+ }),
10808
+ onModified: (name, localView, remoteView) => {
10749
10809
  const changes = describeChanges(localView, remoteView);
10750
- if (changes.length > 0) entries.push({
10810
+ if (changes.length > 0) return {
10751
10811
  type: "modified",
10752
10812
  viewName: name,
10753
10813
  details: changes.join(", ")
10754
- });
10755
- }
10756
- }
10757
- for (const name of Object.keys(remoteViews)) if (localViews[name] === void 0) entries.push({
10758
- type: "deleted",
10759
- viewName: name,
10760
- details: "removed"
10761
- });
10762
- return buildDiffResult(entries);
10814
+ };
10815
+ },
10816
+ onDeleted: (name) => ({
10817
+ type: "deleted",
10818
+ viewName: name,
10819
+ details: "removed"
10820
+ })
10821
+ }));
10763
10822
  } };
10764
10823
 
10765
10824
  //#endregion