@tenonhq/dovetail-core 0.0.108 → 0.0.109
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/appUtils.js
CHANGED
|
@@ -60,6 +60,27 @@ const getUpdateSetConfig = () => (0, updateSetConfig_1.readUpdateSetConfig)();
|
|
|
60
60
|
// (/<table>.do?sys_id=...) resolves against whatever instance the reader is on.
|
|
61
61
|
// Matches any *.service-now.com host so the output is portable across instances.
|
|
62
62
|
const stripRecordLinkHost = (link) => link.replace(/^https?:\/\/[^/]+\.service-now\.com/i, "");
|
|
63
|
+
// Strip the derived `display_value` from every field pair, keeping only `value`.
|
|
64
|
+
// A ServiceNow field is serialized as a { value, display_value } pair. The
|
|
65
|
+
// display_value is the *current* display name of the field's value — for a
|
|
66
|
+
// reference field, the referenced record's name, resolved live on the server at
|
|
67
|
+
// pull time. That makes it non-deterministic on disk: rename a referenced record
|
|
68
|
+
// on the instance and every metaData.json pointing at it re-churns its
|
|
69
|
+
// display_value on the next pull, even though the pointing record is unchanged.
|
|
70
|
+
// `value` (the sys_id / raw value) is the stable identity; display_value is a
|
|
71
|
+
// convenience the mirror does not need. Reconcile already ignores metaData.json
|
|
72
|
+
// content and pushes key off `value` / the manifest, so no consumer depends on
|
|
73
|
+
// the persisted display_value. Dropping it keeps re-pulls byte-identical.
|
|
74
|
+
const stripFieldDisplayValues = (metadata) => {
|
|
75
|
+
for (const key of Object.keys(metadata)) {
|
|
76
|
+
const field = metadata[key];
|
|
77
|
+
if (field !== null &&
|
|
78
|
+
typeof field === "object" &&
|
|
79
|
+
"display_value" in field) {
|
|
80
|
+
delete field.display_value;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
};
|
|
63
84
|
// Merge _lastUpdatedOn into the server-provided metadata content. Preserves all
|
|
64
85
|
// record fields (sys_id, sys_scope, field value/display_value pairs, etc.) so
|
|
65
86
|
// the local metaData.json is a full snapshot of the record, not just a stub.
|
|
@@ -81,6 +102,7 @@ const stampMetadataContent = (file) => {
|
|
|
81
102
|
if (typeof metadata._record_link === "string") {
|
|
82
103
|
metadata._record_link = stripRecordLinkHost(metadata._record_link);
|
|
83
104
|
}
|
|
105
|
+
stripFieldDisplayValues(metadata);
|
|
84
106
|
return { ...file, content: JSON.stringify(metadata, null, 2) };
|
|
85
107
|
}
|
|
86
108
|
catch (e) {
|
|
@@ -60,3 +60,49 @@ describe("stampMetadataContent — _record_link host stripping", () => {
|
|
|
60
60
|
expect((0, appUtils_1.stampMetadataContent)(script)).toEqual(script);
|
|
61
61
|
});
|
|
62
62
|
});
|
|
63
|
+
describe("stampMetadataContent — display_value stripping", () => {
|
|
64
|
+
it("drops display_value from field pairs, keeping value", () => {
|
|
65
|
+
const out = (0, appUtils_1.stampMetadataContent)(metaFile({
|
|
66
|
+
sys_updated_on: { value: "2025-08-21 17:09:02" },
|
|
67
|
+
action: {
|
|
68
|
+
value: "85de623c33ef2a107b18bc534d5c7b92",
|
|
69
|
+
display_value: "Parse hashes for to_addresses",
|
|
70
|
+
},
|
|
71
|
+
}));
|
|
72
|
+
const parsed = parse(out);
|
|
73
|
+
expect(parsed.action).toEqual({
|
|
74
|
+
value: "85de623c33ef2a107b18bc534d5c7b92",
|
|
75
|
+
});
|
|
76
|
+
expect(parsed.action.display_value).toBeUndefined();
|
|
77
|
+
});
|
|
78
|
+
it("strips every field's display_value in one pass", () => {
|
|
79
|
+
const out = (0, appUtils_1.stampMetadataContent)(metaFile({
|
|
80
|
+
a: { value: "1", display_value: "One" },
|
|
81
|
+
b: { value: "2", display_value: "Two" },
|
|
82
|
+
}));
|
|
83
|
+
const parsed = parse(out);
|
|
84
|
+
expect(parsed.a).toEqual({ value: "1" });
|
|
85
|
+
expect(parsed.b).toEqual({ value: "2" });
|
|
86
|
+
});
|
|
87
|
+
it("leaves value-only fields and string keys untouched", () => {
|
|
88
|
+
const out = (0, appUtils_1.stampMetadataContent)(metaFile({
|
|
89
|
+
sys_updated_on: { value: "2025-08-21 17:09:02" },
|
|
90
|
+
_record_link: "/incident.do?sys_id=1",
|
|
91
|
+
name: { value: "plain" },
|
|
92
|
+
}));
|
|
93
|
+
const parsed = parse(out);
|
|
94
|
+
expect(parsed.sys_updated_on).toEqual({ value: "2025-08-21 17:09:02" });
|
|
95
|
+
expect(parsed.name).toEqual({ value: "plain" });
|
|
96
|
+
expect(parsed._record_link).toBe("/incident.do?sys_id=1");
|
|
97
|
+
});
|
|
98
|
+
it("is idempotent — a display-value-free record stays byte-identical", () => {
|
|
99
|
+
const clean = {
|
|
100
|
+
sys_updated_on: { value: "2025-08-21 17:09:02" },
|
|
101
|
+
action: { value: "abc" },
|
|
102
|
+
_record_link: "/incident.do?sys_id=1",
|
|
103
|
+
};
|
|
104
|
+
const first = (0, appUtils_1.stampMetadataContent)(metaFile(clean));
|
|
105
|
+
const second = (0, appUtils_1.stampMetadataContent)(first);
|
|
106
|
+
expect(second.content).toBe(first.content);
|
|
107
|
+
});
|
|
108
|
+
});
|