@adminide-stack/form-builder-core 5.1.4-alpha.82 → 5.1.6-alpha.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 (60) hide show
  1. package/LICENSE +30 -21
  2. package/lib/config/__tests__/connectorFieldTokenResolver.test.d.ts +2 -0
  3. package/lib/config/__tests__/connectorFieldTokenResolver.test.d.ts.map +1 -0
  4. package/lib/config/cdnConfig.d.ts +17 -0
  5. package/lib/config/cdnConfig.d.ts.map +1 -0
  6. package/lib/config/cdnConfig.js +29 -0
  7. package/lib/config/cdnConfig.js.map +1 -0
  8. package/lib/config/configTypes.d.ts +220 -0
  9. package/lib/config/configTypes.d.ts.map +1 -0
  10. package/lib/config/configTypes.js +6 -0
  11. package/lib/config/configTypes.js.map +1 -0
  12. package/lib/config/connectorFieldTokenResolver.d.ts +11 -0
  13. package/lib/config/connectorFieldTokenResolver.d.ts.map +1 -0
  14. package/lib/config/connectorFieldTokenResolver.js +95 -0
  15. package/lib/config/connectorFieldTokenResolver.js.map +1 -0
  16. package/lib/config/connectorsConfig.d.ts +3 -0
  17. package/lib/config/connectorsConfig.d.ts.map +1 -0
  18. package/lib/config/connectorsConfig.js +44533 -0
  19. package/lib/config/connectorsConfig.js.map +1 -0
  20. package/lib/config/connectorsConfig2.d.ts +11 -0
  21. package/lib/config/connectorsConfig2.d.ts.map +1 -0
  22. package/lib/config/llmProviderConfig.d.ts +3 -0
  23. package/lib/config/llmProviderConfig.d.ts.map +1 -0
  24. package/lib/config/llmProviderConfig.js +457 -0
  25. package/lib/config/llmProviderConfig.js.map +1 -0
  26. package/lib/config/mcpConnectorConfig.d.ts +3 -0
  27. package/lib/config/mcpConnectorConfig.d.ts.map +1 -0
  28. package/lib/config/mcpConnectorConfig.js +961 -0
  29. package/lib/config/mcpConnectorConfig.js.map +1 -0
  30. package/lib/index.d.ts +11 -2
  31. package/lib/index.d.ts.map +1 -1
  32. package/lib/index.js +1 -1
  33. package/lib/inngest/generateFunctionCode.js +1 -1
  34. package/lib/utils/__tests__/deepMergeFormSteps.test.d.ts +2 -0
  35. package/lib/utils/__tests__/deepMergeFormSteps.test.d.ts.map +1 -0
  36. package/lib/utils/authMetaDataSchema.d.ts +199 -0
  37. package/lib/utils/authMetaDataSchema.d.ts.map +1 -0
  38. package/lib/utils/authMetaDataSchema.js +1601 -0
  39. package/lib/utils/authMetaDataSchema.js.map +1 -0
  40. package/lib/utils/deepMergeFormSteps.d.ts +15 -0
  41. package/lib/utils/deepMergeFormSteps.d.ts.map +1 -0
  42. package/lib/utils/deepMergeFormSteps.js +64 -0
  43. package/lib/utils/deepMergeFormSteps.js.map +1 -0
  44. package/lib/utils/schemaHelpers.d.ts +85 -0
  45. package/lib/utils/schemaHelpers.d.ts.map +1 -0
  46. package/lib/utils/schemaHelpers.js +473 -0
  47. package/lib/utils/schemaHelpers.js.map +1 -0
  48. package/package.json +3 -2
  49. package/CHANGELOG.md +0 -60
  50. package/rollup.config.mjs +0 -33
  51. package/src/index.ts +0 -5
  52. package/src/inngest/MONACO_INTEGRATION_EXAMPLE.md +0 -406
  53. package/src/inngest/README_AUTOCOMPLETE.md +0 -380
  54. package/src/inngest/generateFunctionCode.ts +0 -248
  55. package/src/inngest/interfaces/index.ts +0 -1
  56. package/src/inngest/interfaces/types.ts +0 -63
  57. package/src/inngest/monacoAutocompleteIntegration.ts +0 -432
  58. package/src/inngest/stepGenerator.ts +0 -538
  59. package/src/utils/json.ts +0 -50
  60. package/tsconfig.json +0 -14
@@ -0,0 +1,1601 @@
1
+ import {CONTRIBUTION_POINTS,ContributeDefaultValueSourceType}from'common';import {resolveConnectorFieldTokensInValue}from'../config/connectorFieldTokenResolver.js';import {parseSampleNodes,flattenSchemas}from'./schemaHelpers.js';const parseScopes = raw => raw.split(',').map(s => s.trim()).filter(Boolean);
2
+ const parseEnumValues = raw => {
3
+ if (Array.isArray(raw)) return raw;
4
+ if (raw === null || raw === undefined) return [];
5
+ if (typeof raw !== 'string') return [];
6
+ if (!raw.trim()) return [];
7
+ try {
8
+ const parsed = JSON.parse(raw);
9
+ return Array.isArray(parsed) ? parsed : [];
10
+ } catch (_) {
11
+ return raw.split('\n').map(line => line.trim()).filter(Boolean).map(line => {
12
+ const [value, label] = line.split(':');
13
+ return {
14
+ value: (value || '').trim(),
15
+ label: (label || value || '').trim()
16
+ };
17
+ });
18
+ }
19
+ };
20
+ const toEnvKey = value => (value || '').replace(/([a-z0-9])([A-Z])/g, '$1_$2').replace(/([A-Z]+)([A-Z][a-z])/g, '$1_$2').replace(/[^a-zA-Z0-9]+/g, '_').replace(/^_+|_+$/g, '').toUpperCase();
21
+ const mapProviderFields = (fields, providerId) => fields.map(field => ({
22
+ name: field.name,
23
+ label: field.label,
24
+ type: field.type,
25
+ placeholder: field.placeholder,
26
+ description: field.description,
27
+ required: field.required,
28
+ defaultValue: field.defaultValue ?? '',
29
+ isConfigField: field.isConfigField,
30
+ isSecret: field.isSecret,
31
+ secretSource: field.isSecret ? field.secretSource || {
32
+ type: 'vault',
33
+ secretType: field.secretType,
34
+ secretKey: field.secretKey || (providerId ? `${toEnvKey(providerId)}_${toEnvKey(field.name)}` : toEnvKey(field.name))
35
+ } : null,
36
+ uiWidget: field.uiWidget,
37
+ enumValues: parseEnumValues(field.enumValues),
38
+ readOnly: field.readOnly,
39
+ iconType: field.iconType,
40
+ genericType: field.genericType || undefined,
41
+ placement: field.placement || undefined,
42
+ placementKey: field.placementKey || undefined,
43
+ scope: field.scope || undefined
44
+ }));
45
+ const buildProviderConfig = (providerConfig, connectorType) => {
46
+ const base = {
47
+ id: providerConfig.id,
48
+ title: providerConfig.title,
49
+ description: providerConfig.description,
50
+ icon: providerConfig.icon || {
51
+ name: providerConfig.iconName,
52
+ style: providerConfig.iconFontSize ? {
53
+ fontSize: providerConfig.iconFontSize
54
+ } : undefined
55
+ },
56
+ logo: providerConfig.logo || undefined,
57
+ color: providerConfig.color || undefined,
58
+ enabled: providerConfig.enabled,
59
+ scopes: Array.isArray(providerConfig.scopes) ? providerConfig.scopes : parseScopes(providerConfig.scopes),
60
+ authMethods: providerConfig.authMethods.length > 0 ? providerConfig.authMethods : ['api_key'],
61
+ categories: providerConfig.categories || [providerConfig.category],
62
+ fields: mapProviderFields(providerConfig.fields, providerConfig.id)
63
+ };
64
+ if (connectorType === 'mcp') {
65
+ return {
66
+ ...base,
67
+ providerUrl: providerConfig.providerUrl || undefined,
68
+ mcpUrl: providerConfig.mcpUrl
69
+ };
70
+ }
71
+ return {
72
+ ...base,
73
+ providerUrl: providerConfig.providerUrl,
74
+ useCustomOAuthEndpoints: providerConfig.useCustomOAuthEndpoints,
75
+ usePKCE: providerConfig.usePKCE,
76
+ clientAuthMethod: providerConfig.clientAuthMethod,
77
+ authorizationUrl: providerConfig.authorizationUrl,
78
+ tokenUrl: providerConfig.tokenUrl,
79
+ revocationUrl: providerConfig.revocationUrl,
80
+ useBasicAuth: providerConfig.useBasicAuth
81
+ };
82
+ };
83
+ const buildAppSeed = p => {
84
+ const authMethods = p.authMethods ? [...p.authMethods] : ['oauth2'];
85
+ return {
86
+ key: `${p.id}:app`,
87
+ providerId: p.id,
88
+ title: p.title,
89
+ description: p.description || '',
90
+ scopes: p.scopes || [],
91
+ providerUrl: p.providerUrl || '',
92
+ useCustomOAuthEndpoints: p.useCustomOAuthEndpoints,
93
+ usePKCE: p.usePKCE,
94
+ clientAuthMethod: p.clientAuthMethod,
95
+ authorizationUrl: p.authorizationUrl,
96
+ tokenUrl: p.tokenUrl,
97
+ revocationUrl: p.revocationUrl,
98
+ useBasicAuth: p.useBasicAuth,
99
+ mcpUrl: p.mcpUrl || '',
100
+ connectorType: 'app',
101
+ authMethods,
102
+ fields: p.fields || [],
103
+ resources: p.resources || [],
104
+ customButtons: p.customButtons || [],
105
+ formIcon: p.logo || '',
106
+ categories: p.categories
107
+ };
108
+ };
109
+ const buildMcpSeed = p => ({
110
+ key: `${p.id}:mcp`,
111
+ providerId: p.id,
112
+ title: p.title,
113
+ description: p.description || '',
114
+ scopes: p.scopes || [],
115
+ providerUrl: p.mcpUrl || '',
116
+ mcpUrl: p.mcpUrl || '',
117
+ connectorType: 'mcp',
118
+ authMethods: [...p.authMethods],
119
+ fields: p.fields || [],
120
+ resources: [],
121
+ customButtons: [],
122
+ formIcon: p.logo || '',
123
+ categories: p.categories
124
+ });
125
+ function buildProviderSeedFromConfig(provider, connectorType) {
126
+ if (connectorType === 'mcp') {
127
+ return buildMcpSeed(provider);
128
+ }
129
+ return buildAppSeed(provider);
130
+ }
131
+ // Auth seed generation
132
+ const sanitizeFieldKey = value => value.replace(/[^a-zA-Z0-9_]/g, '_');
133
+ const seedIsApiKeyOnly = seed => seed.connectorType !== 'mcp' && !seed.authMethods.includes('oauth2') && !seed.authMethods.includes('bearer_token');
134
+ const STEP_KEY = 'step1';
135
+ const seedTemplateType = seed => seed.connectorType === 'mcp' ? 'mcp' : seed.authMethods.includes('oauth2') ? 'oauth' : 'api';
136
+ function buildResourcesNode(seed) {
137
+ if (seed.resources.length === 0) return null;
138
+ const properties = {};
139
+ for (const originalResource of seed.resources) {
140
+ const resource = resolveConnectorFieldTokensInValue(originalResource, {
141
+ fields: seed.fields,
142
+ basePath: `${STEP_KEY}.oauthConnector`,
143
+ context: `${seed.key}.${originalResource.name}`
144
+ });
145
+ const prefix = resource.name;
146
+ const selectedSource = resource.type === 'jsCode' ? 'js-code' : resource.type;
147
+ properties[`${prefix}.id`] = resource.name;
148
+ properties[`${prefix}.name`] = resource.label;
149
+ properties[`${prefix}.type`] = resource.type === 'rest-api' ? 'apiRequest' : resource.type;
150
+ properties[`${prefix}.selectedSource`] = selectedSource;
151
+ properties[`${prefix}.description`] = '';
152
+ properties[`${prefix}.key`] = resource.name;
153
+ if (resource.type === 'rest-api' && resource.config.restApi) {
154
+ const r = resource.config.restApi;
155
+ const rp = `${prefix}.config.restApi`;
156
+ properties[`${rp}.httpMethod`] = r.httpMethod;
157
+ properties[`${rp}.url`] = r.url;
158
+ properties[`${rp}.urlParams`] = r.urlParams;
159
+ r.headers.forEach((h, i) => {
160
+ properties[`${rp}.headers.${i}.key`] = h.key;
161
+ properties[`${rp}.headers.${i}.value`] = h.value;
162
+ });
163
+ properties[`${rp}.body.format`] = r.body.format;
164
+ properties[`${rp}.body.data`] = r.body.data;
165
+ properties[`${rp}.body.rawData`] = r.body.rawData;
166
+ properties[`${rp}.cookies`] = r.cookies;
167
+ properties[`${rp}.runBehavior`] = r.runBehavior;
168
+ properties[`${rp}.transformResults`] = r.transformResults;
169
+ }
170
+ if (resource.type === 'jsCode' && resource.config.jsCode) {
171
+ const j = resource.config.jsCode;
172
+ const jp = `${prefix}.config.jsCode`;
173
+ properties[`${jp}.query`] = j.query;
174
+ properties[`${jp}.transformResults`] = j.transformResults;
175
+ }
176
+ if (resource.type === 'graphql' && resource.config.graphql) {
177
+ const g = resource.config.graphql;
178
+ const gp = `${prefix}.config.graphql`;
179
+ properties[`${gp}.query`] = g.query;
180
+ properties[`${gp}.useInternalLink`] = g.useInternalLink;
181
+ properties[`${gp}.transformResults`] = g.transformResults;
182
+ properties[`${gp}.variables`] = g.variables;
183
+ }
184
+ }
185
+ return {
186
+ type: 'resources',
187
+ properties
188
+ };
189
+ }
190
+ function buildCustomButtonsNode(seed) {
191
+ if (seed.customButtons.length === 0) return null;
192
+ const node = {
193
+ type: 'customButtons'
194
+ };
195
+ seed.customButtons.forEach((btn, idx) => {
196
+ const base = `properties.${STEP_KEY}.${idx}`;
197
+ node[`${base}.id`] = btn.id || `btn-${Math.random().toString(36).substring(2, 15)}`;
198
+ node[`${base}.title`] = btn.title;
199
+ node[`${base}.action`] = btn.action;
200
+ if (btn.resourceId) node[`${base}.resourceId`] = btn.resourceId;
201
+ if (btn.inngestId) node[`${base}.inngestId`] = btn.inngestId;
202
+ });
203
+ return node;
204
+ }
205
+ function buildInngestNode() {
206
+ return {
207
+ type: 'inngest',
208
+ 'properties.generatedCode.submit': `async ({ event, step }) => {\nreturn {\n functionId: 'new-function',\n timestamp: new Date().toISOString()\n};\n}`,
209
+ 'properties.raw.submit.functionID': 'new-function',
210
+ 'properties.raw.submit.functionName': 'New Workflow Function',
211
+ 'properties.raw.submit.functionDescription': 'A new Workflow function',
212
+ 'properties.raw.submit.enabled': false,
213
+ 'properties.raw.submit.displaySubmitButton': false,
214
+ 'properties.raw.submit.events.0': 'app.event',
215
+ 'properties.raw.submit.metadata.version': '1.0.0',
216
+ 'properties.raw.submit.selectedStep': null,
217
+ 'properties.raw.submit.activeTab': 'config',
218
+ 'properties.raw.submit.editorSettings.theme': 'vs-dark',
219
+ 'properties.raw.submit.editorSettings.fontSize': 14,
220
+ 'properties.raw.submit.editorSettings.tabSize': 2,
221
+ 'properties.raw.submit.editorSettings.wordWrap': true,
222
+ 'properties.raw.submit.generatedCode': `async ({ event, step }) => {\nreturn {\n functionId: 'new-function',\n timestamp: new Date().toISOString()\n};\n}`,
223
+ 'properties.raw.submit.isCodeManuallyEdited': false,
224
+ 'properties.raw.submit.stepEditorStates.activeTab': 'config',
225
+ 'properties.raw.submit.version': '1.0.0'
226
+ };
227
+ }
228
+ const seedCategory = seed => seed.categories[0];
229
+ const toVaultPrefix = value => value.replace(/[^a-zA-Z0-9]/g, '_').toUpperCase();
230
+ const seedAuthMethodDefault = seed => seed.authMethods.includes('oauth2') ? 'oauth' : seed.connectorType === 'mcp' ? 'mcp' : 'api_key';
231
+ /**
232
+ * Build a `genericType -> count` map for the given fields. Used by
233
+ * {@link resolveFieldKey} to detect colliding `genericType` values so that
234
+ * generated config paths (e.g. `oauthConnector.info`) don't clash when
235
+ * multiple fields declare the same generic bucket.
236
+ */
237
+ const buildGenericTypeCounts = fields => {
238
+ const counts = new Map();
239
+ if (!fields) return counts;
240
+ for (const field of fields) {
241
+ const gt = field?.genericType;
242
+ if (gt) counts.set(gt, (counts.get(gt) || 0) + 1);
243
+ }
244
+ return counts;
245
+ };
246
+ /**
247
+ * Reserved `genericType` value that means "this is a custom config field —
248
+ * route it under `oauthConnector.additionalKeys.<fieldName>`". Use this for
249
+ * any field that doesn't fit a canonical genericType (api_key, base_url,
250
+ * username, subdomain, etc.). Each field gets its own leaf path, so an
251
+ * arbitrary number of additional keys can coexist without collision.
252
+ */
253
+ const ADDITIONAL_KEYS_GENERIC_TYPE = 'additionalKeys';
254
+ /**
255
+ * Resolve the path-segment for a config field. Rules (first match wins):
256
+ * 1. `genericType === 'additionalKeys'` → `additionalKeys.<field.name>` (namespaced)
257
+ * 2. unique `genericType` → that value
258
+ * 3. otherwise → `field.name` (collision fallback for legacy connectors)
259
+ */
260
+ const resolveFieldKey = (field, genericTypeCounts) => {
261
+ const gt = field?.genericType;
262
+ if (gt === ADDITIONAL_KEYS_GENERIC_TYPE) return `${ADDITIONAL_KEYS_GENERIC_TYPE}.${field.name}`;
263
+ if (gt && (genericTypeCounts.get(gt) || 0) <= 1) return gt;
264
+ return field.name;
265
+ };
266
+ function buildPreGeneratedFormSteps(seed, namespace) {
267
+ const vaultPrefix = toVaultPrefix(seed.providerId);
268
+ const isApp = seed.connectorType === 'app';
269
+ const isApiKeyOnly = seedIsApiKeyOnly(seed);
270
+ const templateType = seedTemplateType(seed);
271
+ const fullPath = `integrationSettings.settings.${templateType}.${namespace}.oauthConnector`;
272
+ if (isApiKeyOnly) {
273
+ // api_key-only: oauthConnector element with minimal children (so widget renders),
274
+ // plus custom field elements with full secret/config metadata.
275
+ const minimalChildren = [{
276
+ id: 'provider',
277
+ type: 'string',
278
+ label: 'Provider',
279
+ default: seed.providerId,
280
+ scope: 2 /* ConfigurationScope.MACHINE */,
281
+ readOnly: true
282
+ }, {
283
+ id: 'providerTitle',
284
+ type: 'string',
285
+ label: 'Provider Title',
286
+ default: seed.title,
287
+ scope: 2 /* ConfigurationScope.MACHINE */,
288
+ readOnly: true
289
+ }, {
290
+ id: 'providerUrl',
291
+ type: 'string',
292
+ label: 'Provider URL',
293
+ default: seed.providerUrl,
294
+ scope: 2 /* ConfigurationScope.MACHINE */,
295
+ readOnly: true
296
+ }, {
297
+ id: 'description',
298
+ type: 'string',
299
+ label: 'Description',
300
+ scope: 2 /* ConfigurationScope.MACHINE */,
301
+ default: seed.description,
302
+ readOnly: true
303
+ }, {
304
+ id: 'connectorType',
305
+ type: 'string',
306
+ label: 'Connector Type',
307
+ default: seed.connectorType,
308
+ scope: 2 /* ConfigurationScope.MACHINE */,
309
+ readOnly: true
310
+ }, {
311
+ id: 'authMethod',
312
+ type: 'string',
313
+ label: 'Auth Method',
314
+ default: 'api_key',
315
+ scope: 2 /* ConfigurationScope.MACHINE */,
316
+ readOnly: true
317
+ }];
318
+ const minimalUiOverrides = {
319
+ provider: {
320
+ 'ui:readonly': true
321
+ },
322
+ providerTitle: {
323
+ 'ui:readonly': true
324
+ },
325
+ providerUrl: {
326
+ 'ui:readonly': true
327
+ },
328
+ description: {
329
+ 'ui:readonly': true
330
+ },
331
+ connectorType: {
332
+ 'ui:widget': 'hidden'
333
+ },
334
+ authMethod: {}
335
+ };
336
+ const elements = [{
337
+ id: 'oauthConnector',
338
+ type: 'object',
339
+ label: 'OAuth Connector',
340
+ required: false,
341
+ description: seed.description || 'Connect to external providers',
342
+ connectorType: seed.connectorType,
343
+ widget: 'OAuthConnectorWidget',
344
+ uiField: 'OAuthConnectorField',
345
+ children: minimalChildren,
346
+ _widgetUiOverrides: minimalUiOverrides,
347
+ connectorProvider: seed.providerId,
348
+ connectorProviderTitle: seed.title,
349
+ connectorDescription: seed.description,
350
+ connectorCategory: seedCategory(seed),
351
+ ...(seed.connectorType === 'mcp' ? {
352
+ connectorMcpUrl: seed.providerUrl
353
+ } : {
354
+ connectorProviderUrl: seed.providerUrl
355
+ }),
356
+ connectorScopes: seed.scopes,
357
+ useCustomOAuthEndpoints: seed.useCustomOAuthEndpoints ?? false,
358
+ connectorAuthorizationUrl: seed.authorizationUrl || '',
359
+ connectorTokenUrl: seed.tokenUrl || '',
360
+ connectorRevocationUrl: seed.revocationUrl || '',
361
+ useBasicAuth: seed.useBasicAuth ?? false,
362
+ connectorRedirectUri: '{{CLIENT_URL}}/{{CONNECTOR_ENDPOINT}}',
363
+ // connectorAuthMethod — present only for non-oauth connectors (matches reference forms)
364
+ ...(!seed.authMethods.includes('oauth2') ? {
365
+ connectorAuthMethod: seedAuthMethodDefault(seed)
366
+ } : {}),
367
+ ...(seed.fields.length > 0 ? {
368
+ connectorProviderFields: seed.fields
369
+ } : {}),
370
+ ...(seed.resources.length > 0 ? {
371
+ connectorProviderResources: seed.resources
372
+ } : {}),
373
+ ...(seed.customButtons.length > 0 ? {
374
+ connectorProviderCustomButtons: seed.customButtons
375
+ } : {}),
376
+ addedFromStepperConfigToggle: true,
377
+ isSchemaElement: true,
378
+ sourceSchemaId: 'configuration',
379
+ fullPath,
380
+ contributionPoint: 'configuration.integrationSettings',
381
+ templateType,
382
+ namespace
383
+ }];
384
+ return [{
385
+ id: STEP_KEY,
386
+ title: 'Step 1',
387
+ elements
388
+ }];
389
+ }
390
+ // Full OAuth children
391
+ const children = [{
392
+ id: 'clientId',
393
+ type: 'string',
394
+ label: 'Client ID',
395
+ description: 'OAuth application client identifier',
396
+ format: 'secret',
397
+ readOnly: true,
398
+ default: '',
399
+ scope: isApp ? 6 /* ConfigurationScope.MACHINE_OVERRIDABLE */ : 1 /* ConfigurationScope.APPLICATION */,
400
+ schemaExtensions: {
401
+ defaultValueSource: {
402
+ type: ContributeDefaultValueSourceType.Vault,
403
+ secretType: 'OAUTH_CLIENT',
404
+ secretKey: `${vaultPrefix}_CLIENT_ID`,
405
+ ...(isApp ? {
406
+ requiredSignature: true
407
+ } : {})
408
+ }
409
+ }
410
+ }, {
411
+ id: 'clientSecret',
412
+ type: 'string',
413
+ label: 'Client Secret',
414
+ description: 'OAuth application client secret',
415
+ format: 'secret',
416
+ readOnly: true,
417
+ default: '',
418
+ scope: isApp ? 6 /* ConfigurationScope.MACHINE_OVERRIDABLE */ : 1 /* ConfigurationScope.APPLICATION */,
419
+ schemaExtensions: {
420
+ defaultValueSource: {
421
+ type: ContributeDefaultValueSourceType.Vault,
422
+ secretType: 'OAUTH_CLIENT',
423
+ secretKey: `${vaultPrefix}_CLIENT_SECRET`,
424
+ ...(isApp ? {
425
+ requiredSignature: true
426
+ } : {})
427
+ }
428
+ }
429
+ }, {
430
+ id: 'apiKey',
431
+ type: 'string',
432
+ label: 'API Key',
433
+ description: 'API Key for MCP integration',
434
+ format: 'secret',
435
+ writeOnly: true,
436
+ default: '',
437
+ scope: 4 /* ConfigurationScope.RESOURCE */,
438
+ schemaExtensions: {
439
+ defaultValueSource: {
440
+ type: ContributeDefaultValueSourceType.Vault,
441
+ secretType: 'API_KEY',
442
+ secretKey: `${vaultPrefix}_API_KEY`
443
+ }
444
+ }
445
+ }, {
446
+ id: 'accessToken',
447
+ type: 'string',
448
+ label: 'Access Token',
449
+ format: 'secret',
450
+ writeOnly: true,
451
+ default: '',
452
+ readOnly: true,
453
+ scope: 1 /* ConfigurationScope.APPLICATION */,
454
+ schemaExtensions: {
455
+ defaultValueSource: {
456
+ type: ContributeDefaultValueSourceType.Vault,
457
+ secretType: 'OAUTH_CLIENT',
458
+ secretKey: `${vaultPrefix}_ACCESS_TOKEN`
459
+ }
460
+ }
461
+ }, {
462
+ id: 'refreshToken',
463
+ type: 'string',
464
+ label: 'Refresh Token',
465
+ format: 'secret',
466
+ writeOnly: true,
467
+ default: '',
468
+ readOnly: true,
469
+ scope: 1 /* ConfigurationScope.APPLICATION */,
470
+ schemaExtensions: {
471
+ defaultValueSource: {
472
+ type: ContributeDefaultValueSourceType.Vault,
473
+ secretType: 'OAUTH_CLIENT',
474
+ secretKey: `${vaultPrefix}_REFRESH_TOKEN`
475
+ }
476
+ }
477
+ }, {
478
+ id: 'expiresAt',
479
+ type: 'number',
480
+ label: 'Expires At',
481
+ description: 'Access token expiry as Unix timestamp in milliseconds; 0 if unset',
482
+ default: 0,
483
+ readOnly: true,
484
+ scope: 1 /* ConfigurationScope.APPLICATION */
485
+ }, {
486
+ id: 'urlTemplateKeyValues',
487
+ type: 'string',
488
+ label: 'URL Template Key Values',
489
+ description: 'Stringified JSON of resolved values for URL template placeholders (e.g. {"zoho-region":"eu"})',
490
+ default: '{}',
491
+ readOnly: true,
492
+ scope: 1 /* ConfigurationScope.APPLICATION */
493
+ }, {
494
+ id: 'authRequestMeta',
495
+ type: 'string',
496
+ label: 'Auth Request Metadata',
497
+ description: 'Serialized OAuth authorization-request metadata (e.g. PKCE verifier, state) for session continuity',
498
+ default: '',
499
+ readOnly: true,
500
+ scope: 1 /* ConfigurationScope.APPLICATION */
501
+ }, {
502
+ id: 'provider',
503
+ type: 'string',
504
+ label: 'Provider',
505
+ default: seed.providerId,
506
+ scope: 2 /* ConfigurationScope.MACHINE */,
507
+ readOnly: true
508
+ }, {
509
+ id: 'providerTitle',
510
+ type: 'string',
511
+ label: 'Provider Title',
512
+ default: seed.title,
513
+ scope: 2 /* ConfigurationScope.MACHINE */,
514
+ readOnly: true
515
+ }, {
516
+ id: 'providerUrl',
517
+ type: 'string',
518
+ label: 'Provider URL',
519
+ default: seed.providerUrl,
520
+ scope: 2 /* ConfigurationScope.MACHINE */,
521
+ readOnly: true
522
+ }, {
523
+ id: 'useCustomOAuthEndpoints',
524
+ type: 'boolean',
525
+ label: 'Use Custom OAuth Endpoints',
526
+ default: seed.useCustomOAuthEndpoints ?? false,
527
+ scope: 2 /* ConfigurationScope.MACHINE */,
528
+ readOnly: true
529
+ }, {
530
+ id: 'authorizationUrl',
531
+ type: 'string',
532
+ label: 'Authorization URL',
533
+ default: seed.authorizationUrl || '',
534
+ scope: 2 /* ConfigurationScope.MACHINE */,
535
+ readOnly: true
536
+ }, {
537
+ id: 'tokenUrl',
538
+ type: 'string',
539
+ label: 'Token URL',
540
+ default: seed.tokenUrl || '',
541
+ scope: 2 /* ConfigurationScope.MACHINE */,
542
+ readOnly: true
543
+ }, {
544
+ id: 'revocationUrl',
545
+ type: 'string',
546
+ label: 'Revocation URL',
547
+ default: seed.revocationUrl || '',
548
+ scope: 2 /* ConfigurationScope.MACHINE */,
549
+ readOnly: true
550
+ }, {
551
+ id: 'useBasicAuth',
552
+ type: 'boolean',
553
+ label: 'Use Basic Auth',
554
+ default: seed.useBasicAuth ?? false,
555
+ scope: 2 /* ConfigurationScope.MACHINE */,
556
+ readOnly: true
557
+ }, {
558
+ id: 'redirectUri',
559
+ type: 'string',
560
+ label: 'Redirect URI',
561
+ scope: 2 /* ConfigurationScope.MACHINE */,
562
+ default: '{{CLIENT_URL}}/{{CONNECTOR_ENDPOINT}}',
563
+ readOnly: true,
564
+ defaultValueSource: {
565
+ type: ContributeDefaultValueSourceType.Environment
566
+ }
567
+ }, {
568
+ id: 'scopes',
569
+ type: 'array',
570
+ label: 'Scopes',
571
+ scope: 2 /* ConfigurationScope.MACHINE */,
572
+ default: seed.scopes,
573
+ readOnly: true
574
+ }, {
575
+ id: 'description',
576
+ type: 'string',
577
+ label: 'Description',
578
+ scope: 2 /* ConfigurationScope.MACHINE */,
579
+ default: seed.description,
580
+ readOnly: true
581
+ }, {
582
+ id: 'connectorType',
583
+ type: 'string',
584
+ label: 'Connector Type',
585
+ default: seed.connectorType,
586
+ scope: 2 /* ConfigurationScope.MACHINE */,
587
+ readOnly: true
588
+ }, {
589
+ id: 'mcpUrl',
590
+ type: 'string',
591
+ label: 'MCP URL',
592
+ default: seed.mcpUrl || '',
593
+ scope: 2 /* ConfigurationScope.MACHINE */,
594
+ readOnly: true
595
+ }, {
596
+ id: 'authMethod',
597
+ type: 'string',
598
+ label: 'Auth Method',
599
+ default: seedAuthMethodDefault(seed),
600
+ scope: 2 /* ConfigurationScope.MACHINE */,
601
+ readOnly: true
602
+ }, {
603
+ id: 'mcpScopes',
604
+ type: 'array',
605
+ label: 'MCP Scopes',
606
+ default: seed.connectorType === 'mcp' ? seed.scopes : [],
607
+ scope: 2 /* ConfigurationScope.MACHINE */,
608
+ readOnly: true
609
+ }, {
610
+ id: 'hideCredentials',
611
+ type: 'boolean',
612
+ label: 'Hide Credentials',
613
+ default: false,
614
+ scope: 2 /* ConfigurationScope.MACHINE */
615
+ }, {
616
+ id: 'isConnected',
617
+ type: 'boolean',
618
+ label: 'Connected',
619
+ default: false,
620
+ scope: 2 /* ConfigurationScope.MACHINE */,
621
+ readOnly: true
622
+ }];
623
+ const widgetUiOverrides = {
624
+ clientId: {
625
+ 'ui:widget': 'hidden',
626
+ 'ui:placeholder': 'Enter OAuth Client ID'
627
+ },
628
+ clientSecret: {
629
+ 'ui:widget': 'hidden',
630
+ 'ui:placeholder': 'Enter OAuth Client Secret'
631
+ },
632
+ apiKey: {
633
+ 'ui:widget': seed.connectorType === 'mcp' || !seed.authMethods.includes('oauth2') ? 'password' : 'hidden',
634
+ 'ui:placeholder': 'Enter API Key'
635
+ },
636
+ accessToken: {
637
+ 'ui:widget': 'hidden'
638
+ },
639
+ refreshToken: {
640
+ 'ui:widget': 'hidden'
641
+ },
642
+ expiresAt: {
643
+ 'ui:widget': 'hidden'
644
+ },
645
+ urlTemplateKeyValues: {
646
+ 'ui:widget': 'hidden'
647
+ },
648
+ authRequestMeta: {
649
+ 'ui:widget': 'hidden'
650
+ },
651
+ provider: {
652
+ 'ui:readonly': true
653
+ },
654
+ providerTitle: {
655
+ 'ui:readonly': true
656
+ },
657
+ providerUrl: {
658
+ 'ui:readonly': true
659
+ },
660
+ useCustomOAuthEndpoints: {
661
+ 'ui:readonly': true
662
+ },
663
+ authorizationUrl: {
664
+ 'ui:readonly': true
665
+ },
666
+ tokenUrl: {
667
+ 'ui:readonly': true
668
+ },
669
+ revocationUrl: {
670
+ 'ui:readonly': true
671
+ },
672
+ redirectUri: {
673
+ 'ui:readonly': true
674
+ },
675
+ scopes: {
676
+ 'ui:readonly': true,
677
+ 'ui:widget': 'chips'
678
+ },
679
+ description: {
680
+ 'ui:readonly': true
681
+ },
682
+ connectorType: {
683
+ 'ui:widget': 'hidden'
684
+ },
685
+ mcpUrl: {
686
+ 'ui:readonly': true
687
+ },
688
+ authMethod: {},
689
+ mcpScopes: {
690
+ 'ui:readonly': true,
691
+ 'ui:widget': 'chips'
692
+ },
693
+ hideCredentials: {
694
+ 'ui:widget': 'hidden'
695
+ },
696
+ isConnected: {
697
+ 'ui:widget': 'hidden'
698
+ }
699
+ };
700
+ return [{
701
+ id: STEP_KEY,
702
+ title: 'Step 1',
703
+ elements: [{
704
+ id: 'oauthConnector',
705
+ type: 'object',
706
+ label: 'OAuth Connector',
707
+ required: false,
708
+ description: seed.description || 'Connect to external OAuth providers',
709
+ connectorType: seed.connectorType,
710
+ widget: 'OAuthConnectorWidget',
711
+ uiField: 'OAuthConnectorField',
712
+ children,
713
+ _widgetUiOverrides: widgetUiOverrides,
714
+ connectorProvider: seed.providerId,
715
+ connectorProviderTitle: seed.title,
716
+ connectorDescription: seed.description,
717
+ connectorCategory: seedCategory(seed),
718
+ ...(seed.connectorType === 'mcp' ? {
719
+ connectorMcpUrl: seed.providerUrl
720
+ } : {
721
+ connectorProviderUrl: seed.providerUrl
722
+ }),
723
+ connectorScopes: seed.scopes,
724
+ useCustomOAuthEndpoints: seed.useCustomOAuthEndpoints ?? false,
725
+ connectorAuthorizationUrl: seed.authorizationUrl || '',
726
+ connectorTokenUrl: seed.tokenUrl || '',
727
+ connectorRevocationUrl: seed.revocationUrl || '',
728
+ useBasicAuth: seed.useBasicAuth ?? false,
729
+ connectorRedirectUri: '{{CLIENT_URL}}/{{CONNECTOR_ENDPOINT}}',
730
+ // connectorAuthMethod — present only for non-oauth connectors (matches reference forms)
731
+ ...(!seed.authMethods.includes('oauth2') ? {
732
+ connectorAuthMethod: seedAuthMethodDefault(seed)
733
+ } : {}),
734
+ ...(seed.fields.length > 0 ? {
735
+ connectorProviderFields: seed.fields
736
+ } : {}),
737
+ ...(seed.resources.length > 0 ? {
738
+ connectorProviderResources: seed.resources
739
+ } : {}),
740
+ ...(seed.customButtons.length > 0 ? {
741
+ connectorProviderCustomButtons: seed.customButtons
742
+ } : {}),
743
+ addedFromStepperConfigToggle: true,
744
+ isSchemaElement: true,
745
+ sourceSchemaId: 'configuration',
746
+ fullPath,
747
+ contributionPoint: 'configuration.integrationSettings',
748
+ templateType,
749
+ namespace
750
+ }]
751
+ }];
752
+ }
753
+ const buildCategoryKeyLookup = () => {
754
+ const lookup = new Map();
755
+ const prefix = 'uiLayout.credential.templates.';
756
+ const suffix = '.value';
757
+ for (const [key, value] of Object.entries(CONTRIBUTION_POINTS)) {
758
+ if (key.startsWith(prefix) && key.endsWith(suffix)) {
759
+ lookup.set(value, key.slice(prefix.length, -suffix.length));
760
+ }
761
+ }
762
+ return lookup;
763
+ };
764
+ const CATEGORY_KEY_LOOKUP = buildCategoryKeyLookup();
765
+ const categoryToTemplateTypeKey = category => {
766
+ const key = CATEGORY_KEY_LOOKUP.get(category);
767
+ if (!key) {
768
+ throw new Error(`No contribution-point template type found for category "${category}"`);
769
+ }
770
+ return key;
771
+ };
772
+ function buildOAuthConfigurationContribution(seedsWithNamespaces) {
773
+ const formProperties = {
774
+ required: []
775
+ };
776
+ const formUIProperties = {};
777
+ const metaPaths = new Set();
778
+ for (const {
779
+ seed,
780
+ namespace
781
+ } of seedsWithNamespaces) {
782
+ const vaultPrefix = toVaultPrefix(seed.providerId);
783
+ const isApp = seed.connectorType === 'app';
784
+ const isApiKeyOnly = seedIsApiKeyOnly(seed);
785
+ const templateType = seedTemplateType(seed);
786
+ const basePath = `integrationSettings.settings.${templateType}.${namespace}.oauthConnector`;
787
+ // Helper to add a config field under the oauthConnector basePath
788
+ const addField = (fieldName, def, uiDef) => {
789
+ formProperties[`${basePath}.${fieldName}`] = {
790
+ ...def,
791
+ fieldMeta: {
792
+ addedFromStepperConfigToggle: true
793
+ }
794
+ };
795
+ if (uiDef && Object.keys(uiDef).length > 0) {
796
+ formUIProperties[`${basePath}.${fieldName}`] = uiDef;
797
+ }
798
+ };
799
+ // Register oauthConnector path + all parents for meta entries (always, so widget renders)
800
+ const baseParts = basePath.split('.');
801
+ for (let i = 1; i < baseParts.length; i++) {
802
+ metaPaths.add(baseParts.slice(0, i).join('.'));
803
+ }
804
+ metaPaths.add(basePath);
805
+ // Root OAuthConnector UI entry (always, so widget renders)
806
+ // Include ui:options so providerFields / authMethods survive the $ref resolution
807
+ const uiOptions = {
808
+ provider: seed.providerId,
809
+ connectorType: seed.connectorType,
810
+ defaultAuthMethod: seedAuthMethodDefault(seed)
811
+ };
812
+ if (seed.authMethods.length > 0) uiOptions.authMethods = seed.authMethods;
813
+ if (seed.clientAuthMethod) uiOptions.defaultClientAuthMethod = seed.clientAuthMethod;
814
+ if (seed.usePKCE !== undefined) uiOptions.usePKCE = seed.usePKCE;
815
+ uiOptions.category = seedCategory(seed);
816
+ if (seed.fields.length > 0) {
817
+ const genericTypeCounts = buildGenericTypeCounts(seed.fields);
818
+ uiOptions.providerFields = seed.fields.map(field => field.isConfigField ? {
819
+ ...field,
820
+ keyReference: `${basePath}.${resolveFieldKey(field, genericTypeCounts)}`,
821
+ schemaReference: 'configuration'
822
+ } : field);
823
+ }
824
+ if (seed.resources.length > 0) uiOptions.providerResources = seed.resources;
825
+ if (seed.customButtons.length > 0) uiOptions.providerCustomButtons = seed.customButtons;
826
+ formUIProperties[basePath] = {
827
+ 'ui:widget': 'OAuthConnectorWidget',
828
+ 'ui:field': 'OAuthConnectorField',
829
+ 'ui:options': uiOptions
830
+ };
831
+ // ── OAuth sub-fields — only for non-api_key-only connectors ─────
832
+ if (!isApiKeyOnly) {
833
+ addField('clientId', {
834
+ type: 'string',
835
+ title: 'Client ID',
836
+ description: 'OAuth application client identifier',
837
+ format: 'secret',
838
+ default: '',
839
+ readOnly: true,
840
+ scope: isApp ? 6 /* ConfigurationScope.MACHINE_OVERRIDABLE */ : 1 /* ConfigurationScope.APPLICATION */,
841
+ defaultValueSource: {
842
+ type: ContributeDefaultValueSourceType.Vault,
843
+ secretType: 'OAUTH_CLIENT',
844
+ secretKey: `${vaultPrefix}_CLIENT_ID`,
845
+ ...(isApp ? {
846
+ requiredSignature: true
847
+ } : {})
848
+ }
849
+ }, {
850
+ 'ui:widget': 'hidden',
851
+ 'ui:description': 'OAuth application client identifier',
852
+ 'ui:placeholder': 'Enter OAuth Client ID'
853
+ });
854
+ addField('clientSecret', {
855
+ type: 'string',
856
+ title: 'Client Secret',
857
+ description: 'OAuth application client secret',
858
+ format: 'secret',
859
+ default: '',
860
+ readOnly: true,
861
+ scope: isApp ? 6 /* ConfigurationScope.MACHINE_OVERRIDABLE */ : 1 /* ConfigurationScope.APPLICATION */,
862
+ defaultValueSource: {
863
+ type: ContributeDefaultValueSourceType.Vault,
864
+ secretType: 'OAUTH_CLIENT',
865
+ secretKey: `${vaultPrefix}_CLIENT_SECRET`,
866
+ ...(isApp ? {
867
+ requiredSignature: true
868
+ } : {})
869
+ }
870
+ }, {
871
+ 'ui:widget': 'hidden',
872
+ 'ui:description': 'OAuth application client secret',
873
+ 'ui:placeholder': 'Enter OAuth Client Secret'
874
+ });
875
+ addField('apiKey', {
876
+ type: 'string',
877
+ title: 'API Key',
878
+ description: 'API Key for MCP integration',
879
+ format: 'secret',
880
+ default: '',
881
+ writeOnly: true,
882
+ scope: 4 /* ConfigurationScope.RESOURCE */,
883
+ defaultValueSource: {
884
+ type: ContributeDefaultValueSourceType.Vault,
885
+ secretType: 'API_KEY',
886
+ secretKey: `${vaultPrefix}_API_KEY`
887
+ }
888
+ }, {
889
+ 'ui:widget': !seed.authMethods.includes('oauth2') ? 'password' : 'hidden',
890
+ 'ui:description': 'API Key for MCP integration',
891
+ 'ui:placeholder': 'Enter API Key'
892
+ });
893
+ addField('accessToken', {
894
+ type: 'string',
895
+ title: 'Access Token',
896
+ description: '',
897
+ format: 'secret',
898
+ default: '',
899
+ readOnly: true,
900
+ writeOnly: true,
901
+ scope: 1 /* ConfigurationScope.APPLICATION */,
902
+ defaultValueSource: {
903
+ type: ContributeDefaultValueSourceType.Vault,
904
+ secretType: 'OAUTH_CLIENT',
905
+ secretKey: `${vaultPrefix}_ACCESS_TOKEN`
906
+ }
907
+ }, {
908
+ 'ui:widget': 'hidden'
909
+ });
910
+ addField('refreshToken', {
911
+ type: 'string',
912
+ title: 'Refresh Token',
913
+ description: '',
914
+ format: 'secret',
915
+ default: '',
916
+ readOnly: true,
917
+ writeOnly: true,
918
+ scope: 1 /* ConfigurationScope.APPLICATION */,
919
+ defaultValueSource: {
920
+ type: ContributeDefaultValueSourceType.Vault,
921
+ secretType: 'OAUTH_CLIENT',
922
+ secretKey: `${vaultPrefix}_REFRESH_TOKEN`
923
+ }
924
+ }, {
925
+ 'ui:widget': 'hidden'
926
+ });
927
+ addField('expiresAt', {
928
+ type: 'number',
929
+ title: 'Expires At',
930
+ description: 'Access token expiry as Unix timestamp in milliseconds; 0 if unset',
931
+ default: 0,
932
+ readOnly: true,
933
+ scope: 1 /* ConfigurationScope.APPLICATION */
934
+ }, {
935
+ 'ui:widget': 'hidden'
936
+ });
937
+ addField('urlTemplateKeyValues', {
938
+ type: 'string',
939
+ title: 'URL Template Key Values',
940
+ description: 'Stringified JSON of resolved values for URL template placeholders (e.g. {"zoho-region":"eu"})',
941
+ default: '{}',
942
+ readOnly: true,
943
+ scope: 1 /* ConfigurationScope.APPLICATION */
944
+ }, {
945
+ 'ui:widget': 'hidden'
946
+ });
947
+ addField('authRequestMeta', {
948
+ type: 'string',
949
+ title: 'Auth Request Metadata',
950
+ description: 'Serialized OAuth authorization-request metadata (e.g. PKCE verifier, state) for session continuity',
951
+ default: '',
952
+ readOnly: true,
953
+ scope: 1 /* ConfigurationScope.APPLICATION */
954
+ }, {
955
+ 'ui:widget': 'hidden'
956
+ });
957
+ addField('provider', {
958
+ type: 'string',
959
+ title: 'Provider',
960
+ default: seed.providerId,
961
+ readOnly: true,
962
+ scope: 2 /* ConfigurationScope.MACHINE */
963
+ }, {
964
+ 'ui:readonly': true
965
+ });
966
+ addField('providerTitle', {
967
+ type: 'string',
968
+ title: 'Provider Title',
969
+ default: seed.title,
970
+ readOnly: true,
971
+ scope: 2 /* ConfigurationScope.MACHINE */
972
+ }, {
973
+ 'ui:readonly': true
974
+ });
975
+ addField('providerUrl', {
976
+ type: 'string',
977
+ title: 'Provider URL',
978
+ default: seed.providerUrl,
979
+ readOnly: true,
980
+ scope: 2 /* ConfigurationScope.MACHINE */
981
+ }, {
982
+ 'ui:readonly': true
983
+ });
984
+ addField('useCustomOAuthEndpoints', {
985
+ type: 'boolean',
986
+ title: 'Use Custom OAuth Endpoints',
987
+ default: seed.useCustomOAuthEndpoints ?? false,
988
+ readOnly: true,
989
+ scope: 2 /* ConfigurationScope.MACHINE */
990
+ }, {
991
+ 'ui:readonly': true
992
+ });
993
+ addField('authorizationUrl', {
994
+ type: 'string',
995
+ title: 'Authorization URL',
996
+ default: seed.authorizationUrl || '',
997
+ readOnly: true,
998
+ scope: 2 /* ConfigurationScope.MACHINE */
999
+ }, {
1000
+ 'ui:readonly': true
1001
+ });
1002
+ addField('tokenUrl', {
1003
+ type: 'string',
1004
+ title: 'Token URL',
1005
+ default: seed.tokenUrl || '',
1006
+ readOnly: true,
1007
+ scope: 2 /* ConfigurationScope.MACHINE */
1008
+ }, {
1009
+ 'ui:readonly': true
1010
+ });
1011
+ addField('revocationUrl', {
1012
+ type: 'string',
1013
+ title: 'Revocation URL',
1014
+ default: seed.revocationUrl || '',
1015
+ readOnly: true,
1016
+ scope: 2 /* ConfigurationScope.MACHINE */
1017
+ }, {
1018
+ 'ui:readonly': true
1019
+ });
1020
+ addField('useBasicAuth', {
1021
+ type: 'boolean',
1022
+ title: 'Use Basic Auth',
1023
+ default: seed.useBasicAuth ?? false,
1024
+ readOnly: true,
1025
+ scope: 2 /* ConfigurationScope.MACHINE */
1026
+ }, {
1027
+ 'ui:widget': 'hidden'
1028
+ });
1029
+ if (seed.clientAuthMethod) {
1030
+ addField('clientAuthMethod', {
1031
+ type: 'string',
1032
+ title: 'Client Auth Method',
1033
+ default: seed.clientAuthMethod,
1034
+ readOnly: true,
1035
+ scope: 2 /* ConfigurationScope.MACHINE */
1036
+ }, {
1037
+ 'ui:widget': 'hidden'
1038
+ });
1039
+ }
1040
+ addField('redirectUri', {
1041
+ type: 'string',
1042
+ title: 'Redirect URI',
1043
+ default: '{{CLIENT_URL}}/{{CONNECTOR_ENDPOINT}}',
1044
+ readOnly: true,
1045
+ scope: 2 /* ConfigurationScope.MACHINE */,
1046
+ defaultValueSource: {
1047
+ type: ContributeDefaultValueSourceType.Environment
1048
+ }
1049
+ }, {
1050
+ 'ui:readonly': true
1051
+ });
1052
+ addField('scopes', {
1053
+ type: 'string',
1054
+ title: 'Scopes',
1055
+ default: seed.scopes,
1056
+ readOnly: true,
1057
+ scope: 2 /* ConfigurationScope.MACHINE */
1058
+ }, {
1059
+ 'ui:readonly': true,
1060
+ 'ui:widget': 'chips'
1061
+ });
1062
+ addField('description', {
1063
+ type: 'string',
1064
+ title: 'Description',
1065
+ default: seed.description,
1066
+ readOnly: true,
1067
+ scope: 2 /* ConfigurationScope.MACHINE */
1068
+ }, {
1069
+ 'ui:readonly': true
1070
+ });
1071
+ addField('connectorType', {
1072
+ type: 'string',
1073
+ title: 'Connector Type',
1074
+ default: seed.connectorType,
1075
+ readOnly: true,
1076
+ scope: 2 /* ConfigurationScope.MACHINE */
1077
+ }, {
1078
+ 'ui:widget': 'hidden'
1079
+ });
1080
+ addField('mcpUrl', {
1081
+ type: 'string',
1082
+ title: 'MCP URL',
1083
+ default: seed.mcpUrl || '',
1084
+ readOnly: true,
1085
+ scope: 2 /* ConfigurationScope.MACHINE */
1086
+ }, {
1087
+ 'ui:readonly': true
1088
+ });
1089
+ addField('authMethod', {
1090
+ type: 'string',
1091
+ title: 'Auth Method',
1092
+ default: seedAuthMethodDefault(seed),
1093
+ readOnly: true,
1094
+ scope: 2 /* ConfigurationScope.MACHINE */
1095
+ }, {});
1096
+ addField('mcpScopes', {
1097
+ type: 'string',
1098
+ title: 'MCP Scopes',
1099
+ default: seed.connectorType === 'mcp' ? seed.scopes : [],
1100
+ readOnly: true,
1101
+ scope: 2 /* ConfigurationScope.MACHINE */
1102
+ }, {
1103
+ 'ui:readonly': true,
1104
+ 'ui:widget': 'chips'
1105
+ });
1106
+ addField('hideCredentials', {
1107
+ type: 'string',
1108
+ title: 'Hide Credentials',
1109
+ default: false,
1110
+ scope: 2 /* ConfigurationScope.MACHINE */
1111
+ }, {
1112
+ 'ui:widget': 'hidden'
1113
+ });
1114
+ addField('isConnected', {
1115
+ type: 'string',
1116
+ title: 'Connected',
1117
+ default: false,
1118
+ readOnly: true,
1119
+ scope: 2 /* ConfigurationScope.MACHINE */
1120
+ }, {
1121
+ 'ui:widget': 'hidden'
1122
+ });
1123
+ } else {
1124
+ // api_key-only: still need minimal sub-fields so the widget can render
1125
+ addField('provider', {
1126
+ type: 'string',
1127
+ title: 'Provider',
1128
+ default: seed.providerId,
1129
+ readOnly: true,
1130
+ scope: 2 /* ConfigurationScope.MACHINE */
1131
+ }, {
1132
+ 'ui:readonly': true
1133
+ });
1134
+ addField('providerTitle', {
1135
+ type: 'string',
1136
+ title: 'Provider Title',
1137
+ default: seed.title,
1138
+ readOnly: true,
1139
+ scope: 2 /* ConfigurationScope.MACHINE */
1140
+ }, {
1141
+ 'ui:readonly': true
1142
+ });
1143
+ addField('providerUrl', {
1144
+ type: 'string',
1145
+ title: 'Provider URL',
1146
+ default: seed.providerUrl,
1147
+ readOnly: true,
1148
+ scope: 2 /* ConfigurationScope.MACHINE */
1149
+ }, {
1150
+ 'ui:readonly': true
1151
+ });
1152
+ addField('description', {
1153
+ type: 'string',
1154
+ title: 'Description',
1155
+ default: seed.description,
1156
+ readOnly: true,
1157
+ scope: 2 /* ConfigurationScope.MACHINE */
1158
+ }, {
1159
+ 'ui:readonly': true
1160
+ });
1161
+ addField('connectorType', {
1162
+ type: 'string',
1163
+ title: 'Connector Type',
1164
+ default: seed.connectorType,
1165
+ readOnly: true,
1166
+ scope: 2 /* ConfigurationScope.MACHINE */
1167
+ }, {
1168
+ 'ui:widget': 'hidden'
1169
+ });
1170
+ addField('authMethod', {
1171
+ type: 'string',
1172
+ title: 'Auth Method',
1173
+ default: 'api_key',
1174
+ readOnly: true,
1175
+ scope: 2 /* ConfigurationScope.MACHINE */
1176
+ }, {});
1177
+ }
1178
+ // Custom isConfigField fields should also be written into configuration schema.
1179
+ const customFieldGenericCounts = buildGenericTypeCounts(seed.fields);
1180
+ for (const field of seed.fields) {
1181
+ if (!field.isConfigField) continue;
1182
+ const fieldPath = `${basePath}.${resolveFieldKey(field, customFieldGenericCounts)}`;
1183
+ const fieldDef = {
1184
+ type: field.type,
1185
+ title: field.label,
1186
+ description: field.description || '',
1187
+ fieldMeta: {
1188
+ addedFromStepperConfigToggle: true
1189
+ },
1190
+ required: field.required,
1191
+ scope: field.scope || 4 /* ConfigurationScope.RESOURCE */
1192
+ };
1193
+ if (field.isSecret && field.secretSource) {
1194
+ fieldDef.format = 'secret';
1195
+ fieldDef.writeOnly = true;
1196
+ fieldDef['x-env'] = {
1197
+ isEnvironmentVariable: true,
1198
+ vaultKeyName: field.secretSource.secretKey,
1199
+ secretType: field.secretSource.secretType
1200
+ };
1201
+ fieldDef.defaultValueSource = {
1202
+ type: field.secretSource.type,
1203
+ secretType: field.secretSource.secretType,
1204
+ secretKey: field.secretSource.secretKey
1205
+ };
1206
+ fieldDef.isEnvironmentVariable = true;
1207
+ fieldDef.secretType = field.secretSource.secretType;
1208
+ fieldDef.vaultKeyName = field.secretSource.secretKey;
1209
+ if (field.placeholder) fieldDef.placeholder = field.placeholder;
1210
+ }
1211
+ formProperties[fieldPath] = fieldDef;
1212
+ const uiEntry = {};
1213
+ if (field.uiWidget && field.uiWidget !== 'text') {
1214
+ uiEntry['ui:widget'] = field.uiWidget;
1215
+ }
1216
+ if (field.placeholder) {
1217
+ uiEntry['ui:placeholder'] = field.placeholder;
1218
+ }
1219
+ if (Object.keys(uiEntry).length > 0) {
1220
+ formUIProperties[fieldPath] = uiEntry;
1221
+ }
1222
+ // Ensure meta paths include this custom field path.
1223
+ const parts = fieldPath.split('.');
1224
+ for (let i = 1; i < parts.length; i++) {
1225
+ metaPaths.add(parts.slice(0, i).join('.'));
1226
+ }
1227
+ }
1228
+ }
1229
+ // Build meta entries for all parent paths
1230
+ const sortedMeta = Array.from(metaPaths).sort();
1231
+ for (const path of sortedMeta) {
1232
+ const parts = path.split('.');
1233
+ formProperties[`${path}.meta`] = {
1234
+ title: parts[parts.length - 1],
1235
+ description: '',
1236
+ type: 'object',
1237
+ position: 0
1238
+ };
1239
+ formProperties[`${path}.required`] = [];
1240
+ }
1241
+ return [{
1242
+ type: 'form-metadata',
1243
+ properties: {
1244
+ schemaId: 'configuration'
1245
+ }
1246
+ }, {
1247
+ type: 'form',
1248
+ properties: formProperties
1249
+ }, {
1250
+ type: 'formUI',
1251
+ properties: formUIProperties
1252
+ }];
1253
+ }
1254
+ function generateFromSeeds(seeds, title, formIcon, formExtensionId) {
1255
+ const allNodes = [];
1256
+ const mergedUiLayoutTemplates = {};
1257
+ const mergedUiLayoutUi = {};
1258
+ let uiLayoutFormMetadataNode = null;
1259
+ const seedsWithNamespaces = [];
1260
+ seeds.forEach((seed, idx) => {
1261
+ const generatedFormId = `${sanitizeFieldKey(seed.providerId)}-${seed.connectorType}-${Math.random().toString(36).substring(2, 10)}`;
1262
+ const providerTitle = `${seed.title} (${seed.connectorType.toUpperCase()})`;
1263
+ const namespace = generatedFormId;
1264
+ seedsWithNamespaces.push({
1265
+ seed,
1266
+ namespace
1267
+ });
1268
+ // ── Stepper-form: minimal form node with $ref to configuration ──
1269
+ seedIsApiKeyOnly(seed);
1270
+ const formProperties = {
1271
+ required: [],
1272
+ [`${STEP_KEY}.meta`]: {
1273
+ title: 'Step 1',
1274
+ description: '',
1275
+ type: 'object',
1276
+ position: 0
1277
+ },
1278
+ [`${STEP_KEY}.required`]: []
1279
+ };
1280
+ // oauthConnector $ref — always present so the widget renders for all forms
1281
+ formProperties[`${STEP_KEY}.oauthConnector`] = {
1282
+ $ref: `configuration#/properties/integrationSettings.settings.${seedTemplateType(seed)}.${namespace}.oauthConnector`
1283
+ };
1284
+ // Custom isConfigField fields are rendered inside the OAuthConnectorWidget
1285
+ // via connectorProviderFields — no standalone $refs needed in the stepper form.
1286
+ const formNode = {
1287
+ type: 'form',
1288
+ properties: formProperties
1289
+ };
1290
+ const formUINode = {
1291
+ type: 'formUI',
1292
+ properties: {
1293
+ [STEP_KEY]: {
1294
+ 'ui:options': {}
1295
+ }
1296
+ }
1297
+ };
1298
+ const definitionsNode = {
1299
+ type: 'definitions',
1300
+ properties: {}
1301
+ };
1302
+ const connectionsNode = {
1303
+ type: 'connections',
1304
+ properties: {
1305
+ type: 'connections',
1306
+ 'properties.steps.0': STEP_KEY,
1307
+ [`properties.transitions.${STEP_KEY}`]: {}
1308
+ }
1309
+ };
1310
+ const buttonsNode = buildCustomButtonsNode(seed);
1311
+ const resourcesNode = buildResourcesNode(seed);
1312
+ const inngestNode = buildInngestNode();
1313
+ const formDataNode = {
1314
+ type: 'formData',
1315
+ properties: {}
1316
+ };
1317
+ // form-metadata goes LAST (matches manual GUI pattern)
1318
+ const formMetadataNode = {
1319
+ type: 'form-metadata',
1320
+ properties: {
1321
+ title: providerTitle,
1322
+ formIcon: seed.formIcon || '',
1323
+ id: generatedFormId,
1324
+ category: seedCategory(seed),
1325
+ preGeneratedFormSteps: buildPreGeneratedFormSteps(seed, namespace)
1326
+ }
1327
+ };
1328
+ // Emit nodes: form-metadata FIRST (parseSampleNodes splits groups at form-metadata boundaries)
1329
+ allNodes.push(formMetadataNode, formNode, formUINode);
1330
+ allNodes.push(definitionsNode, connectionsNode);
1331
+ allNodes.push(buttonsNode || {
1332
+ type: 'customButtons',
1333
+ properties: {}
1334
+ });
1335
+ allNodes.push(resourcesNode || {
1336
+ type: 'resources',
1337
+ properties: {}
1338
+ });
1339
+ allNodes.push(inngestNode);
1340
+ allNodes.push(formDataNode);
1341
+ // Build merged uiLayout (single record)
1342
+ // Only build the metadata node once
1343
+ if (!uiLayoutFormMetadataNode) {
1344
+ uiLayoutFormMetadataNode = {
1345
+ type: 'form-metadata',
1346
+ properties: {
1347
+ id: 'uilayout-oauth',
1348
+ title: 'uiLayout',
1349
+ schemaId: 'uiLayout',
1350
+ contributionPoint: 'credential',
1351
+ namespace: 'templates'
1352
+ }
1353
+ };
1354
+ }
1355
+ // Add to merged templates — key must match formId (same pattern as commonApiSample)
1356
+ const hasDualAuth = seed.connectorType === 'app' && seed.authMethods.includes('api_key') && seed.authMethods.includes('oauth2');
1357
+ const templateProperties = {
1358
+ templateName: {
1359
+ type: 'string',
1360
+ title: 'templateName',
1361
+ default: providerTitle,
1362
+ scope: 3 /* ConfigurationScope.WINDOW */
1363
+ },
1364
+ projectType: {
1365
+ type: 'string',
1366
+ title: 'projectType',
1367
+ default: seed.connectorType === 'mcp' ? 'MCP' : !seed.authMethods.includes('oauth2') && (seed.authMethods.includes('api_key') || seed.authMethods.includes('bearer_token')) ? 'API' : 'OAuth',
1368
+ scope: 3 /* ConfigurationScope.WINDOW */
1369
+ },
1370
+ description: {
1371
+ type: 'string',
1372
+ title: 'description',
1373
+ default: seed.description || `${seed.title} connector template`,
1374
+ scope: 3 /* ConfigurationScope.WINDOW */
1375
+ },
1376
+ tags: {
1377
+ type: 'array',
1378
+ title: 'tags',
1379
+ items: {
1380
+ type: 'string'
1381
+ },
1382
+ uniqueItems: true,
1383
+ default: [seed.providerId, seed.connectorType, 'connector'],
1384
+ scope: 3 /* ConfigurationScope.WINDOW */
1385
+ },
1386
+ keywords: {
1387
+ type: 'array',
1388
+ title: 'keywords',
1389
+ items: {
1390
+ type: 'string'
1391
+ },
1392
+ uniqueItems: true,
1393
+ default: seed.connectorType === 'mcp' ? [seed.providerId, 'mcp', seed.authMethods.includes('oauth2') ? 'oauth' : 'api-key'] : !seed.authMethods.includes('oauth2') && seed.authMethods.includes('api_key') ? [seed.providerId, 'api', 'api-key'] : [seed.providerId, seed.connectorType, 'oauth widget'],
1394
+ scope: 3 /* ConfigurationScope.WINDOW */
1395
+ },
1396
+ formId: {
1397
+ type: 'string',
1398
+ title: 'formId',
1399
+ default: generatedFormId,
1400
+ scope: 3 /* ConfigurationScope.WINDOW */
1401
+ },
1402
+ formExtensionId: {
1403
+ type: 'string',
1404
+ title: 'formExtensionId',
1405
+ default: formExtensionId,
1406
+ scope: 3 /* ConfigurationScope.WINDOW */
1407
+ },
1408
+ formIcon: {
1409
+ type: 'string',
1410
+ title: 'formIcon',
1411
+ default: seed.formIcon || '',
1412
+ scope: 3 /* ConfigurationScope.WINDOW */
1413
+ }
1414
+ };
1415
+ if (hasDualAuth) {
1416
+ templateProperties.apiKeyEnabled = {
1417
+ type: 'boolean',
1418
+ title: 'apiKeyEnabled',
1419
+ default: true,
1420
+ scope: 3 /* ConfigurationScope.WINDOW */
1421
+ };
1422
+ templateProperties.oAuthEnabled = {
1423
+ type: 'boolean',
1424
+ title: 'oAuthEnabled',
1425
+ default: false,
1426
+ scope: 3 /* ConfigurationScope.WINDOW */
1427
+ };
1428
+ } else {
1429
+ // If connectorType is 'app' and only oauth2 is present, enabled should be false
1430
+ const isAppOnlyOauth = seed.connectorType === 'app' && seed.authMethods.length === 1 && seed.authMethods[0] === 'oauth2';
1431
+ templateProperties.enabled = {
1432
+ type: 'boolean',
1433
+ title: 'enabled',
1434
+ default: !isAppOnlyOauth,
1435
+ scope: 3 /* ConfigurationScope.WINDOW */
1436
+ };
1437
+ }
1438
+ mergedUiLayoutTemplates[generatedFormId] = {
1439
+ type: 'object',
1440
+ title: providerTitle,
1441
+ properties: templateProperties,
1442
+ required: []
1443
+ };
1444
+ const templateUi = {
1445
+ templateName: {
1446
+ 'ui:widget': 'text'
1447
+ },
1448
+ projectType: {
1449
+ 'ui:widget': 'text'
1450
+ },
1451
+ description: {
1452
+ 'ui:widget': 'textarea',
1453
+ 'ui:options': {
1454
+ rows: 2
1455
+ }
1456
+ },
1457
+ tags: {
1458
+ 'ui:widget': 'chips',
1459
+ 'ui:options': {
1460
+ label: false
1461
+ }
1462
+ },
1463
+ keywords: {
1464
+ 'ui:widget': 'chips',
1465
+ 'ui:options': {
1466
+ label: false
1467
+ }
1468
+ },
1469
+ formId: {
1470
+ 'ui:readonly': true
1471
+ },
1472
+ formExtensionId: {
1473
+ 'ui:readonly': true
1474
+ },
1475
+ formIcon: {
1476
+ 'ui:widget': 'text'
1477
+ }
1478
+ };
1479
+ if (hasDualAuth) {
1480
+ templateUi.apiKeyEnabled = {
1481
+ 'ui:widget': 'switch'
1482
+ };
1483
+ templateUi.oAuthEnabled = {
1484
+ 'ui:widget': 'switch'
1485
+ };
1486
+ } else {
1487
+ templateUi.enabled = {
1488
+ 'ui:widget': 'switch'
1489
+ };
1490
+ }
1491
+ mergedUiLayoutUi[generatedFormId] = templateUi;
1492
+ });
1493
+ // Compose a single flattened uiLayout schema record for all providers, with '{credentialPath}.{category}.' prefix
1494
+ // Group by connector category so each provider lands under its actual category key
1495
+ const credentialBasePath = CONTRIBUTION_POINTS['uiLayout.credential.path'];
1496
+ const prefixedTemplates = {};
1497
+ const prefixedUi = {};
1498
+ // Build a namespace→category lookup from seeds using contribution-point template type keys
1499
+ const nsToCategoryMap = new Map();
1500
+ seedsWithNamespaces.forEach(({
1501
+ seed,
1502
+ namespace
1503
+ }) => {
1504
+ nsToCategoryMap.set(namespace, categoryToTemplateTypeKey(seedCategory(seed)));
1505
+ });
1506
+ Object.keys(mergedUiLayoutTemplates).forEach(key => {
1507
+ const categoryKey = nsToCategoryMap.get(key);
1508
+ if (!categoryKey) throw new Error(`Missing category mapping for namespace "${key}"`);
1509
+ prefixedTemplates[`${credentialBasePath}.${categoryKey}.${key}`] = mergedUiLayoutTemplates[key];
1510
+ });
1511
+ Object.keys(mergedUiLayoutUi).forEach(key => {
1512
+ const categoryKey = nsToCategoryMap.get(key);
1513
+ if (!categoryKey) throw new Error(`Missing category mapping for namespace "${key}"`);
1514
+ prefixedUi[`${credentialBasePath}.${categoryKey}.${key}`] = mergedUiLayoutUi[key];
1515
+ });
1516
+ const uiLayoutSchema = {
1517
+ type: 'object',
1518
+ properties: {
1519
+ ...prefixedTemplates
1520
+ }
1521
+ };
1522
+ const uiLayoutUiSchema = {
1523
+ ...prefixedUi
1524
+ };
1525
+ const flattenedUiLayout = flattenSchemas({
1526
+ formSchema: uiLayoutSchema,
1527
+ uiSchema: uiLayoutUiSchema
1528
+ });
1529
+ const uiLayoutFormNode = {
1530
+ type: 'form',
1531
+ properties: flattenedUiLayout.formSchema || {}
1532
+ };
1533
+ const uiLayoutFormUINode = {
1534
+ type: 'formUI',
1535
+ properties: flattenedUiLayout.uiSchema || {}
1536
+ };
1537
+ // Merged configuration contribution (ALWAYS emitted)
1538
+ allNodes.push(...buildOAuthConfigurationContribution(seedsWithNamespaces));
1539
+ if (!uiLayoutFormMetadataNode) return allNodes;
1540
+ return [...allNodes, uiLayoutFormMetadataNode, uiLayoutFormNode, uiLayoutFormUINode];
1541
+ }
1542
+ function createOAuthSeedGenerator(seeds) {
1543
+ return (title = '', formIcon = '', formExtensionId = '') => generateFromSeeds(seeds, title, formIcon, formExtensionId);
1544
+ }
1545
+ // create provider refs
1546
+ const findRefPath = node => {
1547
+ if (!node || typeof node !== 'object') return null;
1548
+ if (typeof node.$ref === 'string') return node.$ref;
1549
+ for (const value of Object.values(node)) {
1550
+ if (Array.isArray(value)) {
1551
+ for (const entry of value) {
1552
+ const found = findRefPath(entry);
1553
+ if (found) return found;
1554
+ }
1555
+ } else if (value && typeof value === 'object') {
1556
+ const found = findRefPath(value);
1557
+ if (found) return found;
1558
+ }
1559
+ }
1560
+ return null;
1561
+ };
1562
+ const buildProviderWithRefs = (provider, sampleNodes) => {
1563
+ const refPath = findRefPath({
1564
+ nodes: sampleNodes
1565
+ });
1566
+ const refMatch = refPath && /integrationSettings\.settings\.([^.]+)\.([^.]+)\.oauthConnector/.exec(refPath);
1567
+ const templateType = refMatch?.[1];
1568
+ const namespace = refMatch?.[2];
1569
+ const basePath = templateType && namespace ? `integrationSettings.settings.${templateType}.${namespace}.oauthConnector` : null;
1570
+ if (!basePath) return provider;
1571
+ const providerFieldGenericCounts = buildGenericTypeCounts(provider.fields);
1572
+ return {
1573
+ ...provider,
1574
+ fields: provider.fields.map(field => {
1575
+ if (!field.isConfigField) return field;
1576
+ return {
1577
+ ...field,
1578
+ keyReference: `${basePath}.${resolveFieldKey(field, providerFieldGenericCounts)}`,
1579
+ schemaReference: 'configuration'
1580
+ };
1581
+ })
1582
+ };
1583
+ };
1584
+ const buildMetadataSchema = sampleNodes => {
1585
+ const parsed = parseSampleNodes(sampleNodes, 'stepper-form', 'Multi Form');
1586
+ const mainNodes = parsed.mainMetadataNode ? [parsed.mainMetadataNode, ...parsed.mainNodes] : parsed.mainNodes;
1587
+ return [{
1588
+ schemaId: 'stepper-form',
1589
+ formId: parsed.mainMetadataNode?.properties?.id || '',
1590
+ integrationConfigurationId: null,
1591
+ type: 'Multi Form',
1592
+ configurationNodes: mainNodes
1593
+ }, ...parsed.additionalSchemas.map(entry => ({
1594
+ schemaId: entry.schemaId,
1595
+ formId: entry.formId || '',
1596
+ integrationConfigurationId: null,
1597
+ type: entry.type || 'Contribution',
1598
+ configurationNodes: entry.configurationNodes,
1599
+ contributes: {}
1600
+ }))];
1601
+ };export{buildMetadataSchema,buildProviderConfig,buildProviderSeedFromConfig,buildProviderWithRefs,createOAuthSeedGenerator};//# sourceMappingURL=authMetaDataSchema.js.map