auth0-deploy-cli 8.30.0 → 8.32.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.
Files changed (82) hide show
  1. package/.circleci/config.yml +17 -5
  2. package/AGENTS.md +21 -1
  3. package/CHANGELOG.md +48 -415
  4. package/CLAUDE.md +3 -0
  5. package/lib/args.d.ts +5 -0
  6. package/lib/args.js +23 -0
  7. package/lib/commands/export.js +5 -1
  8. package/lib/commands/import.js +40 -3
  9. package/lib/context/directory/handlers/actions.js +1 -2
  10. package/lib/context/directory/handlers/clients.js +14 -0
  11. package/lib/context/directory/handlers/connections.js +1 -0
  12. package/lib/context/directory/handlers/guardianFactors.js +8 -6
  13. package/lib/context/directory/handlers/tenant.d.ts +1 -6
  14. package/lib/context/directory/handlers/tenant.js +0 -1
  15. package/lib/context/directory/handlers/triggers.js +1 -3
  16. package/lib/context/index.js +5 -0
  17. package/lib/context/yaml/handlers/clients.js +12 -0
  18. package/lib/context/yaml/handlers/connections.js +1 -17
  19. package/lib/context/yaml/handlers/guardianFactors.js +3 -1
  20. package/lib/context/yaml/handlers/tenant.js +1 -1
  21. package/lib/context/yaml/index.js +2 -1
  22. package/lib/sessionDurationsToMinutes.d.ts +9 -7
  23. package/lib/sessionDurationsToMinutes.js +11 -10
  24. package/lib/tools/auth0/handlers/actions.d.ts +1 -0
  25. package/lib/tools/auth0/handlers/actions.js +36 -0
  26. package/lib/tools/auth0/handlers/attackProtection.js +15 -0
  27. package/lib/tools/auth0/handlers/branding.js +7 -0
  28. package/lib/tools/auth0/handlers/clientGrants.js +32 -4
  29. package/lib/tools/auth0/handlers/clients.d.ts +52 -2
  30. package/lib/tools/auth0/handlers/clients.js +175 -22
  31. package/lib/tools/auth0/handlers/connections.d.ts +1 -1
  32. package/lib/tools/auth0/handlers/connections.js +42 -6
  33. package/lib/tools/auth0/handlers/customDomains.d.ts +5 -0
  34. package/lib/tools/auth0/handlers/customDomains.js +26 -0
  35. package/lib/tools/auth0/handlers/databases.d.ts +91 -0
  36. package/lib/tools/auth0/handlers/databases.js +162 -2
  37. package/lib/tools/auth0/handlers/default.d.ts +4 -0
  38. package/lib/tools/auth0/handlers/default.js +82 -4
  39. package/lib/tools/auth0/handlers/emailProvider.js +8 -0
  40. package/lib/tools/auth0/handlers/emailTemplates.js +7 -0
  41. package/lib/tools/auth0/handlers/flowVaultConnections.js +6 -1
  42. package/lib/tools/auth0/handlers/flows.js +13 -4
  43. package/lib/tools/auth0/handlers/forms.js +10 -1
  44. package/lib/tools/auth0/handlers/guardianFactors.js +7 -1
  45. package/lib/tools/auth0/handlers/guardianPhoneFactorMessageTypes.js +8 -1
  46. package/lib/tools/auth0/handlers/guardianPhoneFactorSelectedProvider.js +8 -1
  47. package/lib/tools/auth0/handlers/guardianPolicies.js +7 -0
  48. package/lib/tools/auth0/handlers/hooks.d.ts +1 -0
  49. package/lib/tools/auth0/handlers/hooks.js +10 -0
  50. package/lib/tools/auth0/handlers/organizations.d.ts +11 -1
  51. package/lib/tools/auth0/handlers/organizations.js +48 -52
  52. package/lib/tools/auth0/handlers/pages.js +7 -0
  53. package/lib/tools/auth0/handlers/phoneProvider.js +7 -0
  54. package/lib/tools/auth0/handlers/prompts.js +7 -0
  55. package/lib/tools/auth0/handlers/resourceServers.d.ts +1 -2
  56. package/lib/tools/auth0/handlers/resourceServers.js +9 -25
  57. package/lib/tools/auth0/handlers/roles.js +7 -0
  58. package/lib/tools/auth0/handlers/rules.d.ts +1 -0
  59. package/lib/tools/auth0/handlers/rules.js +9 -0
  60. package/lib/tools/auth0/handlers/rulesConfigs.d.ts +1 -0
  61. package/lib/tools/auth0/handlers/rulesConfigs.js +3 -0
  62. package/lib/tools/auth0/handlers/scimHandler.js +8 -1
  63. package/lib/tools/auth0/handlers/selfServiceProfiles.d.ts +1 -1
  64. package/lib/tools/auth0/handlers/selfServiceProfiles.js +10 -17
  65. package/lib/tools/auth0/handlers/tenant.d.ts +13 -1
  66. package/lib/tools/auth0/handlers/tenant.js +48 -4
  67. package/lib/tools/auth0/handlers/themes.js +11 -2
  68. package/lib/tools/auth0/handlers/triggers.js +6 -0
  69. package/lib/tools/auth0/handlers/userAttributeProfiles.js +16 -10
  70. package/lib/tools/auth0/index.d.ts +3 -0
  71. package/lib/tools/auth0/index.js +204 -0
  72. package/lib/tools/calculateChanges.d.ts +2 -2
  73. package/lib/tools/calculateChanges.js +21 -6
  74. package/lib/tools/calculateDryRunChanges.d.ts +71 -0
  75. package/lib/tools/calculateDryRunChanges.js +492 -0
  76. package/lib/tools/deploy.js +25 -1
  77. package/lib/tools/utils.d.ts +5 -0
  78. package/lib/tools/utils.js +64 -3
  79. package/lib/types.d.ts +17 -0
  80. package/lib/utils.d.ts +7 -0
  81. package/lib/utils.js +46 -2
  82. package/package.json +10 -6
@@ -45,6 +45,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
45
45
  exports.schema = void 0;
46
46
  const logger_1 = __importDefault(require("../../../logger"));
47
47
  const default_1 = __importStar(require("./default"));
48
+ const utils_1 = require("../../utils");
48
49
  /**
49
50
  * Schema
50
51
  */
@@ -480,11 +481,19 @@ class ThemesHandler extends default_1.default {
480
481
  if (!themes) {
481
482
  return;
482
483
  }
484
+ if ((0, utils_1.isDryRun)(this.config)) {
485
+ const { del, update } = await this.calcChanges(assets);
486
+ if (update.length === 0 && del.length === 0) {
487
+ return;
488
+ }
489
+ }
483
490
  // Empty array means themes should be deleted
484
491
  if (themes.length === 0) {
485
- return this.deleteThemes();
492
+ await this.deleteThemes();
493
+ }
494
+ else {
495
+ await this.updateThemes(themes);
486
496
  }
487
- return this.updateThemes(themes);
488
497
  }
489
498
  async deleteThemes() {
490
499
  if (!this.config('AUTH0_ALLOW_DELETE')) {
@@ -122,6 +122,12 @@ class TriggersHandler extends default_1.default {
122
122
  // Do nothing if not set
123
123
  if (!triggers)
124
124
  return;
125
+ if ((0, utils_1.isDryRun)(this.config)) {
126
+ const { update } = await this.calcChanges(assets);
127
+ if (update.length === 0) {
128
+ return;
129
+ }
130
+ }
125
131
  await (0, utils_1.sleep)(2000); // Delay to allow newly-deployed actions to register in backend
126
132
  await Promise.all(Object.entries(triggers).map(async ([name, data]) => {
127
133
  const bindings = data.map((binding) => ({
@@ -46,7 +46,7 @@ exports.getUserAttributeProfiles = exports.schema = void 0;
46
46
  const default_1 = __importStar(require("./default"));
47
47
  const logger_1 = __importDefault(require("../../../logger"));
48
48
  const client_1 = require("../client");
49
- const calculateChanges_1 = require("../../calculateChanges");
49
+ const utils_1 = require("../../utils");
50
50
  const strategies = ['pingfederate', 'ad', 'adfs', 'waad', 'google-apps', 'okta', 'oidc', 'samlp'];
51
51
  const strategyOverrides = {
52
52
  type: 'object',
@@ -147,7 +147,6 @@ exports.schema = {
147
147
  },
148
148
  scim_mapping: {
149
149
  type: 'string',
150
- default: 'externalId',
151
150
  },
152
151
  },
153
152
  },
@@ -269,14 +268,21 @@ class UserAttributeProfilesHandler extends default_1.default {
269
268
  // Do nothing if not set
270
269
  if (!userAttributeProfiles)
271
270
  return;
272
- const existing = await this.getType();
273
- const changes = (0, calculateChanges_1.calculateChanges)({
274
- handler: this,
275
- assets: userAttributeProfiles,
276
- existing,
277
- identifiers: this.identifiers,
278
- allowDelete: !!this.config('AUTH0_ALLOW_DELETE'),
279
- });
271
+ const { del, update, create, conflicts } = await this.calcChanges(assets);
272
+ if ((0, utils_1.isDryRun)(this.config)) {
273
+ if (create.length === 0 &&
274
+ update.length === 0 &&
275
+ del.length === 0 &&
276
+ conflicts.length === 0) {
277
+ return;
278
+ }
279
+ }
280
+ const changes = {
281
+ del,
282
+ update,
283
+ create,
284
+ conflicts,
285
+ };
280
286
  logger_1.default.debug(`Start processChanges for userAttributeProfile [delete:${changes.del.length}] [update:${changes.update.length}], [create:${changes.create.length}]`);
281
287
  await super.processChanges(assets, changes);
282
288
  }
@@ -12,4 +12,7 @@ export default class Auth0 {
12
12
  validate(): Promise<void>;
13
13
  loadAssetsFromAuth0(): Promise<void>;
14
14
  processChanges(): Promise<void>;
15
+ dryRun(opts?: {
16
+ interactive?: boolean;
17
+ }): Promise<boolean>;
15
18
  }
@@ -4,9 +4,16 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  const ajv_1 = __importDefault(require("ajv/lib/ajv"));
7
+ const lodash_1 = require("lodash");
8
+ const chalk_1 = __importDefault(require("chalk"));
9
+ const prompts_1 = require("@clack/prompts");
10
+ const promise_pool_executor_1 = require("promise-pool-executor");
7
11
  const client_1 = __importDefault(require("./client"));
8
12
  const schema_1 = __importDefault(require("./schema"));
9
13
  const handlers_1 = __importDefault(require("./handlers"));
14
+ const calculateDryRunChanges_1 = require("../calculateDryRunChanges");
15
+ const utils_1 = require("../../utils");
16
+ const utils_2 = require("../utils");
10
17
  /**
11
18
  * Sorts handlers by their @order decorator metadata for a given stage.
12
19
  * Handlers are sorted in ascending order (lower values execute first).
@@ -29,6 +36,30 @@ function sortByOrder(toSort, stage) {
29
36
  });
30
37
  return sorted;
31
38
  }
39
+ function buildTableBorder(left, middle, right, widths) {
40
+ return `${left}${widths.map((width) => '─'.repeat(width + 2)).join(middle)}${right}`;
41
+ }
42
+ function formatStatusCell(status, width) {
43
+ let statusColor;
44
+ const baseStatus = status.replace(' *', '');
45
+ switch (baseStatus) {
46
+ case 'CREATE':
47
+ statusColor = chalk_1.default.green;
48
+ break;
49
+ case 'UPDATE':
50
+ statusColor = chalk_1.default.yellow;
51
+ break;
52
+ case 'DELETE':
53
+ statusColor = chalk_1.default.red;
54
+ break;
55
+ default:
56
+ statusColor = chalk_1.default.white;
57
+ }
58
+ if (status.includes(' *')) {
59
+ return `${statusColor(baseStatus.padEnd(width - 2))}${chalk_1.default.dim(' *')}`;
60
+ }
61
+ return statusColor(baseStatus.padEnd(width));
62
+ }
32
63
  class Auth0 {
33
64
  constructor(client, assets, config) {
34
65
  this.client = (0, client_1.default)(client);
@@ -91,5 +122,178 @@ class Auth0 {
91
122
  async processChanges() {
92
123
  await this.runStage('processChanges');
93
124
  }
125
+ async dryRun(opts = {}) {
126
+ const isDebug = process.env.AUTH0_DEBUG === 'true';
127
+ const s = (0, prompts_1.spinner)();
128
+ if (isDebug) {
129
+ prompts_1.log.info('Preparing dry run preview...\n');
130
+ }
131
+ else {
132
+ s.start('Preparing dry run preview...');
133
+ }
134
+ // Collect changes from all handlers
135
+ const allChanges = {};
136
+ const savedAssets = this.assets;
137
+ this.assets = await (0, calculateDryRunChanges_1.dryRunFormatAssets)((0, lodash_1.cloneDeep)(this.assets), this.client);
138
+ const dryRunPool = new promise_pool_executor_1.PromisePoolExecutor({
139
+ concurrencyLimit: 2,
140
+ frequencyLimit: 8,
141
+ frequencyWindow: 1000, // 1 sec
142
+ });
143
+ await dryRunPool
144
+ .addEachTask({
145
+ data: this.handlers,
146
+ generator: (handler) => (async () => {
147
+ try {
148
+ const detailedChanges = [];
149
+ let created = 0;
150
+ let updated = 0;
151
+ let deleted = 0;
152
+ if (isDebug) {
153
+ prompts_1.log.info(`Calculating dry run changes for ${handler.type}...`);
154
+ }
155
+ else {
156
+ s.message(`Calculating dry run changes for ${handler.type}...`);
157
+ }
158
+ const changes = await handler.dryRunChanges(this.assets);
159
+ if (changes.create) {
160
+ changes.create.forEach((item) => {
161
+ detailedChanges.push({
162
+ action: 'CREATE',
163
+ identifier: handler.getResourceName(item),
164
+ details: item,
165
+ });
166
+ });
167
+ created = changes.create.length;
168
+ }
169
+ if (changes.update) {
170
+ changes.update.forEach((item) => {
171
+ detailedChanges.push({
172
+ action: 'UPDATE',
173
+ identifier: handler.getResourceName(item),
174
+ details: item,
175
+ });
176
+ });
177
+ updated = changes.update.length;
178
+ }
179
+ if (changes.del) {
180
+ changes.del.forEach((item) => {
181
+ detailedChanges.push({
182
+ action: 'DELETE',
183
+ identifier: handler.getResourceName(item),
184
+ details: item,
185
+ });
186
+ });
187
+ deleted = changes.del.length;
188
+ }
189
+ allChanges[handler.type] = {
190
+ created,
191
+ updated,
192
+ deleted,
193
+ changes: detailedChanges,
194
+ };
195
+ }
196
+ catch (err) {
197
+ err.type = handler.type;
198
+ err.stage = 'dryRun';
199
+ throw err;
200
+ }
201
+ })(),
202
+ })
203
+ .promise();
204
+ // Restore original assets so processChanges() works correctly after dryRun()
205
+ this.assets = savedAssets;
206
+ if (!isDebug) {
207
+ (0, utils_2.printCLIMessage)('');
208
+ s.stop('Done\n');
209
+ }
210
+ // Build formatted table output
211
+ const allowDelete = this.config('AUTH0_ALLOW_DELETE') === 'true' || this.config('AUTH0_ALLOW_DELETE') === true;
212
+ const tenantDomain = this.config('AUTH0_DOMAIN');
213
+ const inputPath = this.config('AUTH0_INPUT_FILE') || './tenant-config-directory/';
214
+ let output = '\n';
215
+ output += chalk_1.default.bold('Auth0 Deploy CLI - Dry Run Preview\n\n');
216
+ output += chalk_1.default.bold('Tenant:') + ` ${tenantDomain}\n`;
217
+ output += chalk_1.default.bold('Input:') + ` ${inputPath}\n\n`;
218
+ output += 'Simulating deployment... The following changes are proposed:\n\n';
219
+ const tableData = [];
220
+ const resourceTypes = Object.keys(allChanges).sort();
221
+ resourceTypes.forEach((type) => {
222
+ const typeChanges = allChanges[type];
223
+ if (typeChanges.changes.length > 0) {
224
+ const typeName = type.charAt(0).toUpperCase() + type.slice(1);
225
+ typeChanges.changes.forEach((change, index) => {
226
+ const deleteNote = change.action === 'DELETE' && !allowDelete ? ' *' : '';
227
+ tableData.push({
228
+ resource: index === 0 ? typeName : '',
229
+ status: change.action + deleteNote,
230
+ name: change.identifier,
231
+ });
232
+ });
233
+ }
234
+ });
235
+ if (tableData.length > 0) {
236
+ const resourceWidth = Math.max(10, ...tableData.map((d) => d.resource.length));
237
+ const statusWidth = Math.max(8, ...tableData.map((d) => d.status.length));
238
+ const nameWidth = Math.max(25, ...tableData.map((d) => d.name.length));
239
+ const widths = [resourceWidth, statusWidth, nameWidth];
240
+ const topBorder = buildTableBorder('┌', '┬', '┐', widths);
241
+ const headerSeparator = buildTableBorder('├', '┼', '┤', widths);
242
+ const bottomBorder = buildTableBorder('└', '┴', '┘', widths);
243
+ output += `${topBorder}\n`;
244
+ output += `│ ${chalk_1.default.bold('Resource'.padEnd(resourceWidth))} │ ${chalk_1.default.bold('Status'.padEnd(statusWidth))} │ ${chalk_1.default.bold('Name / Identifier'.padEnd(nameWidth))} │\n`;
245
+ output += `${headerSeparator}\n`;
246
+ tableData.forEach((row) => {
247
+ output += `│ ${row.resource.padEnd(resourceWidth)} │ ${formatStatusCell(row.status, statusWidth)} │ ${row.name.padEnd(nameWidth)} │\n`;
248
+ });
249
+ output += `${bottomBorder}\n`;
250
+ if (!allowDelete && tableData.some((d) => d.status.includes('*'))) {
251
+ output += '\n' + chalk_1.default.dim('* Requires AUTH0_ALLOW_DELETE to be enabled');
252
+ }
253
+ }
254
+ else {
255
+ output += chalk_1.default.dim('No changes detected.');
256
+ }
257
+ output += '\n\n';
258
+ output += chalk_1.default.green('Dry Run completed successfully.') + '\n';
259
+ (0, utils_2.printCLIMessage)(output);
260
+ const hasChanges = tableData.length > 0;
261
+ const shouldApplyAfterPreview = (0, utils_1.isTruthy)(this.config('AUTH0_DRY_RUN_APPLY'));
262
+ if (opts.interactive && process.stdout.isTTY && !shouldApplyAfterPreview && hasChanges) {
263
+ (0, prompts_1.intro)(chalk_1.default.inverse(' dry-run '));
264
+ const selectedType = await (0, prompts_1.select)({
265
+ message: 'What would you like to do?',
266
+ options: [
267
+ { value: 'dry-run-apply', label: 'Apply changes' },
268
+ {
269
+ value: 'dry-run-export',
270
+ label: 'Export changes in a file',
271
+ hint: 'No Apply',
272
+ },
273
+ { value: 'dry-run-exit', label: 'Exit', hint: '' },
274
+ ],
275
+ });
276
+ switch (selectedType) {
277
+ case 'dry-run-apply':
278
+ (0, utils_2.printCLIMessage)('\n' + chalk_1.default.green('Applying changes...') + '\n');
279
+ await this.processChanges();
280
+ break;
281
+ case 'dry-run-export': {
282
+ const fileName = 'dry-run-diff-log.json';
283
+ await (0, calculateDryRunChanges_1.exportDiffLog)(fileName);
284
+ (0, utils_2.printCLIMessage)('\n' + chalk_1.default.cyan(`Exported on ./${fileName}`) + '\n');
285
+ break;
286
+ }
287
+ case 'dry-run-exit':
288
+ (0, utils_2.printCLIMessage)('\n' + chalk_1.default.yellow('Deployment cancelled. No changes were made.') + '\n');
289
+ process.exit(0);
290
+ break;
291
+ default:
292
+ (0, utils_2.printCLIMessage)(chalk_1.default.red('Invalid option selected.'));
293
+ process.exit(1);
294
+ }
295
+ }
296
+ return hasChanges;
297
+ }
94
298
  }
95
299
  exports.default = Auth0;
@@ -1,4 +1,4 @@
1
- import APIHandler from '../tools/auth0/handlers/default';
1
+ import APIHandler from './auth0/handlers/default';
2
2
  import { Asset, CalculatedChanges } from '../types';
3
3
  /**
4
4
  * @template T
@@ -20,7 +20,7 @@ export declare function processChangedObjectFields({ handler, desiredAssetState,
20
20
  export declare function calculateChanges({ handler, assets, existing, identifiers, allowDelete, }: {
21
21
  handler: APIHandler;
22
22
  assets: Asset[];
23
- existing: Asset[] | null;
23
+ existing: Asset[] | Asset | null;
24
24
  identifiers: string[];
25
25
  allowDelete: boolean;
26
26
  }): CalculatedChanges;
@@ -67,8 +67,14 @@ function processChangedObjectFields({ handler, desiredAssetState, currentAssetSt
67
67
  function calculateChanges({ handler, assets, existing, identifiers = ['id', 'name'], allowDelete, }) {
68
68
  // Calculate the changes required between two sets of assets.
69
69
  const update = [];
70
- let del = [...(existing || [])];
71
- let create = [...assets];
70
+ let del = [];
71
+ if (existing) {
72
+ del = Array.isArray(existing) ? [...existing] : [existing];
73
+ }
74
+ let create = [];
75
+ if (assets) {
76
+ create = Array.isArray(assets) ? [...assets] : [assets];
77
+ }
72
78
  const conflicts = [];
73
79
  const findByKeyValue = (key, value, arr) => arr.find((e) => {
74
80
  if (Array.isArray(key)) {
@@ -124,10 +130,9 @@ function calculateChanges({ handler, assets, existing, identifiers = ['id', 'nam
124
130
  // Loop through identifiers (in order) to try match assets to existing
125
131
  // If existing then update if not create
126
132
  // The remainder will be deleted
127
- for (const id of identifiers) {
128
- // eslint-disable-line
133
+ identifiers.forEach((id) => {
129
134
  processAssets(id, [...create]);
130
- }
135
+ });
131
136
  // Check if there are assets with names that will conflict with existing names during the update process
132
137
  // This will rename those assets to a temp random name first
133
138
  // This assumes the first identifiers is the unique identifier
@@ -138,7 +143,17 @@ function calculateChanges({ handler, assets, existing, identifiers = ['id', 'nam
138
143
  // If the conflicting item is going to be deleted then skip
139
144
  const inDeleted = del.filter((e) => e.name === a.name && e[uniqueID] !== a[uniqueID])[0];
140
145
  if (!inDeleted) {
141
- const conflict = (existing || []).filter((e) => e.name === a.name && e[uniqueID] !== a[uniqueID])[0];
146
+ let existingArray;
147
+ if (Array.isArray(existing)) {
148
+ existingArray = existing;
149
+ }
150
+ else if (existing) {
151
+ existingArray = [existing];
152
+ }
153
+ else {
154
+ existingArray = [];
155
+ }
156
+ const conflict = existingArray.filter((e) => e.name === a.name && e[uniqueID] !== a[uniqueID])[0];
142
157
  if (conflict) {
143
158
  const temp = Math.random().toString(36).substr(2, 5);
144
159
  conflicts.push({
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Dry-run change calculation utilities.
3
+ *
4
+ * This module handles all logic specific to the --dry-run mode:
5
+ * - Comparing local vs. remote assets to detect creates, updates, and deletes
6
+ * - Normalizing asset representations (session durations, client name→ID, profiles)
7
+ * - Tracking and exporting a structured diff log
8
+ *
9
+ * The core deploy-time change calculation lives in calculateChanges.ts.
10
+ */
11
+ import { Asset, Assets, Auth0APIClient, CalculatedChanges } from '../types';
12
+ export declare const getDiffLog: (resourceTypeName?: string) => string[] | {
13
+ [key: string]: string[];
14
+ };
15
+ /**
16
+ * Writes the accumulated diff log to a JSON file on disk.
17
+ * Useful for CI pipelines that want a machine-readable dry-run report.
18
+ */
19
+ export declare const exportDiffLog: (fileName: string, resourceTypeName?: string) => Promise<void>;
20
+ /**
21
+ * Compares two objects and returns an array of human-readable difference strings.
22
+ * Only considers keys present in `localObj` — extra keys in `remoteObj` are ignored.
23
+ *
24
+ * @param localObj - Local (desired) state
25
+ * @param remoteObj - Remote (current) state
26
+ * @param keyObjPath - Dot-separated JSON path used for nested tracking
27
+ * @param resourceTypeName - Resource type label used in log output
28
+ * @param ignoreDryRunFields - Field paths to skip during comparison
29
+ * @returns Array of difference descriptions, empty if objects are equivalent
30
+ */
31
+ export declare function getObjectDifferences(localObj: Record<string, any>, remoteObj: Record<string, any>, keyObjPath?: string, resourceTypeName?: string, ignoreDryRunFields?: string[]): string[];
32
+ /**
33
+ * Returns true if `localObj` and `remoteObj` differ in any field present in
34
+ * `localObj`. Side-effect: appends found differences to the diff log.
35
+ */
36
+ export declare function hasObjectDifferences(localObj: Record<string, any>, remoteObj: Record<string, any>, keyObjPath?: string, resourceTypeName?: string, ignoreDryRunFields?: string[]): boolean;
37
+ /**
38
+ * Calculates the changes required between local and remote asset sets for dry-run operations.
39
+ *
40
+ * Unlike `calculateChanges`, this function does not mutate assets for API consumption.
41
+ * It is purely for reporting: determining which assets would be created, updated, or deleted.
42
+ *
43
+ * @param params.type - Resource type label used for logging
44
+ * @param params.assets - Local assets to be deployed
45
+ * @param params.existing - Remote assets currently on the tenant, or null
46
+ * @param params.identifiers - Fields used to match local↔remote assets (default: ['id', 'name'])
47
+ * @param params.ignoreDryRunFields - Fields to exclude from diff comparisons
48
+ */
49
+ export declare function calculateDryRunChanges({ type, assets, existing, identifiers, ignoreDryRunFields, }: {
50
+ type: string;
51
+ assets: Asset[] | Asset;
52
+ existing: Asset[] | Asset | null;
53
+ identifiers: string[];
54
+ ignoreDryRunFields: string[];
55
+ }): CalculatedChanges;
56
+ /**
57
+ * Normalizes local assets so they can be accurately compared against remote assets.
58
+ *
59
+ * Transformations applied:
60
+ * - Converts client name references to client IDs (clientGrants, databases, connections, resourceServers)
61
+ * - Resolves connection profile and user attribute profile names to IDs (express_configuration)
62
+ * - Decodes base64-encoded SAML certificates to PEM strings
63
+ * - Resolves organization connection names to connection IDs
64
+ * - Normalizes tenant session duration fields
65
+ * - Removes empty branding templates (avoids false diff against remote)
66
+ *
67
+ * @param localAssets - Local assets object (will be mutated)
68
+ * @param authAPIclient - Auth0 management API client for fetching remote reference data
69
+ * @returns The mutated assets object with normalized values
70
+ */
71
+ export declare function dryRunFormatAssets(localAssets: Assets, authAPIclient: Auth0APIClient): Promise<Assets>;