@tenonhq/sincronia-core 0.0.73 → 0.0.74

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/FileUtils.js CHANGED
@@ -138,12 +138,20 @@ const getFileExtension = (filePath) => {
138
138
  return "";
139
139
  }
140
140
  };
141
- const getBuildExt = (table, recordName, field) => {
142
- const manifest = ConfigManager.getManifest();
141
+ const getBuildExt = (table, recordName, field, scope) => {
142
+ var manifest = ConfigManager.getManifest();
143
143
  if (!manifest) {
144
144
  throw new Error("Failed to retrieve manifest");
145
145
  }
146
- const files = manifest.tables[table].records[recordName].files;
146
+ var resolvedManifest = manifest;
147
+ if (scope && ConfigManager.isMultiScopeManifest(manifest)) {
148
+ var scopeMan = ConfigManager.resolveManifestForScope(manifest, scope);
149
+ if (!scopeMan) {
150
+ throw new Error("Failed to find scope " + scope + " in manifest");
151
+ }
152
+ resolvedManifest = scopeMan;
153
+ }
154
+ const files = resolvedManifest.tables[table].records[recordName].files;
147
155
  const file = files.find((f) => f.name === field);
148
156
  if (!file) {
149
157
  throw new Error("Unable to find file");
@@ -163,11 +171,28 @@ const getFileContextFromPath = (filePath) => {
163
171
  .split(path_1.default.sep)
164
172
  .slice(-2);
165
173
  const targetField = getTargetFieldFromPath(filePath, tableName, ext);
166
- const manifest = ConfigManager.getManifest();
174
+ var manifest = ConfigManager.getManifest();
167
175
  if (!manifest) {
168
176
  throw new Error("No manifest has been loaded!");
169
177
  }
170
- const { tables, scope } = manifest;
178
+ var scope;
179
+ var tables;
180
+ if (ConfigManager.isMultiScopeManifest(manifest)) {
181
+ var detectedScope = ConfigManager.resolveScopeFromPath(filePath);
182
+ if (!detectedScope) {
183
+ return undefined;
184
+ }
185
+ var scopeMan = ConfigManager.resolveManifestForScope(manifest, detectedScope);
186
+ if (!scopeMan) {
187
+ return undefined;
188
+ }
189
+ scope = scopeMan.scope || detectedScope;
190
+ tables = scopeMan.tables;
191
+ }
192
+ else {
193
+ scope = manifest.scope;
194
+ tables = manifest.tables;
195
+ }
171
196
  try {
172
197
  const { records } = tables[tableName];
173
198
  const record = records[recordName];
@@ -41,6 +41,7 @@ exports.watchAllScopesCommand = watchAllScopesCommand;
41
41
  const Logger_1 = require("./Logger");
42
42
  const FileLogger_1 = require("./FileLogger");
43
43
  const ConfigManager = __importStar(require("./config"));
44
+ const AppUtils = __importStar(require("./appUtils"));
44
45
  const wizard_1 = require("./wizard");
45
46
  const snClient_1 = require("./snClient");
46
47
  const commands_1 = require("./commands");
@@ -193,6 +194,8 @@ async function processScope(scopeName, scopeConfig, apiDelay = 0) {
193
194
  else {
194
195
  Logger_1.logger.warn("No _tables whitelist defined — writing ALL tables for " + scopeName);
195
196
  }
197
+ // Normalize record keys from sys_id to display name
198
+ AppUtils.normalizeManifestKeys(manifest);
196
199
  // Build the missing files structure from the filtered manifest
197
200
  var manifestTables = manifest.tables || {};
198
201
  var allMissingFiles = {};
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.checkScope = exports.createAndAssignUpdateSet = exports.swapScope = exports.buildFiles = exports.summarizeRecord = exports.pushFiles = exports.getAppFileList = exports.groupAppFiles = exports.processMissingFiles = exports.findMissingFiles = exports.syncManifest = exports.processManifest = void 0;
39
+ exports.createAndAssignUpdateSet = exports.swapScope = exports.buildFiles = exports.summarizeRecord = exports.pushFiles = exports.getAppFileList = exports.groupAppFiles = exports.processMissingFiles = exports.findMissingFiles = exports.syncManifest = exports.processManifest = exports.normalizeManifestKeys = 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"));
@@ -113,6 +113,34 @@ const processTablesInManifest = async (tables, forceWrite, sourcePath, onRecordP
113
113
  return processRecsInManTable(path_1.default.join(basePath, tableName), tables[tableName], forceWrite, onRecordProcessed);
114
114
  });
115
115
  };
116
+ /**
117
+ * Re-keys manifest records from sys_id to record.name (display value).
118
+ * Some ServiceNow tables return records keyed by sys_id instead of display name.
119
+ * This ensures consistent naming for directories and manifest lookups.
120
+ */
121
+ const normalizeManifestKeys = (manifest) => {
122
+ var tables = manifest.tables || {};
123
+ var tableNames = Object.keys(tables);
124
+ for (var i = 0; i < tableNames.length; i++) {
125
+ var tableName = tableNames[i];
126
+ var records = tables[tableName].records || {};
127
+ var recordKeys = Object.keys(records);
128
+ var normalized = {};
129
+ for (var j = 0; j < recordKeys.length; j++) {
130
+ var key = recordKeys[j];
131
+ var record = records[key];
132
+ var displayKey = record.name || key;
133
+ // Handle duplicate display names by appending sys_id suffix
134
+ if (normalized[displayKey]) {
135
+ displayKey = displayKey + " (" + record.sys_id.substring(0, 8) + ")";
136
+ }
137
+ normalized[displayKey] = record;
138
+ }
139
+ tables[tableName].records = normalized;
140
+ }
141
+ return manifest;
142
+ };
143
+ exports.normalizeManifestKeys = normalizeManifestKeys;
116
144
  const processManifest = async (manifest, forceWrite = false, sourcePath) => {
117
145
  const tableCount = Object.keys(manifest.tables).length;
118
146
  FileLogger_1.fileLogger.debug("Processing manifest: " + (manifest.scope || "legacy") + " (" + tableCount + " tables)");
@@ -142,20 +170,20 @@ const syncManifest = async (scope) => {
142
170
  const config = ConfigManager.getConfig();
143
171
  // Resolve scope-specific source directory
144
172
  var scopeSourcePath = ConfigManager.getSourcePathForScope(scope);
145
- const newManifest = await (0, snClient_1.unwrapSNResponse)(client.getManifest(scope, config));
173
+ const newManifest = (0, exports.normalizeManifestKeys)(await (0, snClient_1.unwrapSNResponse)(client.getManifest(scope, config)));
146
174
  const refreshTableCount = Object.keys(newManifest.tables).length;
147
175
  FileLogger_1.fileLogger.debug("Refreshed manifest for " + scope + ": " + refreshTableCount + " tables");
148
176
  await fUtils.writeScopeManifest(scope, newManifest);
149
177
  await (0, exports.processMissingFiles)(newManifest, scopeSourcePath);
150
178
  // Update the in-memory manifest for this scope
151
- if (typeof curManifest === "object" && !curManifest.tables) {
179
+ if (ConfigManager.isMultiScopeManifest(curManifest)) {
152
180
  curManifest[scope] = newManifest;
153
181
  ConfigManager.updateManifest(curManifest);
154
182
  }
155
183
  }
156
184
  else {
157
185
  // Sync all scopes if manifest has multiple scopes
158
- if (typeof curManifest === "object" && !curManifest.tables) {
186
+ if (ConfigManager.isMultiScopeManifest(curManifest)) {
159
187
  // Multiple scopes detected
160
188
  for (const scopeName of Object.keys(curManifest)) {
161
189
  await (0, exports.syncManifest)(scopeName);
@@ -422,6 +450,18 @@ const writeBuildFile = async (preBuild, buildRes, summary) => {
422
450
  const sourcePath = ConfigManager.getSourcePath();
423
451
  const buildPath = ConfigManager.getBuildPath();
424
452
  const fieldNames = Object.keys(fields);
453
+ const writePromises = fieldNames.map(async (field) => {
454
+ const fieldCtx = fields[field];
455
+ const srcFilePath = fieldCtx.filePath;
456
+ const relativePath = path_1.default.relative(sourcePath, srcFilePath);
457
+ const relPathNoExt = relativePath.split(".").slice(0, -1).join();
458
+ const buildExt = fUtils.getBuildExt(fieldCtx.tableName, fieldCtx.name, fieldCtx.targetField, fieldCtx.scope);
459
+ const relPathNewExt = `${relPathNoExt}.${buildExt}`;
460
+ const buildFilePath = path_1.default.join(buildPath, relPathNewExt);
461
+ await fUtils.createDirRecursively(path_1.default.dirname(buildFilePath));
462
+ const writeResult = await fUtils.writeFileForce(buildFilePath, buildRes.builtRec[fieldCtx.targetField]);
463
+ return writeResult;
464
+ });
425
465
  try {
426
466
  await (0, genericUtils_1.processBatched)(fieldNames, constants_1.CONCURRENCY_FILES, async function (field) {
427
467
  const fieldCtx = fields[field];
@@ -532,92 +572,3 @@ const createAndAssignUpdateSet = async (updateSetName = "", scope) => {
532
572
  };
533
573
  };
534
574
  exports.createAndAssignUpdateSet = createAndAssignUpdateSet;
535
- const checkScope = async (swap) => {
536
- try {
537
- const man = ConfigManager.getManifest();
538
- if (man) {
539
- // Detect multi-scope manifest (keys are scope names, no top-level .scope)
540
- var isMultiScope = typeof man === "object" && !man.scope && !man.tables;
541
- if (isMultiScope) {
542
- // Multi-scope: session scope just needs to match any configured scope
543
- var configuredScopes = Object.keys(man);
544
- var client = (0, snClient_1.defaultClient)();
545
- var scopeObj;
546
- try {
547
- scopeObj = await (0, snClient_1.unwrapSNResponse)(client.getCurrentScope());
548
- }
549
- catch (scopeErr) {
550
- Logger_1.logger.info("Scope check endpoint unavailable, assuming match for multi-scope project");
551
- return { match: true, sessionScope: configuredScopes[0], manifestScope: configuredScopes[0] };
552
- }
553
- var sessionScope = scopeObj.scope;
554
- var scopeMatch = configuredScopes.indexOf(sessionScope) !== -1;
555
- Logger_1.logger.info("Current scope: " + sessionScope + ", Configured scopes: " + configuredScopes.join(", "));
556
- if (scopeMatch) {
557
- return { match: true, sessionScope: sessionScope, manifestScope: sessionScope };
558
- }
559
- else if (swap && configuredScopes.length > 0) {
560
- Logger_1.logger.info("Current scope (" + sessionScope + ") not in configured scopes. Swapping to " + configuredScopes[0] + "...\n");
561
- var swappedScopeObj = await (0, exports.swapScope)(configuredScopes[0]);
562
- return {
563
- match: configuredScopes.indexOf(swappedScopeObj.scope) !== -1,
564
- sessionScope: swappedScopeObj.scope,
565
- manifestScope: configuredScopes[0],
566
- };
567
- }
568
- else {
569
- return { match: false, sessionScope: sessionScope, manifestScope: configuredScopes.join(", ") };
570
- }
571
- }
572
- // Single-scope manifest
573
- var singleClient = (0, snClient_1.defaultClient)();
574
- var singleScopeObj;
575
- try {
576
- singleScopeObj = await (0, snClient_1.unwrapSNResponse)(singleClient.getCurrentScope());
577
- }
578
- catch (scopeErr) {
579
- // getCurrentScope endpoint may not exist on this instance — skip scope check
580
- Logger_1.logger.info(`Scope check endpoint unavailable, assuming scope match for: ${man.scope}`);
581
- return {
582
- match: true,
583
- sessionScope: man.scope,
584
- manifestScope: man.scope,
585
- };
586
- }
587
- Logger_1.logger.info(`Current scope: ${singleScopeObj.scope}, Manifest scope: ${man.scope}`);
588
- if (singleScopeObj.scope === man.scope) {
589
- return {
590
- match: true,
591
- sessionScope: singleScopeObj.scope,
592
- manifestScope: man.scope,
593
- };
594
- }
595
- else if (swap) {
596
- Logger_1.logger.info(`Current scope (${singleScopeObj.scope}) does not match manifest scope (${man.scope}). Swapping...\n`);
597
- var singleSwapped = await (0, exports.swapScope)(man.scope);
598
- return {
599
- match: singleSwapped.scope === man.scope,
600
- sessionScope: singleSwapped.scope,
601
- manifestScope: man.scope,
602
- };
603
- }
604
- else {
605
- return {
606
- match: false,
607
- sessionScope: singleScopeObj.scope,
608
- manifestScope: man.scope,
609
- };
610
- }
611
- }
612
- //first time case
613
- return {
614
- match: true,
615
- sessionScope: "",
616
- manifestScope: "",
617
- };
618
- }
619
- catch (e) {
620
- throw e;
621
- }
622
- };
623
- exports.checkScope = checkScope;
package/dist/commander.js CHANGED
@@ -50,12 +50,6 @@ async function initCommands() {
50
50
  default: "",
51
51
  describe: "Specify branch to do git diff against",
52
52
  },
53
- scopeSwap: {
54
- alias: "ss",
55
- type: "boolean",
56
- default: false,
57
- describe: "Will auto-swap to the correct scope for the files being pushed",
58
- },
59
53
  updateSet: {
60
54
  alias: "us",
61
55
  type: "string",
package/dist/commands.js CHANGED
@@ -37,7 +37,6 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
37
37
  };
38
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
39
  exports.setLogLevel = setLogLevel;
40
- exports.devCommand = devCommand;
41
40
  exports.refreshCommand = refreshCommand;
42
41
  exports.pushCommand = pushCommand;
43
42
  exports.downloadCommand = downloadCommand;
@@ -46,7 +45,6 @@ exports.buildCommand = buildCommand;
46
45
  exports.deployCommand = deployCommand;
47
46
  exports.statusCommand = statusCommand;
48
47
  const ConfigManager = __importStar(require("./config"));
49
- const Watcher_1 = require("./Watcher");
50
48
  const AppUtils = __importStar(require("./appUtils"));
51
49
  const orchestrator_1 = require("./initSystem/orchestrator");
52
50
  const Logger_1 = require("./Logger");
@@ -56,135 +54,108 @@ const snClient_1 = require("./snClient");
56
54
  const inquirer_1 = __importDefault(require("inquirer"));
57
55
  const gitUtils_1 = require("./gitUtils");
58
56
  const FileUtils_1 = require("./FileUtils");
59
- async function scopeCheck(successFunc, swapScopes = false) {
57
+ function setLogLevel(args) {
58
+ Logger_1.logger.setLogLevel(args.logLevel);
59
+ }
60
+ async function refreshCommand(args, log = true) {
61
+ setLogLevel(args);
60
62
  try {
61
- const scopeCheck = await AppUtils.checkScope(swapScopes);
62
- if (!scopeCheck.match) {
63
- (0, logMessages_1.scopeCheckMessage)(scopeCheck);
64
- // Throw exception to register this as an error
65
- throw new Error();
66
- }
67
- else {
68
- await successFunc();
69
- }
63
+ if (!log)
64
+ setLogLevel({ logLevel: "warn" });
65
+ FileLogger_1.fileLogger.debug("Syncing manifest from instance");
66
+ await AppUtils.syncManifest();
67
+ Logger_1.logger.success("Refresh complete!");
68
+ setLogLevel(args);
70
69
  }
71
70
  catch (e) {
72
- Logger_1.logger.error("Scope check failed. Verify your project configuration or run `npx sinc init`");
73
- // Throw exception to register this as an error
74
- process.exit(1);
71
+ throw e;
75
72
  }
76
73
  }
77
- function setLogLevel(args) {
78
- Logger_1.logger.setLogLevel(args.logLevel);
79
- }
80
- async function devCommand(args) {
74
+ async function pushCommand(args) {
81
75
  setLogLevel(args);
82
- await scopeCheck(async () => {
83
- (0, Watcher_1.startWatching)(ConfigManager.getSourcePath());
84
- (0, logMessages_1.devModeLog)();
85
- let refresher = () => {
86
- refreshCommand(args, false);
87
- };
88
- let interval = ConfigManager.getRefresh();
89
- if (interval && interval > 0) {
90
- Logger_1.logger.info(`Checking for new manifest files every ${interval} seconds`);
91
- setInterval(refresher, interval * 1000);
76
+ try {
77
+ const { updateSet, ci: skipPrompt, target, diff } = args;
78
+ let encodedPaths;
79
+ if (target !== undefined && target !== "")
80
+ encodedPaths = target;
81
+ else
82
+ encodedPaths = await (0, gitUtils_1.gitDiffToEncodedPaths)(diff);
83
+ const fileList = await AppUtils.getAppFileList(encodedPaths);
84
+ const instance = process.env.SN_INSTANCE || "unknown";
85
+ Logger_1.logger.info(fileList.length + " files to push to " + instance);
86
+ if (!skipPrompt) {
87
+ const targetServer = process.env.SN_INSTANCE;
88
+ if (!targetServer) {
89
+ Logger_1.logger.error("No SN_INSTANCE configured. Set it in your .env file.");
90
+ return;
91
+ }
92
+ const answers = await inquirer_1.default.prompt([
93
+ {
94
+ type: "confirm",
95
+ name: "confirmed",
96
+ message: "Pushing will overwrite code in your instance. Are you sure?",
97
+ default: false,
98
+ },
99
+ ]);
100
+ if (!answers["confirmed"])
101
+ return;
92
102
  }
93
- });
94
- }
95
- async function refreshCommand(args, log = true) {
96
- setLogLevel(args);
97
- await scopeCheck(async () => {
98
- try {
99
- if (!log)
100
- setLogLevel({ logLevel: "warn" });
101
- FileLogger_1.fileLogger.debug("Syncing manifest from instance");
102
- await AppUtils.syncManifest();
103
- Logger_1.logger.success("Refresh complete!");
104
- setLogLevel(args);
103
+ // Handle --clickup flag: resolve ClickUp task into an update set name
104
+ let resolvedUpdateSet = updateSet;
105
+ if (!resolvedUpdateSet && args.clickup) {
106
+ try {
107
+ const { resolveClickUpForPush } = await Promise.resolve().then(() => __importStar(require("./clickupPushHelper")));
108
+ resolvedUpdateSet = await resolveClickUpForPush(args.clickup);
109
+ }
110
+ catch (cuErr) {
111
+ if (cuErr instanceof Error)
112
+ Logger_1.logger.error(cuErr.message);
113
+ process.exit(1);
114
+ }
105
115
  }
106
- catch (e) {
107
- throw e;
116
+ // Extract scope from file list and auto-swap to it
117
+ var pushScope;
118
+ if (fileList.length > 0) {
119
+ var fieldKeys = Object.keys(fileList[0].fields);
120
+ if (fieldKeys.length > 0) {
121
+ pushScope = fileList[0].fields[fieldKeys[0]].scope;
122
+ }
108
123
  }
109
- });
110
- }
111
- async function pushCommand(args) {
112
- setLogLevel(args);
113
- await scopeCheck(async () => {
114
- try {
115
- const { updateSet, ci: skipPrompt, target, diff } = args;
116
- let encodedPaths;
117
- if (target !== undefined && target !== "")
118
- encodedPaths = target;
119
- else
120
- encodedPaths = await (0, gitUtils_1.gitDiffToEncodedPaths)(diff);
121
- const fileList = await AppUtils.getAppFileList(encodedPaths);
122
- const instance = process.env.SN_INSTANCE || "unknown";
123
- Logger_1.logger.info(fileList.length + " files to push to " + instance);
124
+ if (pushScope) {
125
+ try {
126
+ var client = (0, snClient_1.defaultClient)();
127
+ await client.changeScope(pushScope);
128
+ Logger_1.logger.info("Switched to scope: " + pushScope);
129
+ }
130
+ catch (scopeErr) {
131
+ Logger_1.logger.warn("Could not auto-switch scope: " + scopeErr);
132
+ }
133
+ }
134
+ // Does not create update set if updateSetName is blank
135
+ if (resolvedUpdateSet) {
124
136
  if (!skipPrompt) {
125
- const targetServer = process.env.SN_INSTANCE;
126
- if (!targetServer) {
127
- Logger_1.logger.error("No SN_INSTANCE configured. Set it in your .env file.");
128
- return;
129
- }
130
- const answers = await inquirer_1.default.prompt([
137
+ let answers = await inquirer_1.default.prompt([
131
138
  {
132
139
  type: "confirm",
133
140
  name: "confirmed",
134
- message: "Pushing will overwrite code in your instance. Are you sure?",
141
+ message: `A new Update Set "${resolvedUpdateSet}" will be created for these pushed changes. Do you want to proceed?`,
135
142
  default: false,
136
143
  },
137
144
  ]);
138
- if (!answers["confirmed"])
139
- return;
140
- }
141
- // Handle --clickup flag: resolve ClickUp task into an update set name
142
- let resolvedUpdateSet = updateSet;
143
- if (!resolvedUpdateSet && args.clickup) {
144
- try {
145
- const { resolveClickUpForPush } = await Promise.resolve().then(() => __importStar(require("./clickupPushHelper")));
146
- resolvedUpdateSet = await resolveClickUpForPush(args.clickup);
147
- }
148
- catch (cuErr) {
149
- if (cuErr instanceof Error)
150
- Logger_1.logger.error(cuErr.message);
151
- process.exit(1);
145
+ if (!answers["confirmed"]) {
146
+ process.exit(0);
152
147
  }
153
148
  }
154
- // Extract scope from file list for update set creation
155
- var pushScope;
156
- if (fileList.length > 0) {
157
- var fieldKeys = Object.keys(fileList[0].fields);
158
- if (fieldKeys.length > 0) {
159
- pushScope = fileList[0].fields[fieldKeys[0]].scope;
160
- }
161
- }
162
- // Does not create update set if updateSetName is blank
163
- if (resolvedUpdateSet) {
164
- if (!skipPrompt) {
165
- let answers = await inquirer_1.default.prompt([
166
- {
167
- type: "confirm",
168
- name: "confirmed",
169
- message: `A new Update Set "${resolvedUpdateSet}" will be created for these pushed changes. Do you want to proceed?`,
170
- default: false,
171
- },
172
- ]);
173
- if (!answers["confirmed"]) {
174
- process.exit(0);
175
- }
176
- }
177
- const newUpdateSet = await AppUtils.createAndAssignUpdateSet(resolvedUpdateSet, pushScope);
178
- Logger_1.logger.debug(`New Update Set Created(${newUpdateSet.name}) sys_id:${newUpdateSet.id}`);
179
- }
180
- const pushResults = await AppUtils.pushFiles(fileList);
181
- (0, logMessages_1.logPushResults)(pushResults);
182
- }
183
- catch (e) {
184
- Logger_1.logger.getInternalLogger().error(e);
185
- process.exit(1);
149
+ const newUpdateSet = await AppUtils.createAndAssignUpdateSet(resolvedUpdateSet, pushScope);
150
+ Logger_1.logger.debug(`New Update Set Created(${newUpdateSet.name}) sys_id:${newUpdateSet.id}`);
186
151
  }
187
- }, args.scopeSwap);
152
+ const pushResults = await AppUtils.pushFiles(fileList);
153
+ (0, logMessages_1.logPushResults)(pushResults);
154
+ }
155
+ catch (e) {
156
+ Logger_1.logger.getInternalLogger().error(e);
157
+ process.exit(1);
158
+ }
188
159
  }
189
160
  async function downloadCommand(args) {
190
161
  setLogLevel(args);
@@ -284,43 +255,72 @@ async function getDeployPaths() {
284
255
  }
285
256
  async function deployCommand(args) {
286
257
  setLogLevel(args);
287
- await scopeCheck(async () => {
288
- try {
289
- const targetServer = process.env.SN_INSTANCE || "";
290
- if (!targetServer) {
291
- Logger_1.logger.error("No SN_INSTANCE configured. Set it in your .env file.");
292
- return;
293
- }
294
- const { confirmed } = await inquirer_1.default.prompt([
295
- {
296
- type: "confirm",
297
- name: "confirmed",
298
- message: "Deploying will overwrite code in your instance. Are you sure?",
299
- default: false,
300
- },
301
- ]);
302
- if (!confirmed) {
303
- return;
304
- }
305
- const paths = await getDeployPaths();
306
- Logger_1.logger.silly(`${paths.length} paths found...`);
307
- Logger_1.logger.silly(JSON.stringify(paths, null, 2));
308
- const appFileList = await AppUtils.getAppFileList(paths);
309
- const pushResults = await AppUtils.pushFiles(appFileList);
310
- (0, logMessages_1.logPushResults)(pushResults);
258
+ try {
259
+ const targetServer = process.env.SN_INSTANCE || "";
260
+ if (!targetServer) {
261
+ Logger_1.logger.error("No SN_INSTANCE configured. Set it in your .env file.");
262
+ return;
311
263
  }
312
- catch (e) {
313
- throw e;
264
+ const { confirmed } = await inquirer_1.default.prompt([
265
+ {
266
+ type: "confirm",
267
+ name: "confirmed",
268
+ message: "Deploying will overwrite code in your instance. Are you sure?",
269
+ default: false,
270
+ },
271
+ ]);
272
+ if (!confirmed) {
273
+ return;
314
274
  }
315
- });
275
+ const paths = await getDeployPaths();
276
+ Logger_1.logger.silly(`${paths.length} paths found...`);
277
+ Logger_1.logger.silly(JSON.stringify(paths, null, 2));
278
+ const appFileList = await AppUtils.getAppFileList(paths);
279
+ // Auto-swap to scope detected from files
280
+ if (appFileList.length > 0) {
281
+ var fieldKeys = Object.keys(appFileList[0].fields);
282
+ if (fieldKeys.length > 0) {
283
+ var deployScope = appFileList[0].fields[fieldKeys[0]].scope;
284
+ if (deployScope) {
285
+ try {
286
+ var client = (0, snClient_1.defaultClient)();
287
+ await client.changeScope(deployScope);
288
+ Logger_1.logger.info("Switched to scope: " + deployScope);
289
+ }
290
+ catch (scopeErr) {
291
+ Logger_1.logger.warn("Could not auto-switch scope: " + scopeErr);
292
+ }
293
+ }
294
+ }
295
+ }
296
+ const pushResults = await AppUtils.pushFiles(appFileList);
297
+ (0, logMessages_1.logPushResults)(pushResults);
298
+ }
299
+ catch (e) {
300
+ throw e;
301
+ }
316
302
  }
317
303
  async function statusCommand() {
318
304
  try {
319
305
  const client = (0, snClient_1.defaultClient)();
306
+ var config = ConfigManager.getConfig();
320
307
  let scopeObj = await (0, snClient_1.unwrapSNResponse)(client.getCurrentScope());
321
- Logger_1.logger.info("Instance: " + (process.env.SN_INSTANCE || "not set"));
322
- Logger_1.logger.info("Scope: " + scopeObj.scope);
323
- Logger_1.logger.info("User: " + (process.env.SN_USER || "not set"));
308
+ Logger_1.logger.info("Instance: " + (process.env.SN_INSTANCE || "not set"));
309
+ Logger_1.logger.info("User: " + (process.env.SN_USER || "not set"));
310
+ Logger_1.logger.info("Active scope: " + scopeObj.scope);
311
+ if (config.scopes) {
312
+ var scopeNames = Object.keys(config.scopes);
313
+ Logger_1.logger.info("\nConfigured scopes (" + scopeNames.length + "):");
314
+ for (var i = 0; i < scopeNames.length; i++) {
315
+ var scopeName = scopeNames[i];
316
+ var scopeConf = config.scopes[scopeName];
317
+ var srcDir = (typeof scopeConf === "object" && scopeConf.sourceDirectory)
318
+ ? scopeConf.sourceDirectory
319
+ : "src/" + scopeName;
320
+ var marker = scopeName === scopeObj.scope ? " (active)" : "";
321
+ Logger_1.logger.info(" " + scopeName + marker + " — " + srcDir);
322
+ }
323
+ }
324
324
  }
325
325
  catch (e) {
326
326
  throw e;
package/dist/config.js CHANGED
@@ -54,6 +54,9 @@ exports.getRefresh = getRefresh;
54
54
  exports.getDefaultConfigFile = getDefaultConfigFile;
55
55
  exports.loadScopeManifest = loadScopeManifest;
56
56
  exports.updateManifest = updateManifest;
57
+ exports.isMultiScopeManifest = isMultiScopeManifest;
58
+ exports.resolveManifestForScope = resolveManifestForScope;
59
+ exports.resolveScopeFromPath = resolveScopeFromPath;
57
60
  exports.isDirectiveKey = isDirectiveKey;
58
61
  exports.stripDirectiveKeys = stripDirectiveKeys;
59
62
  exports.resolveConfigForScope = resolveConfigForScope;
@@ -290,6 +293,35 @@ async function loadScopeManifest(scope) {
290
293
  function updateManifest(man) {
291
294
  manifest = man;
292
295
  }
296
+ function isMultiScopeManifest(man) {
297
+ return typeof man === "object" && man !== null && !man.scope && !man.tables;
298
+ }
299
+ function resolveManifestForScope(man, scope) {
300
+ if (!isMultiScopeManifest(man)) {
301
+ if (!scope || man.scope === scope) {
302
+ return man;
303
+ }
304
+ return undefined;
305
+ }
306
+ var scopeMan = man[scope];
307
+ if (scopeMan && scopeMan.tables) {
308
+ return scopeMan;
309
+ }
310
+ return undefined;
311
+ }
312
+ function resolveScopeFromPath(filePath) {
313
+ var cfg = getConfig();
314
+ if (!cfg.scopes)
315
+ return undefined;
316
+ var scopeNames = Object.keys(cfg.scopes);
317
+ for (var i = 0; i < scopeNames.length; i++) {
318
+ var scopeSourcePath = getSourcePathForScope(scopeNames[i]);
319
+ if (filePath.indexOf(scopeSourcePath) === 0) {
320
+ return scopeNames[i];
321
+ }
322
+ }
323
+ return undefined;
324
+ }
293
325
  async function loadConfigPath(pth) {
294
326
  if (!pth) {
295
327
  pth = process.cwd();
@@ -102,7 +102,7 @@ async function resolveScope(args) {
102
102
  // Try to get scope from current manifest
103
103
  try {
104
104
  var manifest = ConfigManager.getManifest();
105
- if (manifest && manifest.scope) {
105
+ if (manifest && !ConfigManager.isMultiScopeManifest(manifest) && manifest.scope) {
106
106
  return manifest.scope;
107
107
  }
108
108
  }
@@ -57,7 +57,7 @@ async function resolveScope(args) {
57
57
  // Try to get scope from current manifest
58
58
  try {
59
59
  var manifest = ConfigManager.getManifest();
60
- if (manifest && manifest.scope) {
60
+ if (manifest && !ConfigManager.isMultiScopeManifest(manifest) && manifest.scope) {
61
61
  return manifest.scope;
62
62
  }
63
63
  }
@@ -173,6 +173,34 @@ async function runLoginPhase(plugin, context) {
173
173
  }
174
174
  }
175
175
  }
176
+ async function runConfigPhase(context) {
177
+ if (context.hasConfig) {
178
+ var answer = await inquirer_1.default.prompt([{
179
+ type: "list",
180
+ name: "configAction",
181
+ message: "Existing config found. Would you like to update it or use the current one?",
182
+ choices: [
183
+ { name: "Use current config", value: "keep" },
184
+ { name: "Update config", value: "update" },
185
+ ],
186
+ }]);
187
+ if (answer.configAction === "keep") {
188
+ Logger_1.logger.info(chalk_1.default.green(" ✓ Using existing sinc.config.js"));
189
+ return;
190
+ }
191
+ }
192
+ // TODO: Future config wizard steps:
193
+ // 1. Scopes — multi-select from available scopes
194
+ // 2. Tables — multi-select with search (inquirer-autocomplete)
195
+ // 3. Fields for selected tables
196
+ // 4. Special scope tables
197
+ // 5. Special scope fields for tables
198
+ Logger_1.logger.info("");
199
+ Logger_1.logger.info(chalk_1.default.magenta(" 🎬 Coming soon to a terminal near you!"));
200
+ Logger_1.logger.info(chalk_1.default.dim(" The config wizard is still in development — stay tuned."));
201
+ Logger_1.logger.info(chalk_1.default.dim(" For now, " + (context.hasConfig ? "we'll keep your current config." : "we'll set you up with the defaults.")));
202
+ Logger_1.logger.info("");
203
+ }
176
204
  async function runConfigurePhase(plugin, context) {
177
205
  const hooks = plugin.configure;
178
206
  if (!hooks || hooks.length === 0)
@@ -239,6 +267,11 @@ async function runInit(options) {
239
267
  }
240
268
  // 5. Save env vars after login
241
269
  saveEnvVars(context, selectedPlugins);
270
+ // 5.5 Config phase
271
+ Logger_1.logger.info("");
272
+ Logger_1.logger.info(chalk_1.default.bold(" ── Config " + "─".repeat(29)));
273
+ Logger_1.logger.info("");
274
+ await runConfigPhase(context);
242
275
  // 6. Configure phase
243
276
  Logger_1.logger.info("");
244
277
  Logger_1.logger.info(chalk_1.default.bold(" ── Configure " + "─".repeat(26)));
@@ -4,21 +4,11 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.logBuildResults = exports.logPushResults = exports.log = void 0;
7
- exports.scopeCheckMessage = scopeCheckMessage;
8
- exports.devModeLog = devModeLog;
9
7
  exports.logFilePush = logFilePush;
10
8
  exports.logDeploy = logDeploy;
11
9
  const Logger_1 = require("./Logger");
12
10
  const chalk_1 = __importDefault(require("chalk"));
13
11
  exports.log = console.log;
14
- function scopeCheckMessage(scopeCheck) {
15
- let sScope = chalk_1.default.blue(scopeCheck.sessionScope);
16
- let mScope = chalk_1.default.blue(scopeCheck.manifestScope);
17
- Logger_1.logger.error("Scope mismatch: your session is " + sScope + " but this project targets " + mScope + ". Switch scopes in ServiceNow to continue.");
18
- }
19
- function devModeLog() {
20
- Logger_1.logger.info(`Dev mode started! Watching for changes...[${chalk_1.default.red("Press CTRL-C to Stop")}]\n`);
21
- }
22
12
  function parseError(err) {
23
13
  return `${err.name}:
24
14
  ${err.message}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tenonhq/sincronia-core",
3
- "version": "0.0.73",
3
+ "version": "0.0.74",
4
4
  "description": "Next-gen file syncer",
5
5
  "license": "GPL-3.0",
6
6
  "main": "./dist/index.js",