@objectstack/plugin-audit 9.6.0 → 9.8.0
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.js +113 -3
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +114 -4
- package/dist/index.mjs.map +1 -1
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -1209,6 +1209,34 @@ var SysActivity = import_data2.ObjectSchema.create({
|
|
|
1209
1209
|
description: "Display label of the target record at write time",
|
|
1210
1210
|
group: "Target"
|
|
1211
1211
|
}),
|
|
1212
|
+
// ── Source pointer (ADR-0052 §5 — ActivityPointer model) ─────────
|
|
1213
|
+
// `object_name`/`record_id` say WHICH record this activity belongs to (the
|
|
1214
|
+
// "regarding" record, e.g. the contact). `source_object`/`source_id` point
|
|
1215
|
+
// to the RICH ENTITY this activity was derived from — the email row in
|
|
1216
|
+
// `sys_email`, the call/meeting in a task object, the `sys_comment` — so the
|
|
1217
|
+
// timeline can drill from a one-line summary to the full record. This is the
|
|
1218
|
+
// queryable, structured equivalent of cramming an id into `metadata`
|
|
1219
|
+
// (cf. Dataverse ActivityPointer → Email/PhoneCall/Appointment subtypes,
|
|
1220
|
+
// Salesforce ActivityTimeline → EmailMessage/Task/Event). Optional: most
|
|
1221
|
+
// CRUD activities have no distinct source (the record IS the source).
|
|
1222
|
+
source_object: import_data2.Field.text({
|
|
1223
|
+
label: "Source Object",
|
|
1224
|
+
required: false,
|
|
1225
|
+
readonly: true,
|
|
1226
|
+
searchable: true,
|
|
1227
|
+
maxLength: 255,
|
|
1228
|
+
description: 'Object name of the rich source entity this activity was derived from (e.g. "sys_email"). Null when the activity is about the target record itself.',
|
|
1229
|
+
group: "Target"
|
|
1230
|
+
}),
|
|
1231
|
+
source_id: import_data2.Field.text({
|
|
1232
|
+
label: "Source ID",
|
|
1233
|
+
required: false,
|
|
1234
|
+
readonly: true,
|
|
1235
|
+
searchable: true,
|
|
1236
|
+
maxLength: 255,
|
|
1237
|
+
description: "Record id of the rich source entity (paired with source_object) \u2014 lets the timeline drill to the full email/call/meeting record.",
|
|
1238
|
+
group: "Target"
|
|
1239
|
+
}),
|
|
1212
1240
|
url: import_data2.Field.url({
|
|
1213
1241
|
label: "URL",
|
|
1214
1242
|
required: false,
|
|
@@ -1437,6 +1465,50 @@ function safeStringify(v) {
|
|
|
1437
1465
|
return String(v);
|
|
1438
1466
|
}
|
|
1439
1467
|
}
|
|
1468
|
+
function displayFieldValue(field, value) {
|
|
1469
|
+
if (value === null || value === void 0 || value === "") return "\u2205";
|
|
1470
|
+
const options = field?.options;
|
|
1471
|
+
if (Array.isArray(options)) {
|
|
1472
|
+
for (const o of options) {
|
|
1473
|
+
const ov = o && typeof o === "object" ? o.value ?? o.name ?? o.label : o;
|
|
1474
|
+
if (ov === value) {
|
|
1475
|
+
const ol = o && typeof o === "object" ? o.label ?? o.name ?? ov : o;
|
|
1476
|
+
return String(ol);
|
|
1477
|
+
}
|
|
1478
|
+
}
|
|
1479
|
+
}
|
|
1480
|
+
return String(value);
|
|
1481
|
+
}
|
|
1482
|
+
function renderTrackedChangeSummary(fields, oldVals, newVals) {
|
|
1483
|
+
if (!fields || !newVals) return null;
|
|
1484
|
+
const parts = [];
|
|
1485
|
+
for (const key of Object.keys(newVals)) {
|
|
1486
|
+
const field = fields[key];
|
|
1487
|
+
if (!field || field.trackHistory !== true) continue;
|
|
1488
|
+
const label = typeof field.label === "string" && field.label || key;
|
|
1489
|
+
const from = displayFieldValue(field, oldVals ? oldVals[key] : void 0);
|
|
1490
|
+
const to = displayFieldValue(field, newVals[key]);
|
|
1491
|
+
parts.push(`${label}: ${from} \u2192 ${to}`);
|
|
1492
|
+
}
|
|
1493
|
+
return parts.length > 0 ? parts.join("; ") : null;
|
|
1494
|
+
}
|
|
1495
|
+
function matchMilestone(objectDef, fields, before, after) {
|
|
1496
|
+
const milestones = objectDef && Array.isArray(objectDef.activityMilestones) ? objectDef.activityMilestones : null;
|
|
1497
|
+
if (!milestones || !after || !before) return null;
|
|
1498
|
+
for (const m of milestones) {
|
|
1499
|
+
if (!m || typeof m.field !== "string" || typeof m.summary !== "string") continue;
|
|
1500
|
+
if (after[m.field] === m.value && before[m.field] !== m.value) {
|
|
1501
|
+
const summary = m.summary.replace(/\{(\w+)\}/g, (_match, key) => {
|
|
1502
|
+
const v = after[key];
|
|
1503
|
+
if (v === null || v === void 0 || v === "") return "";
|
|
1504
|
+
const field = fields ? fields[key] : void 0;
|
|
1505
|
+
return field ? displayFieldValue(field, v) : String(v);
|
|
1506
|
+
});
|
|
1507
|
+
return { summary, type: typeof m.type === "string" ? m.type : void 0 };
|
|
1508
|
+
}
|
|
1509
|
+
}
|
|
1510
|
+
return null;
|
|
1511
|
+
}
|
|
1440
1512
|
function installAuditWriters(engine, packageId = "com.objectstack.audit", opts = {}) {
|
|
1441
1513
|
if (!engine || typeof engine.registerHook !== "function") return;
|
|
1442
1514
|
const getMessaging = opts.getMessaging ?? (() => void 0);
|
|
@@ -1462,6 +1534,30 @@ function installAuditWriters(engine, packageId = "com.objectstack.audit", opts =
|
|
|
1462
1534
|
}
|
|
1463
1535
|
return set != null && set.has(field);
|
|
1464
1536
|
};
|
|
1537
|
+
const fieldDefsCache = /* @__PURE__ */ new Map();
|
|
1538
|
+
const getFieldDefs = (objectName) => {
|
|
1539
|
+
if (fieldDefsCache.has(objectName)) return fieldDefsCache.get(objectName) ?? null;
|
|
1540
|
+
let defs = null;
|
|
1541
|
+
try {
|
|
1542
|
+
const schema = typeof engine.getSchema === "function" ? engine.getSchema(objectName) : null;
|
|
1543
|
+
const fields = schema?.fields;
|
|
1544
|
+
if (fields && typeof fields === "object" && !Array.isArray(fields)) defs = fields;
|
|
1545
|
+
} catch {
|
|
1546
|
+
}
|
|
1547
|
+
fieldDefsCache.set(objectName, defs);
|
|
1548
|
+
return defs;
|
|
1549
|
+
};
|
|
1550
|
+
const objectDefCache = /* @__PURE__ */ new Map();
|
|
1551
|
+
const getObjectDef = (objectName) => {
|
|
1552
|
+
if (objectDefCache.has(objectName)) return objectDefCache.get(objectName);
|
|
1553
|
+
let def = null;
|
|
1554
|
+
try {
|
|
1555
|
+
def = typeof engine.getSchema === "function" ? engine.getSchema(objectName) : null;
|
|
1556
|
+
} catch {
|
|
1557
|
+
}
|
|
1558
|
+
objectDefCache.set(objectName, def);
|
|
1559
|
+
return def;
|
|
1560
|
+
};
|
|
1465
1561
|
const captureBefore = async (ctx) => {
|
|
1466
1562
|
if (SKIP_OBJECTS.has(ctx.object)) return;
|
|
1467
1563
|
const id = ctx.input?.id;
|
|
@@ -1526,9 +1622,23 @@ function installAuditWriters(engine, packageId = "com.objectstack.audit", opts =
|
|
|
1526
1622
|
auditRow.organization_id = tenantId ?? null;
|
|
1527
1623
|
}
|
|
1528
1624
|
const label = recordLabel(after ?? before, recordId ?? "");
|
|
1529
|
-
|
|
1625
|
+
let summary;
|
|
1626
|
+
let activityType = activityTypeFor(action);
|
|
1627
|
+
if (action === "create") {
|
|
1628
|
+
summary = `Created ${ctx.object} "${label}"`;
|
|
1629
|
+
} else if (action === "delete") {
|
|
1630
|
+
summary = `Deleted ${ctx.object} "${label}"`;
|
|
1631
|
+
} else {
|
|
1632
|
+
const milestone = matchMilestone(getObjectDef(ctx.object), getFieldDefs(ctx.object), before, after);
|
|
1633
|
+
if (milestone) {
|
|
1634
|
+
summary = milestone.summary;
|
|
1635
|
+
if (milestone.type) activityType = milestone.type;
|
|
1636
|
+
} else {
|
|
1637
|
+
summary = renderTrackedChangeSummary(getFieldDefs(ctx.object), oldValue, newValue) ?? `Updated ${ctx.object} "${label}"`;
|
|
1638
|
+
}
|
|
1639
|
+
}
|
|
1530
1640
|
const activityRow = {
|
|
1531
|
-
type:
|
|
1641
|
+
type: activityType,
|
|
1532
1642
|
// Explicit ISO timestamp — `defaultValue: 'NOW()'` on the column
|
|
1533
1643
|
// isn't resolved by every driver and would otherwise leak the
|
|
1534
1644
|
// literal string "NOW()" into the row.
|
|
@@ -1673,7 +1783,7 @@ var AuditPlugin = class {
|
|
|
1673
1783
|
scope: "system",
|
|
1674
1784
|
defaultDatasource: "cloud",
|
|
1675
1785
|
namespace: "sys",
|
|
1676
|
-
objects: [SysAuditLog, SysActivity, SysComment, import_audit.
|
|
1786
|
+
objects: [SysAuditLog, SysActivity, SysComment, import_audit.SysNotification],
|
|
1677
1787
|
// ADR-0029 D7 — contribute the Audit Logs entry into the Setup app's
|
|
1678
1788
|
// `group_diagnostics` slot. The plugin owns sys_audit_log (K2).
|
|
1679
1789
|
navigationContributions: [
|