@tenonhq/dovetail-core 0.0.98 → 0.0.99
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 +15 -3
- package/dist/tests/stampMetadataContent.test.js +62 -0
- package/package.json +1 -1
- package/release-manifest.json +53 -1
package/dist/appUtils.js
CHANGED
|
@@ -36,7 +36,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
36
36
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
37
|
};
|
|
38
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
-
exports.createAndAssignUpdateSet = exports.swapScope = exports.buildFiles = exports.summarizeRecord = exports.pushFiles = exports.getAppFileList = exports.groupAppFiles = exports.processMissingFiles = exports.refreshAllFiles = exports.findMissingFiles = exports.syncManifest = exports.processManifest = exports.normalizeManifestKeys = exports.toSafeFolderName = void 0;
|
|
39
|
+
exports.createAndAssignUpdateSet = exports.swapScope = exports.buildFiles = exports.summarizeRecord = exports.pushFiles = exports.getAppFileList = exports.groupAppFiles = exports.processMissingFiles = exports.refreshAllFiles = exports.findMissingFiles = exports.syncManifest = exports.processManifest = exports.normalizeManifestKeys = exports.toSafeFolderName = exports.stampMetadataContent = void 0;
|
|
40
40
|
const path_1 = __importDefault(require("path"));
|
|
41
41
|
const fs_1 = __importDefault(require("fs"));
|
|
42
42
|
const progress_1 = __importDefault(require("progress"));
|
|
@@ -62,6 +62,14 @@ const getUpdateSetConfig = () => {
|
|
|
62
62
|
}
|
|
63
63
|
return {};
|
|
64
64
|
};
|
|
65
|
+
// Strip the ServiceNow instance host from an absolute _record_link, leaving the
|
|
66
|
+
// instance-relative path. The CADSO metadata endpoint returns an absolute URL
|
|
67
|
+
// (https://<instance>.service-now.com/<table>.do?sys_id=...); persisting that
|
|
68
|
+
// makes the on-disk artifact instance-bound, so a re-pull from a different
|
|
69
|
+
// instance rewrites every metaData.json with the new host. The relative path
|
|
70
|
+
// (/<table>.do?sys_id=...) resolves against whatever instance the reader is on.
|
|
71
|
+
// Matches any *.service-now.com host so the output is portable across instances.
|
|
72
|
+
const stripRecordLinkHost = (link) => link.replace(/^https?:\/\/[^/]+\.service-now\.com/i, "");
|
|
65
73
|
// Merge _lastUpdatedOn into the server-provided metadata content. Preserves all
|
|
66
74
|
// record fields (sys_id, sys_scope, field value/display_value pairs, etc.) so
|
|
67
75
|
// the local metaData.json is a full snapshot of the record, not just a stub.
|
|
@@ -80,6 +88,9 @@ const stampMetadataContent = (file) => {
|
|
|
80
88
|
else {
|
|
81
89
|
metadata._lastUpdatedOn = stamp;
|
|
82
90
|
}
|
|
91
|
+
if (typeof metadata._record_link === "string") {
|
|
92
|
+
metadata._record_link = stripRecordLinkHost(metadata._record_link);
|
|
93
|
+
}
|
|
83
94
|
return { ...file, content: JSON.stringify(metadata, null, 2) };
|
|
84
95
|
}
|
|
85
96
|
catch (e) {
|
|
@@ -87,6 +98,7 @@ const stampMetadataContent = (file) => {
|
|
|
87
98
|
return file;
|
|
88
99
|
}
|
|
89
100
|
};
|
|
101
|
+
exports.stampMetadataContent = stampMetadataContent;
|
|
90
102
|
const hasServerMetadata = (files) => files.some((f) => f.name === "metaData" && f.type === "json" && !!f.content);
|
|
91
103
|
const processFilesInManRec = async (recPath, rec, forceWrite) => {
|
|
92
104
|
FileLogger_1.fileLogger.debug("Processing record: " + rec.name + " (" + rec.files.length + " files)");
|
|
@@ -101,7 +113,7 @@ const processFilesInManRec = async (recPath, rec, forceWrite) => {
|
|
|
101
113
|
};
|
|
102
114
|
await fileWrite(stubMetadata, recPath);
|
|
103
115
|
}
|
|
104
|
-
const writeResults = await (0, genericUtils_1.allSettledBatched)(rec.files, constants_1.CONCURRENCY_FILES, function (file) { return fileWrite(stampMetadataContent(file), recPath); });
|
|
116
|
+
const writeResults = await (0, genericUtils_1.allSettledBatched)(rec.files, constants_1.CONCURRENCY_FILES, function (file) { return fileWrite((0, exports.stampMetadataContent)(file), recPath); });
|
|
105
117
|
const writeFailures = writeResults.filter((r) => r.status === "rejected");
|
|
106
118
|
if (writeFailures.length > 0) {
|
|
107
119
|
writeFailures.forEach((f) => {
|
|
@@ -572,7 +584,7 @@ const refreshAllFiles = async (newManifest, sourcePath, options = {}) => {
|
|
|
572
584
|
if (anyChanged || forceWrite) {
|
|
573
585
|
let metadataFile;
|
|
574
586
|
if (metadataFiles.length > 0 && metadataFiles[0].content) {
|
|
575
|
-
metadataFile = stampMetadataContent(metadataFiles[0]);
|
|
587
|
+
metadataFile = (0, exports.stampMetadataContent)(metadataFiles[0]);
|
|
576
588
|
}
|
|
577
589
|
else {
|
|
578
590
|
metadataFile = {
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Tests for stampMetadataContent — the writer that finalizes each record's
|
|
3
|
+
// metaData.json before it lands on disk.
|
|
4
|
+
//
|
|
5
|
+
// Two on-disk-stability concerns are covered:
|
|
6
|
+
// 1. _lastUpdatedOn is sourced from the record's sys_updated_on (stable across
|
|
7
|
+
// re-pulls) rather than a fresh wall-clock stamp.
|
|
8
|
+
// 2. _record_link is rewritten to an instance-relative path. The CADSO metadata
|
|
9
|
+
// endpoint returns an absolute URL (https://<instance>.service-now.com/...);
|
|
10
|
+
// persisting the host makes the artifact instance-bound and dirties every
|
|
11
|
+
// file on a re-pull from a different instance. Stripping any *.service-now.com
|
|
12
|
+
// host keeps the diff to genuine schema changes.
|
|
13
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
|
+
const appUtils_1 = require("../appUtils");
|
|
15
|
+
const metaFile = (content) => ({
|
|
16
|
+
name: "metaData",
|
|
17
|
+
type: "json",
|
|
18
|
+
content: JSON.stringify(content),
|
|
19
|
+
});
|
|
20
|
+
const parse = (file) => JSON.parse(file.content);
|
|
21
|
+
describe("stampMetadataContent — _record_link host stripping", () => {
|
|
22
|
+
it("strips the configured workstudio host, leaving the relative path", () => {
|
|
23
|
+
const out = (0, appUtils_1.stampMetadataContent)(metaFile({
|
|
24
|
+
sys_updated_on: { value: "2025-08-21 17:09:02" },
|
|
25
|
+
_record_link: "https://tenonworkstudio.service-now.com/sys_hub_action_input.do?sys_id=85de623c33ef2a107b18bc534d5c7b92",
|
|
26
|
+
}));
|
|
27
|
+
expect(parse(out)._record_link).toBe("/sys_hub_action_input.do?sys_id=85de623c33ef2a107b18bc534d5c7b92");
|
|
28
|
+
});
|
|
29
|
+
it("strips any *.service-now.com host (instance-portable)", () => {
|
|
30
|
+
for (const host of ["tenonworkshop", "tenon", "tenonworkbench"]) {
|
|
31
|
+
const out = (0, appUtils_1.stampMetadataContent)(metaFile({
|
|
32
|
+
_record_link: `https://${host}.service-now.com/x_cadso_core_setting.do?sys_id=abc`,
|
|
33
|
+
}));
|
|
34
|
+
expect(parse(out)._record_link).toBe("/x_cadso_core_setting.do?sys_id=abc");
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
it("leaves an already-relative _record_link untouched (idempotent)", () => {
|
|
38
|
+
const out = (0, appUtils_1.stampMetadataContent)(metaFile({ _record_link: "/sys_hub_action_input.do?sys_id=abc" }));
|
|
39
|
+
expect(parse(out)._record_link).toBe("/sys_hub_action_input.do?sys_id=abc");
|
|
40
|
+
});
|
|
41
|
+
it("leaves records without a _record_link unchanged", () => {
|
|
42
|
+
const out = (0, appUtils_1.stampMetadataContent)(metaFile({ sys_updated_on: { value: "2025-08-21 17:09:02" } }));
|
|
43
|
+
expect(parse(out)._record_link).toBeUndefined();
|
|
44
|
+
});
|
|
45
|
+
it("ignores a non-string _record_link rather than throwing", () => {
|
|
46
|
+
const out = (0, appUtils_1.stampMetadataContent)(metaFile({ _record_link: null }));
|
|
47
|
+
expect(parse(out)._record_link).toBeNull();
|
|
48
|
+
});
|
|
49
|
+
it("still sources _lastUpdatedOn from sys_updated_on while stripping the host", () => {
|
|
50
|
+
const out = (0, appUtils_1.stampMetadataContent)(metaFile({
|
|
51
|
+
sys_updated_on: { value: "2025-08-21 17:09:02" },
|
|
52
|
+
_record_link: "https://tenonworkstudio.service-now.com/incident.do?sys_id=1",
|
|
53
|
+
}));
|
|
54
|
+
const parsed = parse(out);
|
|
55
|
+
expect(parsed._lastUpdatedOn).toBe("2025-08-21 17:09:02");
|
|
56
|
+
expect(parsed._record_link).toBe("/incident.do?sys_id=1");
|
|
57
|
+
});
|
|
58
|
+
it("passes through non-metaData files verbatim", () => {
|
|
59
|
+
const script = { name: "script", type: "js", content: "var x = 1;" };
|
|
60
|
+
expect((0, appUtils_1.stampMetadataContent)(script)).toEqual(script);
|
|
61
|
+
});
|
|
62
|
+
});
|
package/package.json
CHANGED
package/release-manifest.json
CHANGED
|
@@ -1,7 +1,59 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "dovetail-manifest/v1",
|
|
3
|
-
"generated_at": "2026-06-
|
|
3
|
+
"generated_at": "2026-06-02T17:01:03.752Z",
|
|
4
4
|
"events": [
|
|
5
|
+
{
|
|
6
|
+
"$schema": "dovetail-release-event/v1",
|
|
7
|
+
"event_id": "dovetail-servicenow@0.0.13",
|
|
8
|
+
"package": "@tenonhq/dovetail-servicenow",
|
|
9
|
+
"version": "0.0.13",
|
|
10
|
+
"prev_version": "0.0.12",
|
|
11
|
+
"published_at": "2026-06-02T17:01:03.745Z",
|
|
12
|
+
"git": {
|
|
13
|
+
"sha": "db8fddb963948f013aacfc6f67b26773234ef39d",
|
|
14
|
+
"tag": "dovetail-servicenow@0.0.13"
|
|
15
|
+
},
|
|
16
|
+
"semver_bump": "patch",
|
|
17
|
+
"commits": [
|
|
18
|
+
{
|
|
19
|
+
"sha": "db8fddb96",
|
|
20
|
+
"subject": "feat(servicenow): create-flow — mint + publish a whole flow from scratch (#138)",
|
|
21
|
+
"pr": 138
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"sha": "bd127c726",
|
|
25
|
+
"subject": "chore(release): @tenonhq/dovetail-servicenow@0.0.12 [skip ci]"
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"sha": "448696846",
|
|
29
|
+
"subject": "chore(release): publish 3 packages [skip ci]"
|
|
30
|
+
}
|
|
31
|
+
],
|
|
32
|
+
"exports_delta": null,
|
|
33
|
+
"narrative": null
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
"$schema": "dovetail-release-event/v1",
|
|
37
|
+
"event_id": "dovetail-servicenow@0.0.12",
|
|
38
|
+
"package": "@tenonhq/dovetail-servicenow",
|
|
39
|
+
"version": "0.0.12",
|
|
40
|
+
"prev_version": "0.0.11",
|
|
41
|
+
"published_at": "2026-06-02T16:06:43.535Z",
|
|
42
|
+
"git": {
|
|
43
|
+
"sha": "73ad931dbda5cdf85d4bc91307a17eb4d08de3d2",
|
|
44
|
+
"tag": "dovetail-servicenow@0.0.12"
|
|
45
|
+
},
|
|
46
|
+
"semver_bump": "patch",
|
|
47
|
+
"commits": [
|
|
48
|
+
{
|
|
49
|
+
"sha": "73ad931db",
|
|
50
|
+
"subject": "docs: point SN env-var references at committed .env.example (#137)",
|
|
51
|
+
"pr": 137
|
|
52
|
+
}
|
|
53
|
+
],
|
|
54
|
+
"exports_delta": null,
|
|
55
|
+
"narrative": null
|
|
56
|
+
},
|
|
5
57
|
{
|
|
6
58
|
"$schema": "dovetail-release-event/v1",
|
|
7
59
|
"event_id": "dovetail-schema@0.0.10",
|