kintone-migrator 0.35.3 → 0.35.5
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 +200 -106
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -795,7 +795,7 @@ function compareActions$1(local, remote) {
|
|
|
795
795
|
const diffs = [];
|
|
796
796
|
if (local.index !== remote.index) diffs.push(`index: ${remote.index} -> ${local.index}`);
|
|
797
797
|
if (!deepEqual(local.destApp, remote.destApp)) diffs.push(`destApp: ${formatValue(remote.destApp)} -> ${formatValue(local.destApp)}`);
|
|
798
|
-
if (local.filterCond !== remote.filterCond) diffs.push(`filterCond:
|
|
798
|
+
if (local.filterCond !== remote.filterCond) diffs.push(`filterCond: ${formatValue(remote.filterCond)} -> ${formatValue(local.filterCond)}`);
|
|
799
799
|
if (!deepEqual(local.mappings, remote.mappings)) diffs.push(`mappings: ${formatValue(remote.mappings)} -> ${formatValue(local.mappings)}`);
|
|
800
800
|
if (!deepEqual(local.entities, remote.entities)) diffs.push(`entities: ${formatValue(remote.entities)} -> ${formatValue(local.entities)}`);
|
|
801
801
|
return diffs;
|
|
@@ -1354,6 +1354,17 @@ const SKIP_REVISION_CHECK = Symbol("skip-revision-check");
|
|
|
1354
1354
|
/** The generation read when a caller does not specify a target. */
|
|
1355
1355
|
const DEFAULT_FORM_READ_TARGET = "preview";
|
|
1356
1356
|
//#endregion
|
|
1357
|
+
//#region src/core/domain/formSchema/services/lookupListDecoder.ts
|
|
1358
|
+
/**
|
|
1359
|
+
* Reads a lookup list (`fieldMappings`, `lookupPickerFields`) from an external
|
|
1360
|
+
* value. kintone represents an unset list as `""`, which reads as the empty
|
|
1361
|
+
* list. Returns `undefined` when the value is not a list.
|
|
1362
|
+
*/
|
|
1363
|
+
function decodeLookupList(value) {
|
|
1364
|
+
if (value === "") return [];
|
|
1365
|
+
return Array.isArray(value) ? value : void 0;
|
|
1366
|
+
}
|
|
1367
|
+
//#endregion
|
|
1357
1368
|
//#region src/lib/charValidation.ts
|
|
1358
1369
|
/** Returns true if the string contains any control characters (0x00–0x1f or 0x7f). */
|
|
1359
1370
|
function hasControlChars(s) {
|
|
@@ -1375,6 +1386,16 @@ function sanitizeForDisplay$1(s) {
|
|
|
1375
1386
|
}
|
|
1376
1387
|
//#endregion
|
|
1377
1388
|
//#region src/core/domain/formSchema/valueObject.ts
|
|
1389
|
+
const LookupRelatedApp = {
|
|
1390
|
+
/**
|
|
1391
|
+
* kintone returns `code: ""` for a related app without an app code, while a
|
|
1392
|
+
* schema leaves `code` out for the same app. Both build a related app
|
|
1393
|
+
* without `code`, so only a non-empty `code` is kept.
|
|
1394
|
+
*/
|
|
1395
|
+
create: (app, code) => code === void 0 || code === "" ? { app } : {
|
|
1396
|
+
app,
|
|
1397
|
+
code
|
|
1398
|
+
} };
|
|
1378
1399
|
function hasInvalidFieldCodeChars(code) {
|
|
1379
1400
|
if (hasControlChars(code)) return true;
|
|
1380
1401
|
for (let i = 0; i < code.length; i++) {
|
|
@@ -1468,6 +1489,42 @@ function normalizeDefaultValue(type, rest) {
|
|
|
1468
1489
|
if (rest.defaultValue !== void 0 && typeof rest.defaultValue === "string") rest.defaultValue = rest.defaultValue === "" ? [] : [rest.defaultValue];
|
|
1469
1490
|
}
|
|
1470
1491
|
}
|
|
1492
|
+
function readKintoneLookupList(value, fieldPath) {
|
|
1493
|
+
const list = decodeLookupList(value);
|
|
1494
|
+
if (list === void 0) throw new SystemError(SystemErrorCode.ExternalApiError, `Expected array at ${fieldPath}, got ${typeof value}`);
|
|
1495
|
+
return list;
|
|
1496
|
+
}
|
|
1497
|
+
function fromKintoneLookup(value, fieldPath) {
|
|
1498
|
+
assertRecord(value, fieldPath);
|
|
1499
|
+
assertRecord(value.relatedApp, `${fieldPath}.relatedApp`);
|
|
1500
|
+
const { app, code } = value.relatedApp;
|
|
1501
|
+
assertString(app, `${fieldPath}.relatedApp.app`);
|
|
1502
|
+
if (code !== void 0) assertString(code, `${fieldPath}.relatedApp.code`);
|
|
1503
|
+
assertString(value.relatedKeyField, `${fieldPath}.relatedKeyField`);
|
|
1504
|
+
const fieldMappings = readKintoneLookupList(value.fieldMappings, `${fieldPath}.fieldMappings`).map((mapping, i) => {
|
|
1505
|
+
const mappingPath = `${fieldPath}.fieldMappings[${i}]`;
|
|
1506
|
+
assertRecord(mapping, mappingPath);
|
|
1507
|
+
assertString(mapping.field, `${mappingPath}.field`);
|
|
1508
|
+
assertString(mapping.relatedField, `${mappingPath}.relatedField`);
|
|
1509
|
+
return {
|
|
1510
|
+
field: mapping.field,
|
|
1511
|
+
relatedField: mapping.relatedField
|
|
1512
|
+
};
|
|
1513
|
+
});
|
|
1514
|
+
const lookupPickerFields = readKintoneLookupList(value.lookupPickerFields, `${fieldPath}.lookupPickerFields`);
|
|
1515
|
+
assertStringArray(lookupPickerFields, `${fieldPath}.lookupPickerFields`);
|
|
1516
|
+
const { filterCond, sort } = value;
|
|
1517
|
+
if (filterCond !== void 0) assertString(filterCond, `${fieldPath}.filterCond`);
|
|
1518
|
+
if (sort !== void 0) assertString(sort, `${fieldPath}.sort`);
|
|
1519
|
+
return {
|
|
1520
|
+
relatedApp: LookupRelatedApp.create(app, code),
|
|
1521
|
+
relatedKeyField: value.relatedKeyField,
|
|
1522
|
+
fieldMappings,
|
|
1523
|
+
lookupPickerFields,
|
|
1524
|
+
...filterCond !== void 0 ? { filterCond } : {},
|
|
1525
|
+
...sort !== void 0 ? { sort } : {}
|
|
1526
|
+
};
|
|
1527
|
+
}
|
|
1471
1528
|
function fromKintoneProperty(prop) {
|
|
1472
1529
|
const { type, code, label, noLabel, ...rest } = prop;
|
|
1473
1530
|
const base = {
|
|
@@ -1518,10 +1575,15 @@ function fromKintoneProperty(prop) {
|
|
|
1518
1575
|
}
|
|
1519
1576
|
if (!KNOWN_FIELD_TYPES.has(type)) throw new SystemError(SystemErrorCode.ExternalApiError, `Unknown field type: ${type}`);
|
|
1520
1577
|
normalizeDefaultValue(type, rest);
|
|
1578
|
+
const { lookup, ...others } = rest;
|
|
1579
|
+
const properties = lookup === void 0 ? others : {
|
|
1580
|
+
...others,
|
|
1581
|
+
lookup: fromKintoneLookup(lookup, `${type} "${code}".lookup`)
|
|
1582
|
+
};
|
|
1521
1583
|
return {
|
|
1522
1584
|
...base,
|
|
1523
1585
|
type,
|
|
1524
|
-
properties
|
|
1586
|
+
properties
|
|
1525
1587
|
};
|
|
1526
1588
|
}
|
|
1527
1589
|
function toKintoneProperty(field) {
|
|
@@ -4217,16 +4279,17 @@ async function applyAdminNotes({ container }) {
|
|
|
4217
4279
|
//#endregion
|
|
4218
4280
|
//#region src/core/domain/adminNotes/services/diffDetector.ts
|
|
4219
4281
|
const CONTENT_TRUNCATE_LENGTH = 30;
|
|
4220
|
-
function
|
|
4221
|
-
|
|
4222
|
-
|
|
4282
|
+
function formatContentPreview(text, maxLength) {
|
|
4283
|
+
const codePoints = Array.from(text);
|
|
4284
|
+
if (codePoints.length <= maxLength) return formatValue(text);
|
|
4285
|
+
return `${formatValue(codePoints.slice(0, maxLength).join(""))}...`;
|
|
4223
4286
|
}
|
|
4224
4287
|
function compareConfigs$2(local, remote) {
|
|
4225
4288
|
const entries = [];
|
|
4226
4289
|
if (local.content !== remote.content) entries.push({
|
|
4227
4290
|
type: "modified",
|
|
4228
4291
|
field: "content",
|
|
4229
|
-
details:
|
|
4292
|
+
details: `${formatContentPreview(remote.content, CONTENT_TRUNCATE_LENGTH)} -> ${formatContentPreview(local.content, CONTENT_TRUNCATE_LENGTH)}`
|
|
4230
4293
|
});
|
|
4231
4294
|
if (local.includeInTemplateAndDuplicates !== remote.includeInTemplateAndDuplicates) entries.push({
|
|
4232
4295
|
type: "modified",
|
|
@@ -5923,6 +5986,9 @@ function validateLookupStructure(field) {
|
|
|
5923
5986
|
const issues = [];
|
|
5924
5987
|
if (lookup.relatedApp.app.trim().length === 0) issues.push(issue("error", field.code, field.type, "INVALID_LOOKUP", `Field "${field.code}" lookup must have a non-empty "relatedApp.app"`));
|
|
5925
5988
|
if (lookup.relatedKeyField.trim().length === 0) issues.push(issue("error", field.code, field.type, "INVALID_LOOKUP", `Field "${field.code}" lookup must have a non-empty "relatedKeyField"`));
|
|
5989
|
+
lookup.fieldMappings.forEach((mapping, i) => {
|
|
5990
|
+
for (const key of ["field", "relatedField"]) if (mapping[key].trim().length === 0) issues.push(issue("error", field.code, field.type, "INVALID_LOOKUP", `Field "${field.code}" lookup must have a non-empty "fieldMappings[${i}].${key}"`));
|
|
5991
|
+
});
|
|
5926
5992
|
return issues;
|
|
5927
5993
|
}
|
|
5928
5994
|
function validateReferenceTableRelatedApp(field) {
|
|
@@ -6205,6 +6271,42 @@ function buildFieldDefinition(base, fieldType, properties) {
|
|
|
6205
6271
|
};
|
|
6206
6272
|
}
|
|
6207
6273
|
}
|
|
6274
|
+
function invalidLookup(code, detail) {
|
|
6275
|
+
return new BusinessRuleError(FormSchemaErrorCode.FsInvalidSchemaStructure, `Field "${code}" lookup ${detail}`);
|
|
6276
|
+
}
|
|
6277
|
+
function optionalString(value) {
|
|
6278
|
+
return value === void 0 || value === null ? void 0 : String(value);
|
|
6279
|
+
}
|
|
6280
|
+
/** Reads a lookup list; an omitted or valueless list is the empty list. */
|
|
6281
|
+
function optionalLookupList(code, key, value) {
|
|
6282
|
+
if (value === void 0 || value === null) return [];
|
|
6283
|
+
const list = decodeLookupList(value);
|
|
6284
|
+
if (list === void 0) throw invalidLookup(code, `must have a "${key}" array`);
|
|
6285
|
+
return list;
|
|
6286
|
+
}
|
|
6287
|
+
function parseLookup(code, raw) {
|
|
6288
|
+
if (!isRecord(raw)) throw invalidLookup(code, "must be an object");
|
|
6289
|
+
if (!isRecord(raw.relatedApp)) throw invalidLookup(code, "must have a \"relatedApp\" object");
|
|
6290
|
+
const rawFieldMappings = optionalLookupList(code, "fieldMappings", raw.fieldMappings);
|
|
6291
|
+
const lookupPickerFields = optionalLookupList(code, "lookupPickerFields", raw.lookupPickerFields).map((f) => String(f));
|
|
6292
|
+
const fieldMappings = rawFieldMappings.map((mapping) => {
|
|
6293
|
+
if (!isRecord(mapping)) throw invalidLookup(code, "must have objects in \"fieldMappings\"");
|
|
6294
|
+
return {
|
|
6295
|
+
field: String(mapping.field ?? ""),
|
|
6296
|
+
relatedField: String(mapping.relatedField ?? "")
|
|
6297
|
+
};
|
|
6298
|
+
});
|
|
6299
|
+
const filterCond = optionalString(raw.filterCond);
|
|
6300
|
+
const sort = optionalString(raw.sort);
|
|
6301
|
+
return {
|
|
6302
|
+
relatedApp: LookupRelatedApp.create(String(raw.relatedApp.app ?? ""), optionalString(raw.relatedApp.code)),
|
|
6303
|
+
relatedKeyField: String(raw.relatedKeyField ?? ""),
|
|
6304
|
+
fieldMappings,
|
|
6305
|
+
lookupPickerFields,
|
|
6306
|
+
...filterCond !== void 0 ? { filterCond } : {},
|
|
6307
|
+
...sort !== void 0 ? { sort } : {}
|
|
6308
|
+
};
|
|
6309
|
+
}
|
|
6208
6310
|
function parseReferenceTableField(raw, code, base) {
|
|
6209
6311
|
if (!isRecord(raw.referenceTable)) throw new BusinessRuleError(FormSchemaErrorCode.FsInvalidSchemaStructure, `Field "${code}" of type REFERENCE_TABLE must have a "referenceTable" property`);
|
|
6210
6312
|
const refTable = raw.referenceTable;
|
|
@@ -6259,7 +6361,11 @@ function parseFieldDefinitionFromFlat(raw) {
|
|
|
6259
6361
|
};
|
|
6260
6362
|
}
|
|
6261
6363
|
if (fieldType === "REFERENCE_TABLE") return parseReferenceTableField(raw, code, base);
|
|
6262
|
-
const
|
|
6364
|
+
const { lookup, ...others } = extractProperties(raw);
|
|
6365
|
+
const properties = lookup === void 0 ? others : {
|
|
6366
|
+
...others,
|
|
6367
|
+
lookup: parseLookup(code, lookup)
|
|
6368
|
+
};
|
|
6263
6369
|
validateFieldProperties(code, fieldType, properties);
|
|
6264
6370
|
return buildFieldDefinition(base, fieldType, properties);
|
|
6265
6371
|
}
|
|
@@ -6599,6 +6705,13 @@ const VALID_ENTITY_TYPES$5 = /* @__PURE__ */ new Set([
|
|
|
6599
6705
|
function isNotificationEntityType(value) {
|
|
6600
6706
|
return VALID_ENTITY_TYPES$5.has(value);
|
|
6601
6707
|
}
|
|
6708
|
+
/**
|
|
6709
|
+
* Normalizes a reminder timezone read at a boundary (kintone API or config
|
|
6710
|
+
* file) to the domain form: `null` and `""` mean unset (`undefined`).
|
|
6711
|
+
*/
|
|
6712
|
+
function normalizeReminderTimezone(value) {
|
|
6713
|
+
return value === null || value === "" ? void 0 : value;
|
|
6714
|
+
}
|
|
6602
6715
|
//#endregion
|
|
6603
6716
|
//#region src/core/domain/notification/services/configParser.ts
|
|
6604
6717
|
const REMINDER_OFFSET_MIN = -1e4;
|
|
@@ -6694,10 +6807,13 @@ function parseReminderConfig(raw) {
|
|
|
6694
6807
|
if (!isRecord(raw)) throw new BusinessRuleError(NotificationErrorCode.NtInvalidConfigStructure, "\"reminder\" must be an object");
|
|
6695
6808
|
if (!Array.isArray(raw.notifications)) throw new BusinessRuleError(NotificationErrorCode.NtInvalidConfigStructure, "\"reminder\" must have a \"notifications\" array");
|
|
6696
6809
|
const notifications = raw.notifications.map((item, i) => parseReminderNotification(item, i));
|
|
6697
|
-
|
|
6698
|
-
|
|
6810
|
+
const rawTimezone = raw.timezone ?? null;
|
|
6811
|
+
if (rawTimezone !== null && typeof rawTimezone !== "string") throw new BusinessRuleError(NotificationErrorCode.NtInvalidConfigStructure, `"reminder" has non-string "timezone": ${String(rawTimezone)}`);
|
|
6812
|
+
const timezone = normalizeReminderTimezone(rawTimezone);
|
|
6813
|
+
return timezone !== void 0 ? {
|
|
6814
|
+
timezone,
|
|
6699
6815
|
notifications
|
|
6700
|
-
};
|
|
6816
|
+
} : { notifications };
|
|
6701
6817
|
}
|
|
6702
6818
|
const NotificationConfigParser = { parse: (parsed) => {
|
|
6703
6819
|
const obj = validateParsedConfig(parsed, NotificationErrorCode.NtInvalidConfigStructure, "Notification");
|
|
@@ -6746,8 +6862,7 @@ async function applyNotification({ container }) {
|
|
|
6746
6862
|
if (config.reminder !== void 0) {
|
|
6747
6863
|
const currentReminder = await container.notificationConfigurator.getReminderNotifications();
|
|
6748
6864
|
await container.notificationConfigurator.updateReminderNotifications({
|
|
6749
|
-
|
|
6750
|
-
notifications: config.reminder.notifications,
|
|
6865
|
+
...config.reminder,
|
|
6751
6866
|
revision: currentReminder.revision
|
|
6752
6867
|
});
|
|
6753
6868
|
}
|
|
@@ -8245,8 +8360,11 @@ var KintoneNotificationConfigurator = class {
|
|
|
8245
8360
|
preview: true
|
|
8246
8361
|
});
|
|
8247
8362
|
const notifications = response.notifications.map(fromKintoneReminderNotification);
|
|
8363
|
+
const rawTimezone = response.timezone ?? null;
|
|
8364
|
+
if (rawTimezone !== null && typeof rawTimezone !== "string") throw new SystemError(SystemErrorCode.ExternalApiError, `Unexpected reminder timezone from kintone API: ${String(rawTimezone)}`);
|
|
8365
|
+
const timezone = normalizeReminderTimezone(rawTimezone);
|
|
8248
8366
|
return {
|
|
8249
|
-
timezone:
|
|
8367
|
+
...timezone !== void 0 ? { timezone } : {},
|
|
8250
8368
|
notifications,
|
|
8251
8369
|
revision: response.revision
|
|
8252
8370
|
};
|
|
@@ -8258,9 +8376,9 @@ var KintoneNotificationConfigurator = class {
|
|
|
8258
8376
|
try {
|
|
8259
8377
|
const requestParams = {
|
|
8260
8378
|
app: this.appId,
|
|
8261
|
-
timezone: params.timezone,
|
|
8262
8379
|
notifications: params.notifications.map(toKintoneReminderNotification)
|
|
8263
8380
|
};
|
|
8381
|
+
if (params.timezone !== void 0) requestParams.timezone = params.timezone;
|
|
8264
8382
|
if (params.revision !== void 0) requestParams.revision = params.revision;
|
|
8265
8383
|
return { revision: (await this.client.app.updateReminderNotifications(requestParams)).revision };
|
|
8266
8384
|
} catch (error) {
|
|
@@ -9294,7 +9412,7 @@ function compareConfigs$1(local, remote) {
|
|
|
9294
9412
|
if (lv !== rv) entries.push({
|
|
9295
9413
|
type: "modified",
|
|
9296
9414
|
field,
|
|
9297
|
-
details: suppressValues ? `${field} changed` :
|
|
9415
|
+
details: suppressValues ? `${field} changed` : `${formatValue(rv)} -> ${formatValue(lv)}`
|
|
9298
9416
|
});
|
|
9299
9417
|
}
|
|
9300
9418
|
function compareBoolean(field, l, r, defaultValue) {
|
|
@@ -9475,17 +9593,17 @@ function perRecordLabel(notif) {
|
|
|
9475
9593
|
}
|
|
9476
9594
|
function describePerRecordChanges(local, remote) {
|
|
9477
9595
|
const diffs = [];
|
|
9478
|
-
if (local.title !== remote.title) diffs.push(`title:
|
|
9596
|
+
if (local.title !== remote.title) diffs.push(`title: ${formatValue(remote.title)} -> ${formatValue(local.title)}`);
|
|
9479
9597
|
if (!deepEqual(local.targets, remote.targets)) diffs.push(`targets: ${formatValue(remote.targets)} -> ${formatValue(local.targets)}`);
|
|
9480
9598
|
return diffs.length > 0 ? diffs.join("\n") : "changed";
|
|
9481
9599
|
}
|
|
9482
9600
|
function describeReminderChanges(local, remote) {
|
|
9483
9601
|
const diffs = [];
|
|
9484
|
-
if (local.title !== remote.title) diffs.push(`title:
|
|
9602
|
+
if (local.title !== remote.title) diffs.push(`title: ${formatValue(remote.title)} -> ${formatValue(local.title)}`);
|
|
9485
9603
|
if (local.daysLater !== remote.daysLater) diffs.push(`daysLater: ${remote.daysLater} -> ${local.daysLater}`);
|
|
9486
9604
|
if ((local.hoursLater ?? 0) !== (remote.hoursLater ?? 0)) diffs.push(`hoursLater: ${remote.hoursLater ?? 0} -> ${local.hoursLater ?? 0}`);
|
|
9487
|
-
if ((local.time ?? "") !== (remote.time ?? "")) diffs.push(`time:
|
|
9488
|
-
if (local.filterCond !== remote.filterCond) diffs.push(`filterCond:
|
|
9605
|
+
if ((local.time ?? "") !== (remote.time ?? "")) diffs.push(`time: ${formatValue(remote.time ?? "")} -> ${formatValue(local.time ?? "")}`);
|
|
9606
|
+
if (local.filterCond !== remote.filterCond) diffs.push(`filterCond: ${formatValue(remote.filterCond)} -> ${formatValue(local.filterCond)}`);
|
|
9489
9607
|
if (!deepEqual(local.targets, remote.targets)) diffs.push(`targets: ${formatValue(remote.targets)} -> ${formatValue(local.targets)}`);
|
|
9490
9608
|
return diffs.length > 0 ? diffs.join("\n") : "changed";
|
|
9491
9609
|
}
|
|
@@ -9527,7 +9645,7 @@ function compareReminderSection(local, remote) {
|
|
|
9527
9645
|
type: "added",
|
|
9528
9646
|
section: "reminder",
|
|
9529
9647
|
name: code,
|
|
9530
|
-
details:
|
|
9648
|
+
details: formatValue(localNotif.title)
|
|
9531
9649
|
});
|
|
9532
9650
|
else if (!deepEqual(localNotif, remoteNotif)) entries.push({
|
|
9533
9651
|
type: "modified",
|
|
@@ -9564,11 +9682,11 @@ const NotificationDiffDetector = { detect: (local, remote) => {
|
|
|
9564
9682
|
entries.push(...comparePerRecordSection(localPerRecord, remotePerRecord));
|
|
9565
9683
|
if (local.reminder && remote.reminder) {
|
|
9566
9684
|
entries.push(...compareReminderSection(local.reminder.notifications, remote.reminder.notifications));
|
|
9567
|
-
if (local.reminder.timezone !== remote.reminder.timezone) entries.push({
|
|
9685
|
+
if (local.reminder.timezone !== void 0 && local.reminder.timezone !== remote.reminder.timezone) entries.push({
|
|
9568
9686
|
type: "modified",
|
|
9569
9687
|
section: "reminder",
|
|
9570
9688
|
name: "timezone",
|
|
9571
|
-
details: `${remote.reminder.timezone} -> ${local.reminder.timezone}`
|
|
9689
|
+
details: `${remote.reminder.timezone ?? "(unset)"} -> ${local.reminder.timezone}`
|
|
9572
9690
|
});
|
|
9573
9691
|
} else if (local.reminder && !remote.reminder) entries.push({
|
|
9574
9692
|
type: "added",
|
|
@@ -9585,28 +9703,46 @@ const NotificationDiffDetector = { detect: (local, remote) => {
|
|
|
9585
9703
|
return buildDiffResult(entries);
|
|
9586
9704
|
} };
|
|
9587
9705
|
//#endregion
|
|
9588
|
-
//#region src/core/application/notification/
|
|
9589
|
-
|
|
9590
|
-
|
|
9591
|
-
|
|
9706
|
+
//#region src/core/application/notification/loadNotificationRemote.ts
|
|
9707
|
+
/**
|
|
9708
|
+
* Fetches the three remote sub-configs (general / perRecord / reminder) and
|
|
9709
|
+
* bundles them into a single {@link NotificationRemote}.
|
|
9710
|
+
*
|
|
9711
|
+
* The three getters each carry the app (preview) revision; since the revision is
|
|
9712
|
+
* app-scoped and the three are fetched at (essentially) the same moment, the
|
|
9713
|
+
* general getter's revision is used as the observed remote revision for the
|
|
9714
|
+
* TOCTOU guard. kintone always returns all three sections, so the bundled config
|
|
9715
|
+
* always has `general` / `perRecord` / `reminder` defined (an unset reminder
|
|
9716
|
+
* timezone stays absent).
|
|
9717
|
+
*/
|
|
9718
|
+
async function loadNotificationRemote(container) {
|
|
9719
|
+
const [general, perRecord, reminder] = await Promise.all([
|
|
9592
9720
|
container.notificationConfigurator.getGeneralNotifications(),
|
|
9593
9721
|
container.notificationConfigurator.getPerRecordNotifications(),
|
|
9594
9722
|
container.notificationConfigurator.getReminderNotifications()
|
|
9595
9723
|
]);
|
|
9596
|
-
|
|
9597
|
-
|
|
9598
|
-
|
|
9599
|
-
|
|
9600
|
-
|
|
9601
|
-
|
|
9724
|
+
return {
|
|
9725
|
+
config: {
|
|
9726
|
+
general: {
|
|
9727
|
+
notifyToCommenter: general.notifyToCommenter,
|
|
9728
|
+
notifications: general.notifications
|
|
9729
|
+
},
|
|
9730
|
+
perRecord: perRecord.notifications,
|
|
9731
|
+
reminder: {
|
|
9732
|
+
...reminder.timezone !== void 0 ? { timezone: reminder.timezone } : {},
|
|
9733
|
+
notifications: reminder.notifications
|
|
9734
|
+
}
|
|
9602
9735
|
},
|
|
9603
|
-
|
|
9604
|
-
reminder: {
|
|
9605
|
-
timezone: reminderResult.timezone,
|
|
9606
|
-
notifications: reminderResult.notifications
|
|
9607
|
-
}
|
|
9736
|
+
revision: general.revision
|
|
9608
9737
|
};
|
|
9609
|
-
|
|
9738
|
+
}
|
|
9739
|
+
//#endregion
|
|
9740
|
+
//#region src/core/application/notification/detectNotificationDiff.ts
|
|
9741
|
+
async function detectNotificationDiff({ container }) {
|
|
9742
|
+
const [storageResult, remote] = await Promise.all([container.notificationStorage.get(), loadNotificationRemote(container)]);
|
|
9743
|
+
if (!storageResult.exists) throw new ValidationError(ValidationErrorCode.InvalidInput, "Notification config file not found");
|
|
9744
|
+
const localConfig = parseNotificationConfigText(container.configCodec, storageResult.content);
|
|
9745
|
+
return NotificationDiffDetector.detect(localConfig, remote.config);
|
|
9610
9746
|
}
|
|
9611
9747
|
//#endregion
|
|
9612
9748
|
//#region src/core/domain/plugin/services/diffDetector.ts
|
|
@@ -9619,11 +9755,11 @@ const PluginDiffDetector = { detect: (local, remote) => {
|
|
|
9619
9755
|
if (!remotePlugin) entries.push({
|
|
9620
9756
|
type: "added",
|
|
9621
9757
|
pluginId: id,
|
|
9622
|
-
details:
|
|
9758
|
+
details: formatValue(localPlugin.name)
|
|
9623
9759
|
});
|
|
9624
9760
|
else {
|
|
9625
9761
|
const diffs = [];
|
|
9626
|
-
if (localPlugin.name !== remotePlugin.name) diffs.push(`name:
|
|
9762
|
+
if (localPlugin.name !== remotePlugin.name) diffs.push(`name: ${formatValue(remotePlugin.name)} -> ${formatValue(localPlugin.name)}`);
|
|
9627
9763
|
if (localPlugin.enabled !== remotePlugin.enabled) diffs.push(`enabled: ${String(remotePlugin.enabled)} -> ${String(localPlugin.enabled)}`);
|
|
9628
9764
|
if (diffs.length > 0) entries.push({
|
|
9629
9765
|
type: "modified",
|
|
@@ -9635,7 +9771,7 @@ const PluginDiffDetector = { detect: (local, remote) => {
|
|
|
9635
9771
|
for (const [id, remotePlugin] of remoteMap) if (!localMap.has(id)) entries.push({
|
|
9636
9772
|
type: "deleted",
|
|
9637
9773
|
pluginId: id,
|
|
9638
|
-
details:
|
|
9774
|
+
details: formatValue(remotePlugin.name)
|
|
9639
9775
|
});
|
|
9640
9776
|
return buildDiffResult(entries);
|
|
9641
9777
|
} };
|
|
@@ -9670,7 +9806,7 @@ function isExecutableUserEqual(a, b) {
|
|
|
9670
9806
|
}
|
|
9671
9807
|
function compareActions(localAction, remoteAction) {
|
|
9672
9808
|
const diffs = [];
|
|
9673
|
-
if (localAction.filterCond !== remoteAction.filterCond) diffs.push(`filterCond:
|
|
9809
|
+
if (localAction.filterCond !== remoteAction.filterCond) diffs.push(`filterCond: ${formatValue(remoteAction.filterCond)} -> ${formatValue(localAction.filterCond)}`);
|
|
9674
9810
|
if (localAction.type !== remoteAction.type) diffs.push(`type: ${remoteAction.type} -> ${localAction.type}`);
|
|
9675
9811
|
if (!isExecutableUserEqual(localAction.executableUser, remoteAction.executableUser)) diffs.push(`executableUser: ${formatValue(remoteAction.executableUser)} -> ${formatValue(localAction.executableUser)}`);
|
|
9676
9812
|
return diffs;
|
|
@@ -9729,7 +9865,7 @@ function compareActionEntries(local, remote, entries) {
|
|
|
9729
9865
|
type: "added",
|
|
9730
9866
|
category: "action",
|
|
9731
9867
|
name: actionLabel(pair.local, pair.index),
|
|
9732
|
-
details: `filterCond:
|
|
9868
|
+
details: `filterCond: ${formatValue(pair.local.filterCond)}`
|
|
9733
9869
|
});
|
|
9734
9870
|
break;
|
|
9735
9871
|
case "remoteOnly":
|
|
@@ -9737,7 +9873,7 @@ function compareActionEntries(local, remote, entries) {
|
|
|
9737
9873
|
type: "deleted",
|
|
9738
9874
|
category: "action",
|
|
9739
9875
|
name: actionLabel(pair.remote, pair.index),
|
|
9740
|
-
details: `filterCond:
|
|
9876
|
+
details: `filterCond: ${formatValue(pair.remote.filterCond)}`
|
|
9741
9877
|
});
|
|
9742
9878
|
break;
|
|
9743
9879
|
case "both": {
|
|
@@ -9854,11 +9990,11 @@ async function detectRecordPermissionDiff({ container }) {
|
|
|
9854
9990
|
//#region src/core/domain/report/services/diffDetector.ts
|
|
9855
9991
|
function compareReports(local, remote) {
|
|
9856
9992
|
const diffs = [];
|
|
9857
|
-
if (local.name !== remote.name) diffs.push(`name:
|
|
9993
|
+
if (local.name !== remote.name) diffs.push(`name: ${formatValue(remote.name)} -> ${formatValue(local.name)}`);
|
|
9858
9994
|
if (local.chartType !== remote.chartType) diffs.push(`chartType: ${remote.chartType} -> ${local.chartType}`);
|
|
9859
9995
|
if ((local.chartMode ?? "") !== (remote.chartMode ?? "")) diffs.push(`chartMode: ${remote.chartMode ?? "(unset)"} -> ${local.chartMode ?? "(unset)"}`);
|
|
9860
9996
|
if (local.index !== remote.index) diffs.push(`index: ${remote.index} -> ${local.index}`);
|
|
9861
|
-
if (local.filterCond !== remote.filterCond) diffs.push(`filterCond:
|
|
9997
|
+
if (local.filterCond !== remote.filterCond) diffs.push(`filterCond: ${formatValue(remote.filterCond)} -> ${formatValue(local.filterCond)}`);
|
|
9862
9998
|
if (!deepEqual(local.groups, remote.groups)) diffs.push(`groups: ${formatValue(remote.groups)} -> ${formatValue(local.groups)}`);
|
|
9863
9999
|
if (!deepEqual(local.aggregations, remote.aggregations)) diffs.push(`aggregations: ${formatValue(remote.aggregations)} -> ${formatValue(local.aggregations)}`);
|
|
9864
10000
|
if (!deepEqual(local.sorts, remote.sorts)) diffs.push(`sorts: ${formatValue(remote.sorts)} -> ${formatValue(local.sorts)}`);
|
|
@@ -9901,7 +10037,7 @@ async function detectReportDiff({ container }) {
|
|
|
9901
10037
|
//#endregion
|
|
9902
10038
|
//#region src/core/domain/view/services/diffDetector.ts
|
|
9903
10039
|
function checkOptionalStringChange(changes, field, localVal, remoteVal) {
|
|
9904
|
-
if ((localVal ?? "") !== (remoteVal ?? "")) changes.push(`${field}:
|
|
10040
|
+
if ((localVal ?? "") !== (remoteVal ?? "")) changes.push(`${field}: ${formatValue(remoteVal ?? "")} -> ${formatValue(localVal ?? "")}`);
|
|
9905
10041
|
}
|
|
9906
10042
|
function describeChanges(local, remote) {
|
|
9907
10043
|
const changes = [];
|
|
@@ -11209,7 +11345,7 @@ const NotificationConfigSerializer = { serialize: (config) => {
|
|
|
11209
11345
|
};
|
|
11210
11346
|
if (config.perRecord !== void 0) serialized.perRecord = config.perRecord.map(serializePerRecordNotification);
|
|
11211
11347
|
if (config.reminder !== void 0) serialized.reminder = {
|
|
11212
|
-
timezone: config.reminder.timezone,
|
|
11348
|
+
...config.reminder.timezone !== void 0 ? { timezone: config.reminder.timezone } : {},
|
|
11213
11349
|
notifications: config.reminder.notifications.map(serializeReminderNotification)
|
|
11214
11350
|
};
|
|
11215
11351
|
return serialized;
|
|
@@ -11217,22 +11353,7 @@ const NotificationConfigSerializer = { serialize: (config) => {
|
|
|
11217
11353
|
//#endregion
|
|
11218
11354
|
//#region src/core/application/notification/captureNotification.ts
|
|
11219
11355
|
async function captureNotification({ container }) {
|
|
11220
|
-
const
|
|
11221
|
-
container.notificationConfigurator.getGeneralNotifications(),
|
|
11222
|
-
container.notificationConfigurator.getPerRecordNotifications(),
|
|
11223
|
-
container.notificationConfigurator.getReminderNotifications()
|
|
11224
|
-
]);
|
|
11225
|
-
const config = {
|
|
11226
|
-
general: {
|
|
11227
|
-
notifyToCommenter: general.notifyToCommenter,
|
|
11228
|
-
notifications: general.notifications
|
|
11229
|
-
},
|
|
11230
|
-
perRecord: perRecord.notifications,
|
|
11231
|
-
reminder: {
|
|
11232
|
-
timezone: reminder.timezone,
|
|
11233
|
-
notifications: reminder.notifications
|
|
11234
|
-
}
|
|
11235
|
-
};
|
|
11356
|
+
const { config } = await loadNotificationRemote(container);
|
|
11236
11357
|
return {
|
|
11237
11358
|
configText: stringifyConfig(container.configCodec, NotificationConfigSerializer.serialize(config)),
|
|
11238
11359
|
hasExistingConfig: (await container.notificationStorage.get()).exists
|
|
@@ -13800,7 +13921,8 @@ var capture_default$8 = createCaptureCommand({
|
|
|
13800
13921
|
* conflict-resolution namespace:
|
|
13801
13922
|
*
|
|
13802
13923
|
* - **reminder** = `code` stable key (`compareReminderSection`, diffDetector
|
|
13803
|
-
* reminder map). Plus the `timezone` scalar as a single whole-entity 3-way
|
|
13924
|
+
* reminder map). Plus the `timezone` scalar as a single whole-entity 3-way
|
|
13925
|
+
* when the local config manages it (sets a timezone).
|
|
13804
13926
|
* - **perRecord** = group by `filterCond`, then within a group match by index
|
|
13805
13927
|
* (`filterCond#i` composite key — `comparePerRecordSection`). `perRecordLabel`
|
|
13806
13928
|
* is display-only and is NOT used as a match key.
|
|
@@ -13863,7 +13985,7 @@ function computeNotificationThreeWayMerge(base, local, remote) {
|
|
|
13863
13985
|
const perRecord = classifyThreeWay(buildPerRecordMap(base.perRecord), buildPerRecordMap(local.perRecord), buildPerRecordMap(remote.perRecord), isPerRecordEqual);
|
|
13864
13986
|
const reminder = classifyThreeWay(buildReminderMap(base.reminder), buildReminderMap(local.reminder), buildReminderMap(remote.reminder), isReminderEqual);
|
|
13865
13987
|
const generalScalar = computeSingleThreeWayMerge(base.general?.notifyToCommenter ?? false, local.general?.notifyToCommenter ?? false, remote.general?.notifyToCommenter ?? false, (a, b) => a === b);
|
|
13866
|
-
const reminderTimezone = computeSingleThreeWayMerge(base.reminder?.timezone ??
|
|
13988
|
+
const reminderTimezone = local.reminder?.timezone !== void 0 ? computeSingleThreeWayMerge(base.reminder?.timezone ?? null, local.reminder.timezone, remote.reminder?.timezone ?? null, (a, b) => a === b) : void 0;
|
|
13867
13989
|
const entries = [
|
|
13868
13990
|
...general.entries,
|
|
13869
13991
|
...perRecord.entries,
|
|
@@ -13874,12 +13996,12 @@ function computeNotificationThreeWayMerge(base, local, remote) {
|
|
|
13874
13996
|
...perRecord.conflicts.map((e) => ({ key: e.key })),
|
|
13875
13997
|
...reminder.conflicts.map((e) => ({ key: e.key })),
|
|
13876
13998
|
...scalarConflictKey(generalScalar, GENERAL_SCALAR_KEY),
|
|
13877
|
-
...scalarConflictKey(reminderTimezone, REMINDER_TIMEZONE_KEY)
|
|
13999
|
+
...reminderTimezone !== void 0 ? scalarConflictKey(reminderTimezone, REMINDER_TIMEZONE_KEY) : []
|
|
13878
14000
|
];
|
|
13879
14001
|
return {
|
|
13880
14002
|
entries,
|
|
13881
14003
|
generalScalar,
|
|
13882
|
-
reminderTimezone,
|
|
14004
|
+
...reminderTimezone !== void 0 ? { reminderTimezone } : {},
|
|
13883
14005
|
conflicts,
|
|
13884
14006
|
hasConflict: conflicts.length > 0
|
|
13885
14007
|
};
|
|
@@ -13929,7 +14051,7 @@ function collectReminderNotifications(entries, resolution) {
|
|
|
13929
14051
|
*/
|
|
13930
14052
|
function resolveNotificationMerge(merge, resolution) {
|
|
13931
14053
|
const notifyToCommenter = resolveSingleMerge(merge.generalScalar, resolution.get(GENERAL_SCALAR_KEY), NotificationErrorCode.NtUnresolvedConflict);
|
|
13932
|
-
const timezone = resolveSingleMerge(merge.reminderTimezone, resolution.get(REMINDER_TIMEZONE_KEY), NotificationErrorCode.NtUnresolvedConflict);
|
|
14054
|
+
const timezone = merge.reminderTimezone !== void 0 ? resolveSingleMerge(merge.reminderTimezone, resolution.get(REMINDER_TIMEZONE_KEY), NotificationErrorCode.NtUnresolvedConflict) : null;
|
|
13933
14055
|
return {
|
|
13934
14056
|
general: {
|
|
13935
14057
|
notifyToCommenter,
|
|
@@ -13937,7 +14059,7 @@ function resolveNotificationMerge(merge, resolution) {
|
|
|
13937
14059
|
},
|
|
13938
14060
|
perRecord: collectPerRecordNotifications(merge.entries, resolution),
|
|
13939
14061
|
reminder: {
|
|
13940
|
-
timezone,
|
|
14062
|
+
...timezone !== null ? { timezone } : {},
|
|
13941
14063
|
notifications: collectReminderNotifications(merge.entries, resolution)
|
|
13942
14064
|
}
|
|
13943
14065
|
};
|
|
@@ -13956,37 +14078,6 @@ const NotificationStateParser = { parse: (parsed) => ({ config: NotificationConf
|
|
|
13956
14078
|
//#endregion
|
|
13957
14079
|
//#region src/core/application/notification/loadNotificationThreeWayInputs.ts
|
|
13958
14080
|
/**
|
|
13959
|
-
* Fetches the three remote sub-configs (general / perRecord / reminder) and
|
|
13960
|
-
* bundles them into a single {@link NotificationRemote}.
|
|
13961
|
-
*
|
|
13962
|
-
* The three getters each carry the app (preview) revision; since the revision is
|
|
13963
|
-
* app-scoped and the three are fetched at (essentially) the same moment, the
|
|
13964
|
-
* general getter's revision is used as the observed remote revision for the
|
|
13965
|
-
* TOCTOU guard. kintone always returns all three sections, so the bundled config
|
|
13966
|
-
* always has `general` / `perRecord` / `reminder` defined.
|
|
13967
|
-
*/
|
|
13968
|
-
async function loadNotificationRemote(container) {
|
|
13969
|
-
const [general, perRecord, reminder] = await Promise.all([
|
|
13970
|
-
container.notificationConfigurator.getGeneralNotifications(),
|
|
13971
|
-
container.notificationConfigurator.getPerRecordNotifications(),
|
|
13972
|
-
container.notificationConfigurator.getReminderNotifications()
|
|
13973
|
-
]);
|
|
13974
|
-
return {
|
|
13975
|
-
config: {
|
|
13976
|
-
general: {
|
|
13977
|
-
notifyToCommenter: general.notifyToCommenter,
|
|
13978
|
-
notifications: general.notifications
|
|
13979
|
-
},
|
|
13980
|
-
perRecord: perRecord.notifications,
|
|
13981
|
-
reminder: {
|
|
13982
|
-
timezone: reminder.timezone,
|
|
13983
|
-
notifications: reminder.notifications
|
|
13984
|
-
}
|
|
13985
|
-
},
|
|
13986
|
-
revision: general.revision
|
|
13987
|
-
};
|
|
13988
|
-
}
|
|
13989
|
-
/**
|
|
13990
14081
|
* Loads the four inputs of a 3-way notification sync (base snapshot, base app
|
|
13991
14082
|
* revision, local YAML, remote config + revision) via the generic
|
|
13992
14083
|
* {@link loadThreeWayInputs}. Notification bundles three sub-configs into one
|
|
@@ -14089,7 +14180,7 @@ function buildThreeWay(merge) {
|
|
|
14089
14180
|
}
|
|
14090
14181
|
}
|
|
14091
14182
|
pushScalar(merge.generalScalar, "general:notifyToCommenter", "notifyToCommenter", localChanges, remoteDrift, conflicts, extras);
|
|
14092
|
-
pushScalar(merge.reminderTimezone, "reminder:timezone", "timezone", localChanges, remoteDrift, conflicts, extras);
|
|
14183
|
+
if (merge.reminderTimezone !== void 0) pushScalar(merge.reminderTimezone, "reminder:timezone", "timezone", localChanges, remoteDrift, conflicts, extras);
|
|
14093
14184
|
return {
|
|
14094
14185
|
mode: "three-way",
|
|
14095
14186
|
localChanges,
|
|
@@ -14312,7 +14403,7 @@ async function pushNotification({ container, input }) {
|
|
|
14312
14403
|
return false;
|
|
14313
14404
|
};
|
|
14314
14405
|
const isDriftKind = (kind) => kind === "remoteOnly" || kind === "conflict";
|
|
14315
|
-
if (merge.entries.some((e) => isManaged(e.key) && isDriftKind(e.change.kind)) || local.general !== void 0 && isDriftKind(merge.generalScalar.change.kind) ||
|
|
14406
|
+
if (merge.entries.some((e) => isManaged(e.key) && isDriftKind(e.change.kind)) || local.general !== void 0 && isDriftKind(merge.generalScalar.change.kind) || merge.reminderTimezone !== void 0 && isDriftKind(merge.reminderTimezone.change.kind)) throw buildDriftConflict(NOTIFICATION_PULL_COMMAND);
|
|
14316
14407
|
}
|
|
14317
14408
|
const sendRevision = !(input.force || firstTime);
|
|
14318
14409
|
let revision = remote.revision;
|
|
@@ -14338,11 +14429,14 @@ async function pushNotification({ container, input }) {
|
|
|
14338
14429
|
}
|
|
14339
14430
|
if (local.reminder !== void 0) {
|
|
14340
14431
|
revision = (await container.notificationConfigurator.updateReminderNotifications({
|
|
14341
|
-
|
|
14342
|
-
notifications: local.reminder.notifications,
|
|
14432
|
+
...local.reminder,
|
|
14343
14433
|
...sendRevision ? { revision } : {}
|
|
14344
14434
|
})).revision;
|
|
14345
|
-
|
|
14435
|
+
const timezone = local.reminder.timezone ?? remote.config.reminder?.timezone;
|
|
14436
|
+
newBase.reminder = {
|
|
14437
|
+
...timezone !== void 0 ? { timezone } : {},
|
|
14438
|
+
notifications: local.reminder.notifications
|
|
14439
|
+
};
|
|
14346
14440
|
}
|
|
14347
14441
|
await saveNotificationSnapshotAndRevision(container, newBase, revision);
|
|
14348
14442
|
return {
|