kintone-migrator 0.35.2 → 0.35.4
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 +208 -178
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -121,7 +121,6 @@ const ProcessManagementErrorCode = {
|
|
|
121
121
|
PmInvalidEntityType: "PM_INVALID_ENTITY_TYPE",
|
|
122
122
|
PmInvalidBooleanField: "PM_INVALID_BOOLEAN_FIELD",
|
|
123
123
|
PmInvalidActionReference: "PM_INVALID_ACTION_REFERENCE",
|
|
124
|
-
PmDuplicateActionName: "PM_DUPLICATE_ACTION_NAME",
|
|
125
124
|
PmUnresolvedConflict: "PM_UNRESOLVED_CONFLICT"
|
|
126
125
|
};
|
|
127
126
|
//#endregion
|
|
@@ -6600,6 +6599,13 @@ const VALID_ENTITY_TYPES$5 = /* @__PURE__ */ new Set([
|
|
|
6600
6599
|
function isNotificationEntityType(value) {
|
|
6601
6600
|
return VALID_ENTITY_TYPES$5.has(value);
|
|
6602
6601
|
}
|
|
6602
|
+
/**
|
|
6603
|
+
* Normalizes a reminder timezone read at a boundary (kintone API or config
|
|
6604
|
+
* file) to the domain form: `null` and `""` mean unset (`undefined`).
|
|
6605
|
+
*/
|
|
6606
|
+
function normalizeReminderTimezone(value) {
|
|
6607
|
+
return value === null || value === "" ? void 0 : value;
|
|
6608
|
+
}
|
|
6603
6609
|
//#endregion
|
|
6604
6610
|
//#region src/core/domain/notification/services/configParser.ts
|
|
6605
6611
|
const REMINDER_OFFSET_MIN = -1e4;
|
|
@@ -6695,10 +6701,13 @@ function parseReminderConfig(raw) {
|
|
|
6695
6701
|
if (!isRecord(raw)) throw new BusinessRuleError(NotificationErrorCode.NtInvalidConfigStructure, "\"reminder\" must be an object");
|
|
6696
6702
|
if (!Array.isArray(raw.notifications)) throw new BusinessRuleError(NotificationErrorCode.NtInvalidConfigStructure, "\"reminder\" must have a \"notifications\" array");
|
|
6697
6703
|
const notifications = raw.notifications.map((item, i) => parseReminderNotification(item, i));
|
|
6698
|
-
|
|
6699
|
-
|
|
6704
|
+
const rawTimezone = raw.timezone ?? null;
|
|
6705
|
+
if (rawTimezone !== null && typeof rawTimezone !== "string") throw new BusinessRuleError(NotificationErrorCode.NtInvalidConfigStructure, `"reminder" has non-string "timezone": ${String(rawTimezone)}`);
|
|
6706
|
+
const timezone = normalizeReminderTimezone(rawTimezone);
|
|
6707
|
+
return timezone !== void 0 ? {
|
|
6708
|
+
timezone,
|
|
6700
6709
|
notifications
|
|
6701
|
-
};
|
|
6710
|
+
} : { notifications };
|
|
6702
6711
|
}
|
|
6703
6712
|
const NotificationConfigParser = { parse: (parsed) => {
|
|
6704
6713
|
const obj = validateParsedConfig(parsed, NotificationErrorCode.NtInvalidConfigStructure, "Notification");
|
|
@@ -6747,8 +6756,7 @@ async function applyNotification({ container }) {
|
|
|
6747
6756
|
if (config.reminder !== void 0) {
|
|
6748
6757
|
const currentReminder = await container.notificationConfigurator.getReminderNotifications();
|
|
6749
6758
|
await container.notificationConfigurator.updateReminderNotifications({
|
|
6750
|
-
|
|
6751
|
-
notifications: config.reminder.notifications,
|
|
6759
|
+
...config.reminder,
|
|
6752
6760
|
revision: currentReminder.revision
|
|
6753
6761
|
});
|
|
6754
6762
|
}
|
|
@@ -6811,6 +6819,16 @@ async function applyPlugin({ container }) {
|
|
|
6811
6819
|
};
|
|
6812
6820
|
}
|
|
6813
6821
|
//#endregion
|
|
6822
|
+
//#region src/core/domain/processManagement/valueObject.ts
|
|
6823
|
+
/**
|
|
6824
|
+
* Human-readable label of an action, e.g. `申請する (未申請 → 部長承認待ち)`.
|
|
6825
|
+
* The name alone is ambiguous: kintone allows several actions with the same
|
|
6826
|
+
* name, both from different states and as conditional branches from one state.
|
|
6827
|
+
*/
|
|
6828
|
+
function describeProcessAction(action) {
|
|
6829
|
+
return `${action.name} (${action.from} → ${action.to})`;
|
|
6830
|
+
}
|
|
6831
|
+
//#endregion
|
|
6814
6832
|
//#region src/core/domain/processManagement/services/configParser.ts
|
|
6815
6833
|
const VALID_ASSIGNEE_TYPES$1 = /* @__PURE__ */ new Set([
|
|
6816
6834
|
"ONE",
|
|
@@ -6890,15 +6908,10 @@ const ProcessManagementConfigParser = { parse: (parsed) => {
|
|
|
6890
6908
|
for (const [name, value] of Object.entries(rawStates)) states[name] = parseState(value, name);
|
|
6891
6909
|
if (!Array.isArray(obj.actions) && obj.actions !== void 0) throw new BusinessRuleError(ProcessManagementErrorCode.PmInvalidConfigStructure, "Config \"actions\" must be an array");
|
|
6892
6910
|
const actions = (Array.isArray(obj.actions) ? obj.actions : []).map((item, i) => parseAction(item, i));
|
|
6893
|
-
const actionNames = /* @__PURE__ */ new Set();
|
|
6894
|
-
for (const action of actions) {
|
|
6895
|
-
if (actionNames.has(action.name)) throw new BusinessRuleError(ProcessManagementErrorCode.PmDuplicateActionName, `Duplicate action name: "${action.name}"`);
|
|
6896
|
-
actionNames.add(action.name);
|
|
6897
|
-
}
|
|
6898
6911
|
const stateNames = new Set(Object.keys(states));
|
|
6899
6912
|
for (const action of actions) {
|
|
6900
|
-
if (!stateNames.has(action.from)) throw new BusinessRuleError(ProcessManagementErrorCode.PmInvalidActionReference, `Action "${action
|
|
6901
|
-
if (!stateNames.has(action.to)) throw new BusinessRuleError(ProcessManagementErrorCode.PmInvalidActionReference, `Action "${action
|
|
6913
|
+
if (!stateNames.has(action.from)) throw new BusinessRuleError(ProcessManagementErrorCode.PmInvalidActionReference, `Action "${describeProcessAction(action)}" references unknown "from" state: "${action.from}"`);
|
|
6914
|
+
if (!stateNames.has(action.to)) throw new BusinessRuleError(ProcessManagementErrorCode.PmInvalidActionReference, `Action "${describeProcessAction(action)}" references unknown "to" state: "${action.to}"`);
|
|
6902
6915
|
}
|
|
6903
6916
|
return {
|
|
6904
6917
|
enable,
|
|
@@ -8241,8 +8254,11 @@ var KintoneNotificationConfigurator = class {
|
|
|
8241
8254
|
preview: true
|
|
8242
8255
|
});
|
|
8243
8256
|
const notifications = response.notifications.map(fromKintoneReminderNotification);
|
|
8257
|
+
const rawTimezone = response.timezone ?? null;
|
|
8258
|
+
if (rawTimezone !== null && typeof rawTimezone !== "string") throw new SystemError(SystemErrorCode.ExternalApiError, `Unexpected reminder timezone from kintone API: ${String(rawTimezone)}`);
|
|
8259
|
+
const timezone = normalizeReminderTimezone(rawTimezone);
|
|
8244
8260
|
return {
|
|
8245
|
-
timezone:
|
|
8261
|
+
...timezone !== void 0 ? { timezone } : {},
|
|
8246
8262
|
notifications,
|
|
8247
8263
|
revision: response.revision
|
|
8248
8264
|
};
|
|
@@ -8254,9 +8270,9 @@ var KintoneNotificationConfigurator = class {
|
|
|
8254
8270
|
try {
|
|
8255
8271
|
const requestParams = {
|
|
8256
8272
|
app: this.appId,
|
|
8257
|
-
timezone: params.timezone,
|
|
8258
8273
|
notifications: params.notifications.map(toKintoneReminderNotification)
|
|
8259
8274
|
};
|
|
8275
|
+
if (params.timezone !== void 0) requestParams.timezone = params.timezone;
|
|
8260
8276
|
if (params.revision !== void 0) requestParams.revision = params.revision;
|
|
8261
8277
|
return { revision: (await this.client.app.updateReminderNotifications(requestParams)).revision };
|
|
8262
8278
|
} catch (error) {
|
|
@@ -9363,6 +9379,56 @@ function groupByKey(items, keyFn) {
|
|
|
9363
9379
|
return map;
|
|
9364
9380
|
}
|
|
9365
9381
|
//#endregion
|
|
9382
|
+
//#region src/lib/pairByKeyInOrder.ts
|
|
9383
|
+
/**
|
|
9384
|
+
* Pairs local and remote items by key. Items sharing a key are matched by
|
|
9385
|
+
* their order of appearance, so the n-th local item with a key pairs with the
|
|
9386
|
+
* n-th remote item with that key.
|
|
9387
|
+
*
|
|
9388
|
+
* Pairs are returned grouped by key: keys in local first-appearance order,
|
|
9389
|
+
* then remote-only keys in remote first-appearance order.
|
|
9390
|
+
*/
|
|
9391
|
+
function pairByKeyInOrder(local, remote, keyFn) {
|
|
9392
|
+
const localGroups = groupByKey(local, keyFn);
|
|
9393
|
+
const remoteGroups = groupByKey(remote, keyFn);
|
|
9394
|
+
const pairs = [];
|
|
9395
|
+
for (const [key, localItems] of localGroups) {
|
|
9396
|
+
const remoteItems = remoteGroups.get(key) ?? [];
|
|
9397
|
+
const length = Math.max(localItems.length, remoteItems.length);
|
|
9398
|
+
for (let index = 0; index < length; index++) if (index >= remoteItems.length) pairs.push({
|
|
9399
|
+
kind: "localOnly",
|
|
9400
|
+
key,
|
|
9401
|
+
index,
|
|
9402
|
+
local: localItems[index]
|
|
9403
|
+
});
|
|
9404
|
+
else if (index >= localItems.length) pairs.push({
|
|
9405
|
+
kind: "remoteOnly",
|
|
9406
|
+
key,
|
|
9407
|
+
index,
|
|
9408
|
+
remote: remoteItems[index]
|
|
9409
|
+
});
|
|
9410
|
+
else pairs.push({
|
|
9411
|
+
kind: "both",
|
|
9412
|
+
key,
|
|
9413
|
+
index,
|
|
9414
|
+
local: localItems[index],
|
|
9415
|
+
remote: remoteItems[index]
|
|
9416
|
+
});
|
|
9417
|
+
}
|
|
9418
|
+
for (const [key, remoteItems] of remoteGroups) {
|
|
9419
|
+
if (localGroups.has(key)) continue;
|
|
9420
|
+
remoteItems.forEach((remoteItem, index) => {
|
|
9421
|
+
pairs.push({
|
|
9422
|
+
kind: "remoteOnly",
|
|
9423
|
+
key,
|
|
9424
|
+
index,
|
|
9425
|
+
remote: remoteItem
|
|
9426
|
+
});
|
|
9427
|
+
});
|
|
9428
|
+
}
|
|
9429
|
+
return pairs;
|
|
9430
|
+
}
|
|
9431
|
+
//#endregion
|
|
9366
9432
|
//#region src/core/domain/notification/services/diffDetector.ts
|
|
9367
9433
|
function serializeEntity$1(entity) {
|
|
9368
9434
|
return `${entity.type}:${entity.code}`;
|
|
@@ -9416,14 +9482,6 @@ function compareGeneralSection(local, remote) {
|
|
|
9416
9482
|
});
|
|
9417
9483
|
return entries;
|
|
9418
9484
|
}
|
|
9419
|
-
/**
|
|
9420
|
-
* Groups per-record notifications by filterCond. Multiple notifications can share
|
|
9421
|
-
* the same filterCond, so notifications within a group are compared by position
|
|
9422
|
-
* (index-based matching).
|
|
9423
|
-
*/
|
|
9424
|
-
function buildPerRecordMultiMap(notifications) {
|
|
9425
|
-
return groupByKey(notifications, (n) => n.filterCond);
|
|
9426
|
-
}
|
|
9427
9485
|
function perRecordLabel(notif) {
|
|
9428
9486
|
return notif.title || notif.filterCond || "(empty filter)";
|
|
9429
9487
|
}
|
|
@@ -9445,43 +9503,30 @@ function describeReminderChanges(local, remote) {
|
|
|
9445
9503
|
}
|
|
9446
9504
|
function comparePerRecordSection(local, remote) {
|
|
9447
9505
|
const entries = [];
|
|
9448
|
-
const
|
|
9449
|
-
|
|
9450
|
-
|
|
9451
|
-
const remoteNotifs = remoteMulti.get(key) ?? [];
|
|
9452
|
-
const maxLen = Math.max(localNotifs.length, remoteNotifs.length);
|
|
9453
|
-
for (let i = 0; i < maxLen; i++) {
|
|
9454
|
-
const localNotif = localNotifs[i];
|
|
9455
|
-
const remoteNotif = remoteNotifs[i];
|
|
9456
|
-
if (localNotif && !remoteNotif) entries.push({
|
|
9506
|
+
for (const pair of pairByKeyInOrder(local, remote, (n) => n.filterCond)) switch (pair.kind) {
|
|
9507
|
+
case "localOnly":
|
|
9508
|
+
entries.push({
|
|
9457
9509
|
type: "added",
|
|
9458
9510
|
section: "perRecord",
|
|
9459
|
-
name: perRecordLabel(
|
|
9511
|
+
name: perRecordLabel(pair.local),
|
|
9460
9512
|
details: "new notification"
|
|
9461
9513
|
});
|
|
9462
|
-
|
|
9514
|
+
break;
|
|
9515
|
+
case "remoteOnly":
|
|
9516
|
+
entries.push({
|
|
9463
9517
|
type: "deleted",
|
|
9464
9518
|
section: "perRecord",
|
|
9465
|
-
name: perRecordLabel(
|
|
9519
|
+
name: perRecordLabel(pair.remote),
|
|
9466
9520
|
details: "removed"
|
|
9467
9521
|
});
|
|
9468
|
-
|
|
9469
|
-
|
|
9470
|
-
|
|
9471
|
-
|
|
9472
|
-
|
|
9473
|
-
|
|
9474
|
-
|
|
9475
|
-
});
|
|
9476
|
-
}
|
|
9477
|
-
}
|
|
9522
|
+
break;
|
|
9523
|
+
case "both": if (!deepEqual(pair.local, pair.remote)) entries.push({
|
|
9524
|
+
type: "modified",
|
|
9525
|
+
section: "perRecord",
|
|
9526
|
+
name: perRecordLabel(pair.local),
|
|
9527
|
+
details: describePerRecordChanges(pair.local, pair.remote)
|
|
9528
|
+
});
|
|
9478
9529
|
}
|
|
9479
|
-
for (const [key, remoteNotifs] of remoteMulti) if (!localMulti.has(key)) for (const remoteNotif of remoteNotifs) entries.push({
|
|
9480
|
-
type: "deleted",
|
|
9481
|
-
section: "perRecord",
|
|
9482
|
-
name: perRecordLabel(remoteNotif),
|
|
9483
|
-
details: "removed"
|
|
9484
|
-
});
|
|
9485
9530
|
return entries;
|
|
9486
9531
|
}
|
|
9487
9532
|
function compareReminderSection(local, remote) {
|
|
@@ -9531,11 +9576,11 @@ const NotificationDiffDetector = { detect: (local, remote) => {
|
|
|
9531
9576
|
entries.push(...comparePerRecordSection(localPerRecord, remotePerRecord));
|
|
9532
9577
|
if (local.reminder && remote.reminder) {
|
|
9533
9578
|
entries.push(...compareReminderSection(local.reminder.notifications, remote.reminder.notifications));
|
|
9534
|
-
if (local.reminder.timezone !== remote.reminder.timezone) entries.push({
|
|
9579
|
+
if (local.reminder.timezone !== void 0 && local.reminder.timezone !== remote.reminder.timezone) entries.push({
|
|
9535
9580
|
type: "modified",
|
|
9536
9581
|
section: "reminder",
|
|
9537
9582
|
name: "timezone",
|
|
9538
|
-
details: `${remote.reminder.timezone} -> ${local.reminder.timezone}`
|
|
9583
|
+
details: `${remote.reminder.timezone ?? "(unset)"} -> ${local.reminder.timezone}`
|
|
9539
9584
|
});
|
|
9540
9585
|
} else if (local.reminder && !remote.reminder) entries.push({
|
|
9541
9586
|
type: "added",
|
|
@@ -9552,28 +9597,46 @@ const NotificationDiffDetector = { detect: (local, remote) => {
|
|
|
9552
9597
|
return buildDiffResult(entries);
|
|
9553
9598
|
} };
|
|
9554
9599
|
//#endregion
|
|
9555
|
-
//#region src/core/application/notification/
|
|
9556
|
-
|
|
9557
|
-
|
|
9558
|
-
|
|
9600
|
+
//#region src/core/application/notification/loadNotificationRemote.ts
|
|
9601
|
+
/**
|
|
9602
|
+
* Fetches the three remote sub-configs (general / perRecord / reminder) and
|
|
9603
|
+
* bundles them into a single {@link NotificationRemote}.
|
|
9604
|
+
*
|
|
9605
|
+
* The three getters each carry the app (preview) revision; since the revision is
|
|
9606
|
+
* app-scoped and the three are fetched at (essentially) the same moment, the
|
|
9607
|
+
* general getter's revision is used as the observed remote revision for the
|
|
9608
|
+
* TOCTOU guard. kintone always returns all three sections, so the bundled config
|
|
9609
|
+
* always has `general` / `perRecord` / `reminder` defined (an unset reminder
|
|
9610
|
+
* timezone stays absent).
|
|
9611
|
+
*/
|
|
9612
|
+
async function loadNotificationRemote(container) {
|
|
9613
|
+
const [general, perRecord, reminder] = await Promise.all([
|
|
9559
9614
|
container.notificationConfigurator.getGeneralNotifications(),
|
|
9560
9615
|
container.notificationConfigurator.getPerRecordNotifications(),
|
|
9561
9616
|
container.notificationConfigurator.getReminderNotifications()
|
|
9562
9617
|
]);
|
|
9563
|
-
|
|
9564
|
-
|
|
9565
|
-
|
|
9566
|
-
|
|
9567
|
-
|
|
9568
|
-
|
|
9618
|
+
return {
|
|
9619
|
+
config: {
|
|
9620
|
+
general: {
|
|
9621
|
+
notifyToCommenter: general.notifyToCommenter,
|
|
9622
|
+
notifications: general.notifications
|
|
9623
|
+
},
|
|
9624
|
+
perRecord: perRecord.notifications,
|
|
9625
|
+
reminder: {
|
|
9626
|
+
...reminder.timezone !== void 0 ? { timezone: reminder.timezone } : {},
|
|
9627
|
+
notifications: reminder.notifications
|
|
9628
|
+
}
|
|
9569
9629
|
},
|
|
9570
|
-
|
|
9571
|
-
reminder: {
|
|
9572
|
-
timezone: reminderResult.timezone,
|
|
9573
|
-
notifications: reminderResult.notifications
|
|
9574
|
-
}
|
|
9630
|
+
revision: general.revision
|
|
9575
9631
|
};
|
|
9576
|
-
|
|
9632
|
+
}
|
|
9633
|
+
//#endregion
|
|
9634
|
+
//#region src/core/application/notification/detectNotificationDiff.ts
|
|
9635
|
+
async function detectNotificationDiff({ container }) {
|
|
9636
|
+
const [storageResult, remote] = await Promise.all([container.notificationStorage.get(), loadNotificationRemote(container)]);
|
|
9637
|
+
if (!storageResult.exists) throw new ValidationError(ValidationErrorCode.InvalidInput, "Notification config file not found");
|
|
9638
|
+
const localConfig = parseNotificationConfigText(container.configCodec, storageResult.content);
|
|
9639
|
+
return NotificationDiffDetector.detect(localConfig, remote.config);
|
|
9577
9640
|
}
|
|
9578
9641
|
//#endregion
|
|
9579
9642
|
//#region src/core/domain/plugin/services/diffDetector.ts
|
|
@@ -9637,8 +9700,6 @@ function isExecutableUserEqual(a, b) {
|
|
|
9637
9700
|
}
|
|
9638
9701
|
function compareActions(localAction, remoteAction) {
|
|
9639
9702
|
const diffs = [];
|
|
9640
|
-
if (localAction.from !== remoteAction.from) diffs.push(`from: ${remoteAction.from} -> ${localAction.from}`);
|
|
9641
|
-
if (localAction.to !== remoteAction.to) diffs.push(`to: ${remoteAction.to} -> ${localAction.to}`);
|
|
9642
9703
|
if (localAction.filterCond !== remoteAction.filterCond) diffs.push(`filterCond: "${remoteAction.filterCond}" -> "${localAction.filterCond}"`);
|
|
9643
9704
|
if (localAction.type !== remoteAction.type) diffs.push(`type: ${remoteAction.type} -> ${localAction.type}`);
|
|
9644
9705
|
if (!isExecutableUserEqual(localAction.executableUser, remoteAction.executableUser)) diffs.push(`executableUser: ${formatValue(remoteAction.executableUser)} -> ${formatValue(localAction.executableUser)}`);
|
|
@@ -9674,33 +9735,52 @@ function compareStates(local, remote, entries) {
|
|
|
9674
9735
|
details: `assignee: ${remote.states[name].assignee.type}`
|
|
9675
9736
|
});
|
|
9676
9737
|
}
|
|
9738
|
+
/**
|
|
9739
|
+
* Identifies an action by name + from + to. kintone allows same-named actions
|
|
9740
|
+
* both from different states and as conditional branches from one state.
|
|
9741
|
+
* Repeats of the whole triple are not rejected either, so actions sharing a
|
|
9742
|
+
* triple are told apart by their order of appearance.
|
|
9743
|
+
*/
|
|
9744
|
+
function transitionKey(action) {
|
|
9745
|
+
return JSON.stringify([
|
|
9746
|
+
action.name,
|
|
9747
|
+
action.from,
|
|
9748
|
+
action.to
|
|
9749
|
+
]);
|
|
9750
|
+
}
|
|
9751
|
+
function actionLabel(action, index) {
|
|
9752
|
+
const label = describeProcessAction(action);
|
|
9753
|
+
return index === 0 ? label : `${label} #${index + 1}`;
|
|
9754
|
+
}
|
|
9677
9755
|
function compareActionEntries(local, remote, entries) {
|
|
9678
|
-
const
|
|
9679
|
-
|
|
9680
|
-
|
|
9681
|
-
|
|
9682
|
-
|
|
9683
|
-
|
|
9684
|
-
|
|
9685
|
-
|
|
9686
|
-
|
|
9687
|
-
|
|
9688
|
-
|
|
9689
|
-
|
|
9756
|
+
for (const pair of pairByKeyInOrder(local.actions, remote.actions, transitionKey)) switch (pair.kind) {
|
|
9757
|
+
case "localOnly":
|
|
9758
|
+
entries.push({
|
|
9759
|
+
type: "added",
|
|
9760
|
+
category: "action",
|
|
9761
|
+
name: actionLabel(pair.local, pair.index),
|
|
9762
|
+
details: `filterCond: "${pair.local.filterCond}"`
|
|
9763
|
+
});
|
|
9764
|
+
break;
|
|
9765
|
+
case "remoteOnly":
|
|
9766
|
+
entries.push({
|
|
9767
|
+
type: "deleted",
|
|
9768
|
+
category: "action",
|
|
9769
|
+
name: actionLabel(pair.remote, pair.index),
|
|
9770
|
+
details: `filterCond: "${pair.remote.filterCond}"`
|
|
9771
|
+
});
|
|
9772
|
+
break;
|
|
9773
|
+
case "both": {
|
|
9774
|
+
const actionDiffs = compareActions(pair.local, pair.remote);
|
|
9690
9775
|
if (actionDiffs.length > 0) entries.push({
|
|
9691
9776
|
type: "modified",
|
|
9692
9777
|
category: "action",
|
|
9693
|
-
name,
|
|
9778
|
+
name: actionLabel(pair.local, pair.index),
|
|
9694
9779
|
details: actionDiffs.join("\n")
|
|
9695
9780
|
});
|
|
9781
|
+
break;
|
|
9696
9782
|
}
|
|
9697
9783
|
}
|
|
9698
|
-
for (const [name, remoteAction] of remoteActionMap) if (!localActionMap.has(name)) entries.push({
|
|
9699
|
-
type: "deleted",
|
|
9700
|
-
category: "action",
|
|
9701
|
-
name,
|
|
9702
|
-
details: `${remoteAction.from} -> ${remoteAction.to}`
|
|
9703
|
-
});
|
|
9704
9784
|
}
|
|
9705
9785
|
function compareConfigs(local, remote) {
|
|
9706
9786
|
const entries = [];
|
|
@@ -9758,24 +9838,27 @@ function describeRight(right) {
|
|
|
9758
9838
|
});
|
|
9759
9839
|
return perEntity.length > 0 ? perEntity.join(", ") : "no entities";
|
|
9760
9840
|
}
|
|
9761
|
-
|
|
9762
|
-
const
|
|
9763
|
-
for (
|
|
9764
|
-
const
|
|
9765
|
-
|
|
9766
|
-
|
|
9767
|
-
|
|
9768
|
-
|
|
9769
|
-
|
|
9770
|
-
|
|
9771
|
-
|
|
9772
|
-
|
|
9773
|
-
|
|
9774
|
-
|
|
9775
|
-
|
|
9776
|
-
|
|
9777
|
-
|
|
9778
|
-
|
|
9841
|
+
const RecordPermissionDiffDetector = { detect: (local, remote) => {
|
|
9842
|
+
const entries = [];
|
|
9843
|
+
for (const pair of pairByKeyInOrder(local.rights, remote.rights, (r) => r.filterCond)) {
|
|
9844
|
+
const filterCond = pair.key;
|
|
9845
|
+
switch (pair.kind) {
|
|
9846
|
+
case "localOnly":
|
|
9847
|
+
entries.push({
|
|
9848
|
+
type: "added",
|
|
9849
|
+
filterCond,
|
|
9850
|
+
details: describeRight(pair.local)
|
|
9851
|
+
});
|
|
9852
|
+
break;
|
|
9853
|
+
case "remoteOnly":
|
|
9854
|
+
entries.push({
|
|
9855
|
+
type: "deleted",
|
|
9856
|
+
filterCond,
|
|
9857
|
+
details: describeRight(pair.remote)
|
|
9858
|
+
});
|
|
9859
|
+
break;
|
|
9860
|
+
case "both": if (!isRightEqual(pair.local, pair.remote)) {
|
|
9861
|
+
const detail = pair.local.entities.length !== pair.remote.entities.length ? `entities: ${pair.remote.entities.length} -> ${pair.local.entities.length}` : `entities: ${describeRight(pair.remote)} -> ${describeRight(pair.local)}`;
|
|
9779
9862
|
entries.push({
|
|
9780
9863
|
type: "modified",
|
|
9781
9864
|
filterCond,
|
|
@@ -9784,17 +9867,6 @@ function compareRightsForFilter(filterCond, localRights, remoteRights, entries)
|
|
|
9784
9867
|
}
|
|
9785
9868
|
}
|
|
9786
9869
|
}
|
|
9787
|
-
}
|
|
9788
|
-
const RecordPermissionDiffDetector = { detect: (local, remote) => {
|
|
9789
|
-
const entries = [];
|
|
9790
|
-
const localMulti = groupByKey(local.rights, (r) => r.filterCond);
|
|
9791
|
-
const remoteMulti = groupByKey(remote.rights, (r) => r.filterCond);
|
|
9792
|
-
for (const [filterCond, localRights] of localMulti) compareRightsForFilter(filterCond, localRights, remoteMulti.get(filterCond) ?? [], entries);
|
|
9793
|
-
for (const [filterCond, remoteRights] of remoteMulti) if (!localMulti.has(filterCond)) for (const remoteRight of remoteRights) entries.push({
|
|
9794
|
-
type: "deleted",
|
|
9795
|
-
filterCond,
|
|
9796
|
-
details: describeRight(remoteRight)
|
|
9797
|
-
});
|
|
9798
9870
|
return buildDiffResult(entries);
|
|
9799
9871
|
} };
|
|
9800
9872
|
//#endregion
|
|
@@ -11167,7 +11239,7 @@ const NotificationConfigSerializer = { serialize: (config) => {
|
|
|
11167
11239
|
};
|
|
11168
11240
|
if (config.perRecord !== void 0) serialized.perRecord = config.perRecord.map(serializePerRecordNotification);
|
|
11169
11241
|
if (config.reminder !== void 0) serialized.reminder = {
|
|
11170
|
-
timezone: config.reminder.timezone,
|
|
11242
|
+
...config.reminder.timezone !== void 0 ? { timezone: config.reminder.timezone } : {},
|
|
11171
11243
|
notifications: config.reminder.notifications.map(serializeReminderNotification)
|
|
11172
11244
|
};
|
|
11173
11245
|
return serialized;
|
|
@@ -11175,22 +11247,7 @@ const NotificationConfigSerializer = { serialize: (config) => {
|
|
|
11175
11247
|
//#endregion
|
|
11176
11248
|
//#region src/core/application/notification/captureNotification.ts
|
|
11177
11249
|
async function captureNotification({ container }) {
|
|
11178
|
-
const
|
|
11179
|
-
container.notificationConfigurator.getGeneralNotifications(),
|
|
11180
|
-
container.notificationConfigurator.getPerRecordNotifications(),
|
|
11181
|
-
container.notificationConfigurator.getReminderNotifications()
|
|
11182
|
-
]);
|
|
11183
|
-
const config = {
|
|
11184
|
-
general: {
|
|
11185
|
-
notifyToCommenter: general.notifyToCommenter,
|
|
11186
|
-
notifications: general.notifications
|
|
11187
|
-
},
|
|
11188
|
-
perRecord: perRecord.notifications,
|
|
11189
|
-
reminder: {
|
|
11190
|
-
timezone: reminder.timezone,
|
|
11191
|
-
notifications: reminder.notifications
|
|
11192
|
-
}
|
|
11193
|
-
};
|
|
11250
|
+
const { config } = await loadNotificationRemote(container);
|
|
11194
11251
|
return {
|
|
11195
11252
|
configText: stringifyConfig(container.configCodec, NotificationConfigSerializer.serialize(config)),
|
|
11196
11253
|
hasExistingConfig: (await container.notificationStorage.get()).exists
|
|
@@ -13758,7 +13815,8 @@ var capture_default$8 = createCaptureCommand({
|
|
|
13758
13815
|
* conflict-resolution namespace:
|
|
13759
13816
|
*
|
|
13760
13817
|
* - **reminder** = `code` stable key (`compareReminderSection`, diffDetector
|
|
13761
|
-
* reminder map). Plus the `timezone` scalar as a single whole-entity 3-way
|
|
13818
|
+
* reminder map). Plus the `timezone` scalar as a single whole-entity 3-way
|
|
13819
|
+
* when the local config manages it (sets a timezone).
|
|
13762
13820
|
* - **perRecord** = group by `filterCond`, then within a group match by index
|
|
13763
13821
|
* (`filterCond#i` composite key — `comparePerRecordSection`). `perRecordLabel`
|
|
13764
13822
|
* is display-only and is NOT used as a match key.
|
|
@@ -13821,7 +13879,7 @@ function computeNotificationThreeWayMerge(base, local, remote) {
|
|
|
13821
13879
|
const perRecord = classifyThreeWay(buildPerRecordMap(base.perRecord), buildPerRecordMap(local.perRecord), buildPerRecordMap(remote.perRecord), isPerRecordEqual);
|
|
13822
13880
|
const reminder = classifyThreeWay(buildReminderMap(base.reminder), buildReminderMap(local.reminder), buildReminderMap(remote.reminder), isReminderEqual);
|
|
13823
13881
|
const generalScalar = computeSingleThreeWayMerge(base.general?.notifyToCommenter ?? false, local.general?.notifyToCommenter ?? false, remote.general?.notifyToCommenter ?? false, (a, b) => a === b);
|
|
13824
|
-
const reminderTimezone = computeSingleThreeWayMerge(base.reminder?.timezone ??
|
|
13882
|
+
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;
|
|
13825
13883
|
const entries = [
|
|
13826
13884
|
...general.entries,
|
|
13827
13885
|
...perRecord.entries,
|
|
@@ -13832,12 +13890,12 @@ function computeNotificationThreeWayMerge(base, local, remote) {
|
|
|
13832
13890
|
...perRecord.conflicts.map((e) => ({ key: e.key })),
|
|
13833
13891
|
...reminder.conflicts.map((e) => ({ key: e.key })),
|
|
13834
13892
|
...scalarConflictKey(generalScalar, GENERAL_SCALAR_KEY),
|
|
13835
|
-
...scalarConflictKey(reminderTimezone, REMINDER_TIMEZONE_KEY)
|
|
13893
|
+
...reminderTimezone !== void 0 ? scalarConflictKey(reminderTimezone, REMINDER_TIMEZONE_KEY) : []
|
|
13836
13894
|
];
|
|
13837
13895
|
return {
|
|
13838
13896
|
entries,
|
|
13839
13897
|
generalScalar,
|
|
13840
|
-
reminderTimezone,
|
|
13898
|
+
...reminderTimezone !== void 0 ? { reminderTimezone } : {},
|
|
13841
13899
|
conflicts,
|
|
13842
13900
|
hasConflict: conflicts.length > 0
|
|
13843
13901
|
};
|
|
@@ -13887,7 +13945,7 @@ function collectReminderNotifications(entries, resolution) {
|
|
|
13887
13945
|
*/
|
|
13888
13946
|
function resolveNotificationMerge(merge, resolution) {
|
|
13889
13947
|
const notifyToCommenter = resolveSingleMerge(merge.generalScalar, resolution.get(GENERAL_SCALAR_KEY), NotificationErrorCode.NtUnresolvedConflict);
|
|
13890
|
-
const timezone = resolveSingleMerge(merge.reminderTimezone, resolution.get(REMINDER_TIMEZONE_KEY), NotificationErrorCode.NtUnresolvedConflict);
|
|
13948
|
+
const timezone = merge.reminderTimezone !== void 0 ? resolveSingleMerge(merge.reminderTimezone, resolution.get(REMINDER_TIMEZONE_KEY), NotificationErrorCode.NtUnresolvedConflict) : null;
|
|
13891
13949
|
return {
|
|
13892
13950
|
general: {
|
|
13893
13951
|
notifyToCommenter,
|
|
@@ -13895,7 +13953,7 @@ function resolveNotificationMerge(merge, resolution) {
|
|
|
13895
13953
|
},
|
|
13896
13954
|
perRecord: collectPerRecordNotifications(merge.entries, resolution),
|
|
13897
13955
|
reminder: {
|
|
13898
|
-
timezone,
|
|
13956
|
+
...timezone !== null ? { timezone } : {},
|
|
13899
13957
|
notifications: collectReminderNotifications(merge.entries, resolution)
|
|
13900
13958
|
}
|
|
13901
13959
|
};
|
|
@@ -13914,37 +13972,6 @@ const NotificationStateParser = { parse: (parsed) => ({ config: NotificationConf
|
|
|
13914
13972
|
//#endregion
|
|
13915
13973
|
//#region src/core/application/notification/loadNotificationThreeWayInputs.ts
|
|
13916
13974
|
/**
|
|
13917
|
-
* Fetches the three remote sub-configs (general / perRecord / reminder) and
|
|
13918
|
-
* bundles them into a single {@link NotificationRemote}.
|
|
13919
|
-
*
|
|
13920
|
-
* The three getters each carry the app (preview) revision; since the revision is
|
|
13921
|
-
* app-scoped and the three are fetched at (essentially) the same moment, the
|
|
13922
|
-
* general getter's revision is used as the observed remote revision for the
|
|
13923
|
-
* TOCTOU guard. kintone always returns all three sections, so the bundled config
|
|
13924
|
-
* always has `general` / `perRecord` / `reminder` defined.
|
|
13925
|
-
*/
|
|
13926
|
-
async function loadNotificationRemote(container) {
|
|
13927
|
-
const [general, perRecord, reminder] = await Promise.all([
|
|
13928
|
-
container.notificationConfigurator.getGeneralNotifications(),
|
|
13929
|
-
container.notificationConfigurator.getPerRecordNotifications(),
|
|
13930
|
-
container.notificationConfigurator.getReminderNotifications()
|
|
13931
|
-
]);
|
|
13932
|
-
return {
|
|
13933
|
-
config: {
|
|
13934
|
-
general: {
|
|
13935
|
-
notifyToCommenter: general.notifyToCommenter,
|
|
13936
|
-
notifications: general.notifications
|
|
13937
|
-
},
|
|
13938
|
-
perRecord: perRecord.notifications,
|
|
13939
|
-
reminder: {
|
|
13940
|
-
timezone: reminder.timezone,
|
|
13941
|
-
notifications: reminder.notifications
|
|
13942
|
-
}
|
|
13943
|
-
},
|
|
13944
|
-
revision: general.revision
|
|
13945
|
-
};
|
|
13946
|
-
}
|
|
13947
|
-
/**
|
|
13948
13975
|
* Loads the four inputs of a 3-way notification sync (base snapshot, base app
|
|
13949
13976
|
* revision, local YAML, remote config + revision) via the generic
|
|
13950
13977
|
* {@link loadThreeWayInputs}. Notification bundles three sub-configs into one
|
|
@@ -14047,7 +14074,7 @@ function buildThreeWay(merge) {
|
|
|
14047
14074
|
}
|
|
14048
14075
|
}
|
|
14049
14076
|
pushScalar(merge.generalScalar, "general:notifyToCommenter", "notifyToCommenter", localChanges, remoteDrift, conflicts, extras);
|
|
14050
|
-
pushScalar(merge.reminderTimezone, "reminder:timezone", "timezone", localChanges, remoteDrift, conflicts, extras);
|
|
14077
|
+
if (merge.reminderTimezone !== void 0) pushScalar(merge.reminderTimezone, "reminder:timezone", "timezone", localChanges, remoteDrift, conflicts, extras);
|
|
14051
14078
|
return {
|
|
14052
14079
|
mode: "three-way",
|
|
14053
14080
|
localChanges,
|
|
@@ -14270,7 +14297,7 @@ async function pushNotification({ container, input }) {
|
|
|
14270
14297
|
return false;
|
|
14271
14298
|
};
|
|
14272
14299
|
const isDriftKind = (kind) => kind === "remoteOnly" || kind === "conflict";
|
|
14273
|
-
if (merge.entries.some((e) => isManaged(e.key) && isDriftKind(e.change.kind)) || local.general !== void 0 && isDriftKind(merge.generalScalar.change.kind) ||
|
|
14300
|
+
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);
|
|
14274
14301
|
}
|
|
14275
14302
|
const sendRevision = !(input.force || firstTime);
|
|
14276
14303
|
let revision = remote.revision;
|
|
@@ -14296,11 +14323,14 @@ async function pushNotification({ container, input }) {
|
|
|
14296
14323
|
}
|
|
14297
14324
|
if (local.reminder !== void 0) {
|
|
14298
14325
|
revision = (await container.notificationConfigurator.updateReminderNotifications({
|
|
14299
|
-
|
|
14300
|
-
notifications: local.reminder.notifications,
|
|
14326
|
+
...local.reminder,
|
|
14301
14327
|
...sendRevision ? { revision } : {}
|
|
14302
14328
|
})).revision;
|
|
14303
|
-
|
|
14329
|
+
const timezone = local.reminder.timezone ?? remote.config.reminder?.timezone;
|
|
14330
|
+
newBase.reminder = {
|
|
14331
|
+
...timezone !== void 0 ? { timezone } : {},
|
|
14332
|
+
notifications: local.reminder.notifications
|
|
14333
|
+
};
|
|
14304
14334
|
}
|
|
14305
14335
|
await saveNotificationSnapshotAndRevision(container, newBase, revision);
|
|
14306
14336
|
return {
|