@tenonhq/dovetail-core 0.0.85
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/README.md +6 -0
- package/dist/FileLogger.js +207 -0
- package/dist/FileUtils.js +387 -0
- package/dist/Logger.js +88 -0
- package/dist/MultiScopeWatcher.js +615 -0
- package/dist/PluginManager.js +125 -0
- package/dist/_defaultOptionsOLD.js +44 -0
- package/dist/allScopesCommands.js +418 -0
- package/dist/appUtils.js +929 -0
- package/dist/benchmark.js +104 -0
- package/dist/bootstrap.js +62 -0
- package/dist/claudeCommand.js +60 -0
- package/dist/clickupCommands.js +472 -0
- package/dist/clickupPushHelper.js +33 -0
- package/dist/commander.js +466 -0
- package/dist/commands.js +378 -0
- package/dist/config.js +697 -0
- package/dist/constants.js +15 -0
- package/dist/createRecordCommand.js +314 -0
- package/dist/dashboardCommand.js +38 -0
- package/dist/defaultOptions.js +14 -0
- package/dist/deleteRecordCommand.js +282 -0
- package/dist/flowDesigner/values.js +55 -0
- package/dist/genericUtils.js +189 -0
- package/dist/gitUtils.js +99 -0
- package/dist/index.js +35 -0
- package/dist/initSystem/corePlugin.js +344 -0
- package/dist/initSystem/discovery.js +107 -0
- package/dist/initSystem/orchestrator.js +408 -0
- package/dist/logMessages.js +82 -0
- package/dist/loginCommand.js +20 -0
- package/dist/migrateCommand.js +123 -0
- package/dist/projectFiles.js +32 -0
- package/dist/recentEdits.js +66 -0
- package/dist/schemaCommand.js +85 -0
- package/dist/snClient.js +519 -0
- package/dist/tests/benchmarkRefresh.test.js +265 -0
- package/dist/tests/clickupCommands.test.js +384 -0
- package/dist/tests/clickupPushHelper.test.js +124 -0
- package/dist/tests/configFallback.test.js +68 -0
- package/dist/tests/cred.test.js +59 -0
- package/dist/tests/discovery.test.js +67 -0
- package/dist/tests/ensureUpdateSetWarnings.test.js +218 -0
- package/dist/tests/errorLogLevels.test.js +273 -0
- package/dist/tests/example.test.js +6 -0
- package/dist/tests/fileContextSkipReason.test.js +116 -0
- package/dist/tests/globalDebounce.test.js +308 -0
- package/dist/tests/migrateCommand.test.js +113 -0
- package/dist/tests/multi-scope-watcher.test.js +558 -0
- package/dist/tests/orchestrator.test.js +23 -0
- package/dist/tests/pushFiles.test.js +248 -0
- package/dist/tests/rateLimitCoordination.test.js +271 -0
- package/dist/tests/resolveConfigReadOnly.test.js +67 -0
- package/dist/tests/retryOnHttpErr.test.js +154 -0
- package/dist/tests/scopeCaching.test.js +115 -0
- package/dist/tests/serializeUpdateSetConfig.test.js +325 -0
- package/dist/tests/snClient.fallback.test.js +120 -0
- package/dist/tests/syncManifestRefresh.test.js +287 -0
- package/dist/tests/syncManifestWhitelist.test.js +192 -0
- package/dist/tests/taskClear.test.js +170 -0
- package/dist/tests/taskStaleness.test.js +220 -0
- package/dist/tests/unwrapSNResponseError.test.js +145 -0
- package/dist/tests/v2Values.test.js +79 -0
- package/dist/tests/validateTaskId.test.js +304 -0
- package/dist/tests/verifyUpdateSetSwitch.test.js +277 -0
- package/dist/tests/writeEnvVar.test.js +119 -0
- package/dist/updateSetCommands.js +702 -0
- package/dist/wizard.js +40 -0
- package/package.json +69 -0
- package/skills/dove-configure-pipeline.md +158 -0
- package/skills/dove-create-plugin.md +174 -0
- package/skills/dove-create-record.md +135 -0
- package/skills/dove-debug-build.md +117 -0
- package/skills/dove-delete-record.md +104 -0
- package/skills/dove-manage-tables.md +174 -0
- package/skills/dove-manage-update-sets.md +88 -0
- package/skills/dove-setup-project.md +154 -0
- package/skills/dove-troubleshoot-sync.md +113 -0
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
const ConfigManager = __importStar(require("./config"));
|
|
40
|
+
const fs_1 = __importDefault(require("fs"));
|
|
41
|
+
const path_1 = __importDefault(require("path"));
|
|
42
|
+
const fsp = fs_1.default.promises;
|
|
43
|
+
class PluginManager {
|
|
44
|
+
pluginRules;
|
|
45
|
+
constructor() {
|
|
46
|
+
this.pluginRules = [];
|
|
47
|
+
}
|
|
48
|
+
async loadPluginConfig() {
|
|
49
|
+
let conf = ConfigManager.getConfig();
|
|
50
|
+
if (conf && conf.rules) {
|
|
51
|
+
this.pluginRules = conf.rules;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
determinePlugins(context) {
|
|
55
|
+
let plugins = [];
|
|
56
|
+
for (let rule of this.pluginRules) {
|
|
57
|
+
let reg = rule.match;
|
|
58
|
+
if (reg.test(context.filePath)) {
|
|
59
|
+
plugins = rule.plugins;
|
|
60
|
+
//only match first rule
|
|
61
|
+
break;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return plugins;
|
|
65
|
+
}
|
|
66
|
+
async runPlugins(plugins, context, content) {
|
|
67
|
+
try {
|
|
68
|
+
let output = content;
|
|
69
|
+
for (let pConfig of plugins) {
|
|
70
|
+
let pluginPath = path_1.default.join(ConfigManager.getRootDir(), "node_modules", pConfig.name);
|
|
71
|
+
let plugin = await Promise.resolve(`${pluginPath}`).then(s => __importStar(require(s)));
|
|
72
|
+
let results = await plugin.run(context, output, pConfig.options);
|
|
73
|
+
if (!results.success) {
|
|
74
|
+
return {
|
|
75
|
+
success: false,
|
|
76
|
+
content: "",
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
output = results.output;
|
|
80
|
+
}
|
|
81
|
+
return {
|
|
82
|
+
success: true,
|
|
83
|
+
content: output,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
catch (e) {
|
|
87
|
+
throw e;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
async processFile(context, content) {
|
|
91
|
+
const plugins = this.determinePlugins(context);
|
|
92
|
+
if (plugins.length > 0) {
|
|
93
|
+
try {
|
|
94
|
+
const pluginResults = await this.runPlugins(plugins, context, content);
|
|
95
|
+
if (pluginResults.success) {
|
|
96
|
+
return pluginResults.content;
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
throw new Error(`Failed to build ${context.tableName}=>${context.sys_id}!`);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
catch (e) {
|
|
103
|
+
throw e;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
return content;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
async getFinalFileContents(context, processFile = true) {
|
|
111
|
+
const { filePath } = context;
|
|
112
|
+
try {
|
|
113
|
+
const contents = await fsp.readFile(filePath, "utf-8");
|
|
114
|
+
if (processFile) {
|
|
115
|
+
await this.loadPluginConfig();
|
|
116
|
+
return await this.processFile(context, contents);
|
|
117
|
+
}
|
|
118
|
+
return contents;
|
|
119
|
+
}
|
|
120
|
+
catch (e) {
|
|
121
|
+
throw e;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
exports.default = new PluginManager();
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.tableOptions = exports.scopes = exports.includes = exports.excludes = void 0;
|
|
4
|
+
let excludes = {
|
|
5
|
+
sys_scope_privilege: true,
|
|
6
|
+
sys_dictionary: true,
|
|
7
|
+
sys_impex_entry: true,
|
|
8
|
+
sys_security_acl: true,
|
|
9
|
+
sys_transform_map: true,
|
|
10
|
+
sys_ui_policy: true,
|
|
11
|
+
sys_ui_list_control: true,
|
|
12
|
+
sys_relationship: true,
|
|
13
|
+
sys_report: true,
|
|
14
|
+
item_option_new: true,
|
|
15
|
+
sys_process_flow: true,
|
|
16
|
+
content_block_programmatic: true,
|
|
17
|
+
sp_instance: true,
|
|
18
|
+
sys_transform_script: true,
|
|
19
|
+
sc_category: true,
|
|
20
|
+
sysrule_view: true,
|
|
21
|
+
sc_cat_item: true,
|
|
22
|
+
sysevent_in_email_action: true,
|
|
23
|
+
sys_navigator: true,
|
|
24
|
+
sys_transform_entry: true,
|
|
25
|
+
metric_definition: true,
|
|
26
|
+
content_block_lists: true,
|
|
27
|
+
content_block_detail: true,
|
|
28
|
+
sp_portal: true,
|
|
29
|
+
sc_cat_item_producer: true,
|
|
30
|
+
sys_impex_map: true,
|
|
31
|
+
};
|
|
32
|
+
exports.excludes = excludes;
|
|
33
|
+
let includes = {
|
|
34
|
+
content_css: {
|
|
35
|
+
style: {
|
|
36
|
+
type: "css",
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
exports.includes = includes;
|
|
41
|
+
let tableOptions = {};
|
|
42
|
+
exports.tableOptions = tableOptions;
|
|
43
|
+
let scopes = {};
|
|
44
|
+
exports.scopes = scopes;
|
|
@@ -0,0 +1,418 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.processScope = processScope;
|
|
40
|
+
exports.initScopesCommand = initScopesCommand;
|
|
41
|
+
exports.watchAllScopesCommand = watchAllScopesCommand;
|
|
42
|
+
const Logger_1 = require("./Logger");
|
|
43
|
+
const FileLogger_1 = require("./FileLogger");
|
|
44
|
+
const ConfigManager = __importStar(require("./config"));
|
|
45
|
+
const AppUtils = __importStar(require("./appUtils"));
|
|
46
|
+
const wizard_1 = require("./wizard");
|
|
47
|
+
const snClient_1 = require("./snClient");
|
|
48
|
+
const commands_1 = require("./commands");
|
|
49
|
+
const path = __importStar(require("path"));
|
|
50
|
+
const fs = __importStar(require("fs"));
|
|
51
|
+
const child_process_1 = require("child_process");
|
|
52
|
+
const progress_1 = __importDefault(require("progress"));
|
|
53
|
+
const fsp = fs.promises;
|
|
54
|
+
// Custom function to process manifest with specific source directory
|
|
55
|
+
async function processManifestForScope(manifest, sourceDirectory, forceWrite = false) {
|
|
56
|
+
try {
|
|
57
|
+
// Ensure the source directory exists
|
|
58
|
+
try {
|
|
59
|
+
await fsp.mkdir(sourceDirectory, { recursive: true });
|
|
60
|
+
}
|
|
61
|
+
catch (dirError) {
|
|
62
|
+
Logger_1.logger.error("Failed to create directory: " + sourceDirectory);
|
|
63
|
+
throw dirError;
|
|
64
|
+
}
|
|
65
|
+
const tables = manifest.tables || {};
|
|
66
|
+
const tableNames = Object.keys(tables);
|
|
67
|
+
var totalRecords = 0;
|
|
68
|
+
for (var tn = 0; tn < tableNames.length; tn++) {
|
|
69
|
+
totalRecords += Object.keys(tables[tableNames[tn]].records || {}).length;
|
|
70
|
+
}
|
|
71
|
+
var scopeLabel = sourceDirectory.split(path.sep).pop() || "scope";
|
|
72
|
+
var progBar = null;
|
|
73
|
+
if (Logger_1.logger.getLogLevel() === "info" && totalRecords > 0) {
|
|
74
|
+
progBar = new progress_1.default(":scope :bar :current/:total (:percent)", {
|
|
75
|
+
total: totalRecords,
|
|
76
|
+
width: 40,
|
|
77
|
+
complete: "=",
|
|
78
|
+
incomplete: "-",
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
for (const tableName of tableNames) {
|
|
82
|
+
const tableRecords = tables[tableName];
|
|
83
|
+
const tablePath = path.join(sourceDirectory, tableName);
|
|
84
|
+
// Process each record in the table
|
|
85
|
+
const recordNames = Object.keys(tableRecords.records || {});
|
|
86
|
+
FileLogger_1.fileLogger.debug("Table " + tableName + ": " + recordNames.length + " records");
|
|
87
|
+
for (const recordName of recordNames) {
|
|
88
|
+
const record = tableRecords.records[recordName];
|
|
89
|
+
// Use the record's name property instead of the key for the directory name
|
|
90
|
+
const recordDirName = record.name || recordName;
|
|
91
|
+
const recordPath = path.join(tablePath, recordDirName);
|
|
92
|
+
// Check if metadata file exists in the files from server
|
|
93
|
+
const hasMetadataFromServer = record.files && record.files.some((f) => f.name === 'metaData' && f.type === 'json');
|
|
94
|
+
// Ensure the record directory exists
|
|
95
|
+
await fsp.mkdir(recordPath, { recursive: true });
|
|
96
|
+
// Process each file in the record
|
|
97
|
+
for (const file of record.files || []) {
|
|
98
|
+
const filePath = path.join(recordPath, `${file.name}.${file.type}`);
|
|
99
|
+
const fileContent = file.content || "";
|
|
100
|
+
const fileDir = path.dirname(filePath);
|
|
101
|
+
await fsp.mkdir(fileDir, { recursive: true });
|
|
102
|
+
try {
|
|
103
|
+
await fsp.writeFile(filePath, fileContent, "utf8");
|
|
104
|
+
}
|
|
105
|
+
catch (writeError) {
|
|
106
|
+
Logger_1.logger.error("Failed to write: " + filePath);
|
|
107
|
+
throw writeError;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
// If no metadata from server, create a basic one
|
|
111
|
+
if (!hasMetadataFromServer) {
|
|
112
|
+
const metadataFilePath = path.join(recordPath, "metaData.json");
|
|
113
|
+
const metadataContent = {
|
|
114
|
+
_generatedAt: new Date().toISOString(),
|
|
115
|
+
_note: "Generated locally - metadata not provided by server"
|
|
116
|
+
};
|
|
117
|
+
try {
|
|
118
|
+
await fsp.writeFile(metadataFilePath, JSON.stringify(metadataContent, null, 2), "utf8");
|
|
119
|
+
}
|
|
120
|
+
catch (metaError) {
|
|
121
|
+
Logger_1.logger.error("Failed to write metadata: " + metadataFilePath);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
if (progBar)
|
|
125
|
+
progBar.tick({ scope: scopeLabel });
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
catch (error) {
|
|
130
|
+
Logger_1.logger.error("Error processing files for " + sourceDirectory + ": " + error);
|
|
131
|
+
throw error;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
async function processScope(scopeName, scopeConfig, apiDelay = 0) {
|
|
135
|
+
try {
|
|
136
|
+
const instance = process.env.SN_INSTANCE || "unknown";
|
|
137
|
+
Logger_1.logger.info("Processing scope: " + scopeName + " (" + instance + ")");
|
|
138
|
+
// Get the client
|
|
139
|
+
const client = (0, snClient_1.defaultClient)();
|
|
140
|
+
// Get the config
|
|
141
|
+
const config = ConfigManager.getConfig();
|
|
142
|
+
// Determine the source directory for this scope
|
|
143
|
+
let sourceDirectory;
|
|
144
|
+
if (typeof scopeConfig === "object" && scopeConfig.sourceDirectory) {
|
|
145
|
+
sourceDirectory = path.resolve(ConfigManager.getRootDir(), scopeConfig.sourceDirectory);
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
// Default to src/{scope} if no sourceDirectory specified
|
|
149
|
+
sourceDirectory = path.resolve(ConfigManager.getRootDir(), "src", scopeName);
|
|
150
|
+
}
|
|
151
|
+
FileLogger_1.fileLogger.debug("Source directory for " + scopeName + ": " + sourceDirectory);
|
|
152
|
+
const apps = await (0, snClient_1.unwrapSNResponse)(client.getAppList());
|
|
153
|
+
const scopeApp = apps.find((app) => app.scope === scopeName);
|
|
154
|
+
if (!scopeApp) {
|
|
155
|
+
Logger_1.logger.warn("Scope " + scopeName + " not found in ServiceNow apps list");
|
|
156
|
+
}
|
|
157
|
+
else {
|
|
158
|
+
Logger_1.logger.info("Found app: " + scopeApp.displayName);
|
|
159
|
+
}
|
|
160
|
+
// Resolve the table whitelist and field overrides for this scope
|
|
161
|
+
var resolvedConfig = ConfigManager.resolveConfigForScope(scopeName);
|
|
162
|
+
var allowedTables = resolvedConfig.tables;
|
|
163
|
+
// Build a config safe for the ServiceNow API (no _ directive keys)
|
|
164
|
+
var apiConfig = Object.assign({}, config, {
|
|
165
|
+
includes: resolvedConfig.apiIncludes,
|
|
166
|
+
excludes: resolvedConfig.apiExcludes,
|
|
167
|
+
});
|
|
168
|
+
Logger_1.logger.info("Downloading manifest for " + scopeName + "...");
|
|
169
|
+
if (apiDelay > 0) {
|
|
170
|
+
await delay(apiDelay);
|
|
171
|
+
}
|
|
172
|
+
const manifest = await (0, snClient_1.unwrapSNResponse)(client.getManifest(scopeName, apiConfig, false));
|
|
173
|
+
// Client-side table filtering — only keep tables in the _tables whitelist
|
|
174
|
+
if (allowedTables && allowedTables.length > 0) {
|
|
175
|
+
var manifestTableNames = Object.keys(manifest.tables || {});
|
|
176
|
+
var filteredTables = {};
|
|
177
|
+
var skippedCount = 0;
|
|
178
|
+
for (var t = 0; t < manifestTableNames.length; t++) {
|
|
179
|
+
var tName = manifestTableNames[t];
|
|
180
|
+
if (allowedTables.indexOf(tName) !== -1) {
|
|
181
|
+
filteredTables[tName] = manifest.tables[tName];
|
|
182
|
+
}
|
|
183
|
+
else {
|
|
184
|
+
skippedCount++;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
var keptCount = Object.keys(filteredTables).length;
|
|
188
|
+
if (skippedCount > 0) {
|
|
189
|
+
FileLogger_1.fileLogger.debug("Filtered " + skippedCount + " tables not in _tables whitelist for " + scopeName +
|
|
190
|
+
". Keeping " + keptCount + " of " + manifestTableNames.length);
|
|
191
|
+
}
|
|
192
|
+
Logger_1.logger.info(scopeName + ": " + keptCount + " tables match config (" + skippedCount + " filtered out)");
|
|
193
|
+
manifest.tables = filteredTables;
|
|
194
|
+
}
|
|
195
|
+
else {
|
|
196
|
+
Logger_1.logger.warn("No _tables whitelist defined — writing ALL tables for " + scopeName);
|
|
197
|
+
}
|
|
198
|
+
// Normalize record keys from sys_id to display name
|
|
199
|
+
AppUtils.normalizeManifestKeys(manifest);
|
|
200
|
+
// Build the missing files structure from the filtered manifest
|
|
201
|
+
var manifestTables = manifest.tables || {};
|
|
202
|
+
var allMissingFiles = {};
|
|
203
|
+
for (var tableName in manifestTables) {
|
|
204
|
+
var table = manifestTables[tableName];
|
|
205
|
+
allMissingFiles[tableName] = {};
|
|
206
|
+
for (var recordName in table.records || {}) {
|
|
207
|
+
var record = table.records[recordName];
|
|
208
|
+
allMissingFiles[tableName][record.sys_id] = record.files.map(function (f) {
|
|
209
|
+
return { name: f.name, type: f.type };
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
// Download file contents in chunks to avoid ServiceNow 500 errors on large payloads
|
|
214
|
+
var CHUNK_SIZE = 5;
|
|
215
|
+
var tableNames = Object.keys(allMissingFiles);
|
|
216
|
+
var totalChunks = Math.ceil(tableNames.length / CHUNK_SIZE);
|
|
217
|
+
Logger_1.logger.info("Downloading file contents for " + scopeName + " (" + tableNames.length + " tables in " + totalChunks + " batch" + (totalChunks !== 1 ? "es" : "") + ")...");
|
|
218
|
+
for (var chunkIdx = 0; chunkIdx < tableNames.length; chunkIdx += CHUNK_SIZE) {
|
|
219
|
+
var chunkTableNames = tableNames.slice(chunkIdx, chunkIdx + CHUNK_SIZE);
|
|
220
|
+
var chunkMissing = {};
|
|
221
|
+
for (var ci = 0; ci < chunkTableNames.length; ci++) {
|
|
222
|
+
chunkMissing[chunkTableNames[ci]] = allMissingFiles[chunkTableNames[ci]];
|
|
223
|
+
}
|
|
224
|
+
if (apiDelay > 0) {
|
|
225
|
+
await delay(apiDelay);
|
|
226
|
+
}
|
|
227
|
+
var batchNum = Math.floor(chunkIdx / CHUNK_SIZE) + 1;
|
|
228
|
+
FileLogger_1.fileLogger.debug("Downloading batch " + batchNum + "/" + totalChunks + " for " + scopeName + ": " + chunkTableNames.join(", "));
|
|
229
|
+
var chunkContent = await (0, snClient_1.unwrapSNResponse)(client.getMissingFiles(chunkMissing, config.tableOptions || {}));
|
|
230
|
+
// Merge chunk content back into the manifest
|
|
231
|
+
for (var chunkTable in chunkContent || {}) {
|
|
232
|
+
if (manifest.tables[chunkTable]) {
|
|
233
|
+
for (var chunkRecName in chunkContent[chunkTable].records || {}) {
|
|
234
|
+
var recordWithContent = chunkContent[chunkTable].records[chunkRecName];
|
|
235
|
+
var manifestRecords = manifest.tables[chunkTable].records;
|
|
236
|
+
var matchingRecord = Object.values(manifestRecords).find(function (r) {
|
|
237
|
+
return r.sys_id === recordWithContent.sys_id;
|
|
238
|
+
});
|
|
239
|
+
if (matchingRecord) {
|
|
240
|
+
matchingRecord.files = recordWithContent.files;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
const tableCount = Object.keys((manifest && manifest.tables) || {}).length;
|
|
247
|
+
Logger_1.logger.info("Writing " + tableCount + " tables for " + scopeName + "...");
|
|
248
|
+
await processManifestForScope(manifest, sourceDirectory, true);
|
|
249
|
+
// Create the scope-specific manifest structure
|
|
250
|
+
const scopeManifest = {
|
|
251
|
+
tables: (manifest && manifest.tables) || {},
|
|
252
|
+
scope: scopeName,
|
|
253
|
+
};
|
|
254
|
+
Logger_1.logger.success("Scope " + scopeName + " complete — files saved to " + sourceDirectory);
|
|
255
|
+
return {
|
|
256
|
+
scope: scopeName,
|
|
257
|
+
success: true,
|
|
258
|
+
manifest: scopeManifest,
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
catch (error) {
|
|
262
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
263
|
+
Logger_1.logger.error("Failed to process scope " + scopeName + ": " + errorMessage);
|
|
264
|
+
return {
|
|
265
|
+
scope: scopeName,
|
|
266
|
+
success: false,
|
|
267
|
+
error: error instanceof Error ? error : new Error(String(error)),
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
// Helper function to add delay between API calls
|
|
272
|
+
async function delay(ms) {
|
|
273
|
+
if (ms > 0) {
|
|
274
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
async function initScopesCommand(args) {
|
|
278
|
+
(0, commands_1.setLogLevel)(args);
|
|
279
|
+
// Get API delay from args, default to 0ms (no delay)
|
|
280
|
+
const apiDelay = args.delay || 0;
|
|
281
|
+
if (apiDelay > 0) {
|
|
282
|
+
Logger_1.logger.info(`Using ${apiDelay}ms delay between API calls to prevent server overload`);
|
|
283
|
+
}
|
|
284
|
+
try {
|
|
285
|
+
// First check if we have environment variables set
|
|
286
|
+
if (!process.env.SN_USER ||
|
|
287
|
+
!process.env.SN_PASSWORD ||
|
|
288
|
+
!process.env.SN_INSTANCE) {
|
|
289
|
+
try {
|
|
290
|
+
let loginAnswers = await (0, wizard_1.getLoginInfo)();
|
|
291
|
+
await (0, wizard_1.setupDotEnv)(loginAnswers);
|
|
292
|
+
}
|
|
293
|
+
catch (error) {
|
|
294
|
+
Logger_1.logger.error("Error getting ServiceNow credentials: " + error);
|
|
295
|
+
throw error;
|
|
296
|
+
}
|
|
297
|
+
if (!process.env.SN_USER ||
|
|
298
|
+
!process.env.SN_PASSWORD ||
|
|
299
|
+
!process.env.SN_INSTANCE) {
|
|
300
|
+
Logger_1.logger.error("Missing ServiceNow credentials. Please ensure SN_USER, SN_PASSWORD, and SN_INSTANCE are set in your .env file");
|
|
301
|
+
throw new Error("ServiceNow credentials not configured");
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
// Load config
|
|
305
|
+
await ConfigManager.loadConfigs();
|
|
306
|
+
const config = ConfigManager.getConfig();
|
|
307
|
+
if (!config.scopes) {
|
|
308
|
+
Logger_1.logger.error("No scopes defined in dove.config.js");
|
|
309
|
+
throw new Error("No scopes defined in configuration");
|
|
310
|
+
}
|
|
311
|
+
const scopes = Object.keys(config.scopes);
|
|
312
|
+
const instance = process.env.SN_INSTANCE || "unknown";
|
|
313
|
+
Logger_1.logger.info("Initializing " + scopes.length + " scopes from " + instance + ": " + scopes.join(", "));
|
|
314
|
+
const scopePromises = scopes.map((scopeName) => processScope(scopeName, config.scopes[scopeName], apiDelay));
|
|
315
|
+
const results = await Promise.allSettled(scopePromises);
|
|
316
|
+
// Collect successful manifests
|
|
317
|
+
const manifests = {};
|
|
318
|
+
let successCount = 0;
|
|
319
|
+
let failCount = 0;
|
|
320
|
+
results.forEach((result, index) => {
|
|
321
|
+
const scopeName = scopes[index];
|
|
322
|
+
if (result.status === "fulfilled" && result.value.success) {
|
|
323
|
+
manifests[scopeName] = result.value.manifest;
|
|
324
|
+
successCount++;
|
|
325
|
+
}
|
|
326
|
+
else {
|
|
327
|
+
failCount++;
|
|
328
|
+
const error = result.status === "rejected" ? result.reason : (result.value && result.value.error);
|
|
329
|
+
Logger_1.logger.error(`Failed to process ${scopeName}: ${(error && error.message) || "Unknown error"}`);
|
|
330
|
+
}
|
|
331
|
+
});
|
|
332
|
+
// Write per-scope manifest files instead of a single combined one
|
|
333
|
+
for (const [scopeName, scopeData] of Object.entries(manifests)) {
|
|
334
|
+
const scopeManifestPath = ConfigManager.getScopeManifestPath(scopeName);
|
|
335
|
+
await fsp.writeFile(scopeManifestPath, JSON.stringify(scopeData, null, 2));
|
|
336
|
+
Logger_1.logger.info(`Wrote manifest for ${scopeName} to: ${scopeManifestPath}`);
|
|
337
|
+
}
|
|
338
|
+
Logger_1.logger.info("─".repeat(50));
|
|
339
|
+
Logger_1.logger.success("Scope initialization complete — " + successCount + "/" + scopes.length + " scopes processed");
|
|
340
|
+
if (failCount > 0) {
|
|
341
|
+
Logger_1.logger.warn(failCount + " scope(s) failed — check errors above");
|
|
342
|
+
}
|
|
343
|
+
Logger_1.logger.info("Manifests: dove.manifest.<scope>.json");
|
|
344
|
+
Logger_1.logger.success("Run 'npx dove watch' to start development");
|
|
345
|
+
}
|
|
346
|
+
catch (e) {
|
|
347
|
+
Logger_1.logger.error("Error initializing scopes: " + e);
|
|
348
|
+
throw e;
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
async function watchAllScopesCommand(args) {
|
|
352
|
+
(0, commands_1.setLogLevel)(args);
|
|
353
|
+
var dashboardProcess = null;
|
|
354
|
+
try {
|
|
355
|
+
// First check if we have environment variables set
|
|
356
|
+
if (!process.env.SN_USER ||
|
|
357
|
+
!process.env.SN_PASSWORD ||
|
|
358
|
+
!process.env.SN_INSTANCE) {
|
|
359
|
+
let loginAnswers = await (0, wizard_1.getLoginInfo)();
|
|
360
|
+
Logger_1.logger.error("Missing ServiceNow credentials. Please ensure SN_USER, SN_PASSWORD, and SN_INSTANCE are set in your .env file");
|
|
361
|
+
throw new Error("ServiceNow credentials not configured");
|
|
362
|
+
}
|
|
363
|
+
// Start dashboard unless --no-dashboard flag is set
|
|
364
|
+
if (!args.noDashboard) {
|
|
365
|
+
dashboardProcess = startDashboardProcess(args.port);
|
|
366
|
+
}
|
|
367
|
+
// Import and start the multi-scope watcher
|
|
368
|
+
const { startMultiScopeWatching } = await Promise.resolve().then(() => __importStar(require("./MultiScopeWatcher")));
|
|
369
|
+
// Start watching all scopes with monitoring options
|
|
370
|
+
await startMultiScopeWatching({
|
|
371
|
+
monitorIntervalMs: args.noMonitoring ? 0 : (args.monitorInterval || 120) * 1000,
|
|
372
|
+
});
|
|
373
|
+
// Keep the process running
|
|
374
|
+
process.on("SIGINT", async () => {
|
|
375
|
+
Logger_1.logger.info("\nStopping multi-scope watch...");
|
|
376
|
+
if (dashboardProcess) {
|
|
377
|
+
dashboardProcess.kill("SIGINT");
|
|
378
|
+
}
|
|
379
|
+
const { stopMultiScopeWatching } = await Promise.resolve().then(() => __importStar(require("./MultiScopeWatcher")));
|
|
380
|
+
stopMultiScopeWatching();
|
|
381
|
+
process.exit(0);
|
|
382
|
+
});
|
|
383
|
+
}
|
|
384
|
+
catch (error) {
|
|
385
|
+
if (dashboardProcess) {
|
|
386
|
+
dashboardProcess.kill("SIGINT");
|
|
387
|
+
}
|
|
388
|
+
Logger_1.logger.error("Failed to start multi-scope watch: " + error);
|
|
389
|
+
throw error;
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
function startDashboardProcess(portOverride) {
|
|
393
|
+
var serverPath;
|
|
394
|
+
try {
|
|
395
|
+
serverPath = require.resolve("@tenonhq/dovetail-dashboard/server.js");
|
|
396
|
+
}
|
|
397
|
+
catch (e) {
|
|
398
|
+
Logger_1.logger.warn("Dashboard package not installed. Run: npm install @tenonhq/dovetail-dashboard");
|
|
399
|
+
return null;
|
|
400
|
+
}
|
|
401
|
+
var port = portOverride ? String(portOverride) : (process.env.DASHBOARD_PORT || "3456");
|
|
402
|
+
var server = (0, child_process_1.spawn)("node", [serverPath], {
|
|
403
|
+
cwd: process.cwd(),
|
|
404
|
+
stdio: "ignore",
|
|
405
|
+
env: { ...process.env, DASHBOARD_PORT: port },
|
|
406
|
+
detached: false,
|
|
407
|
+
});
|
|
408
|
+
server.on("error", function (err) {
|
|
409
|
+
Logger_1.logger.warn("Failed to start dashboard: " + err.message);
|
|
410
|
+
});
|
|
411
|
+
Logger_1.logger.info("Dashboard started at http://localhost:" + port);
|
|
412
|
+
// Open browser after a short delay
|
|
413
|
+
setTimeout(function () {
|
|
414
|
+
var url = "http://localhost:" + port;
|
|
415
|
+
(0, child_process_1.spawn)("open", [url], { stdio: "ignore" });
|
|
416
|
+
}, 1000);
|
|
417
|
+
return server;
|
|
418
|
+
}
|