@tadnt2003/n8n-nodes-infisical 0.3.6 → 0.3.8

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.
@@ -211,13 +211,25 @@ const CREDENTIAL_FIELD_MAPS = {
211
211
  { param: 'algorithm', secretKey: 'algorithm' },
212
212
  ],
213
213
  };
214
- function toSlug(name) {
215
- return name.trim().replace(/[^a-zA-Z0-9_-]+/g, '-').replace(/-+/g, '-').replace(/^-|-$/g, '');
214
+ // Lossless encoding: [A-Za-z0-9-] pass through, _ → __, everything else → _XX hex sequences.
215
+ // Infisical folder names only allow [a-zA-Z0-9_-], so % cannot be used as an escape character.
216
+ function toFolderName(name) {
217
+ return [...name].map(c => {
218
+ if (/[A-Za-z0-9-]/.test(c))
219
+ return c;
220
+ if (c === '_')
221
+ return '__';
222
+ return encodeURIComponent(c).replace(/%/g, '_');
223
+ }).join('');
224
+ }
225
+ function fromFolderName(slug) {
226
+ const restored = slug.replace(/_([0-9A-Fa-f]{2}|_)/g, (_, p1) => p1 === '_' ? '_' : '%' + p1);
227
+ return decodeURIComponent(restored);
216
228
  }
217
229
  function buildSecretPath(rootPath, credentialName) {
218
230
  const root = rootPath.replace(/\/+$/, '') || '/';
219
- const slug = toSlug(credentialName);
220
- return root === '/' ? `/${slug}` : `${root}/${slug}`;
231
+ const folder = toFolderName(credentialName);
232
+ return root === '/' ? `/${folder}` : `${root}/${folder}`;
221
233
  }
222
234
  // Fix 7.4: handles `number` type (port, connectTimeout, maxConnections, etc.)
223
235
  // Fix 7.7: reads schema's own `default` before falling back to enum heuristic
@@ -257,18 +269,19 @@ function coerceValue(raw, def) {
257
269
  //
258
270
  // When condKey IS in schema properties, fire only when the current value matches condValues.
259
271
  //
260
- // When condKey is NOT in schema properties (e.g. useDynamicClientRegistration on
261
- // googleOAuth2Api), JSON Schema evaluates the if-clause vacuously — the properties keyword
262
- // has nothing to check, so it passes, and both the [true] branch and the [false] branch
263
- // appear to fire simultaneously. That is the "vacuous truth" problem.
264
- //
265
- // n8n avoids it by internally defaulting the absent key to false before validating, so only
266
- // the branch whose condValues contain a falsy value actually fires (the "standard mode"
267
- // branch). We mirror that: condValues=[false] fires, condValues=[true] does not.
272
+ // When condKey is NOT in schema properties (e.g. useDynamicClientRegistration or grantType on
273
+ // googleOAuth2Api / googleSheetsOAuth2Api), JSON Schema evaluates the if-clause vacuously:
274
+ // the `properties` keyword only constrains keys that ARE present, so an absent key always
275
+ // satisfies the constraint. The if-clause passes then fires (else never fires) for every
276
+ // such branch, regardless of condValues. n8n's own schema validator follows this standard
277
+ // JSON Schema behaviour when useDynamicClientRegistration is absent, BOTH the [true] and
278
+ // [false] allOf branches fire, requiring serverUrl AND clientId/clientSecret simultaneously.
279
+ // We mirror that by always returning true for vacuous-truth branches.
268
280
  function conditionFires(condKeyInSchema, condValues, condVal) {
269
281
  if (condKeyInSchema)
270
282
  return condValues.includes(condVal);
271
- return condValues.some((v) => !v);
283
+ // Vacuous truth: absent key → if always passes → then always fires.
284
+ return true;
272
285
  }
273
286
  // Validate credential data against the n8n schema's required fields and conditional requirements.
274
287
  // For form mode, pass availableFormFields to skip checks for fields the form cannot provide.
@@ -580,7 +593,8 @@ async function autoSyncFromInfisical(ctx, apiUrl, baseHeaders, i) {
580
593
  credentialData[key] = coerceValue(raw, schemaInfo === null || schemaInfo === void 0 ? void 0 : schemaInfo.props[key]);
581
594
  }
582
595
  }
583
- const existing = credByName.get(folderName);
596
+ const credentialName = fromFolderName(folderName);
597
+ const existing = credByName.get(credentialName);
584
598
  if (existing) {
585
599
  // Fix 7.2: apply the same fullData build logic as the create path so that condition-key
586
600
  // changes (e.g. ssl:false→true) get their required fields filled and their prohibited
@@ -618,7 +632,7 @@ async function autoSyncFromInfisical(ctx, apiUrl, baseHeaders, i) {
618
632
  method: 'POST',
619
633
  url: `${n8nApiUrl}/api/v1/credentials`,
620
634
  headers: n8nHeaders,
621
- body: { name: folderName, type: credentialType, data: fullData },
635
+ body: { name: credentialName, type: credentialType, data: fullData },
622
636
  });
623
637
  results.push({
624
638
  json: Object.assign(Object.assign({}, created), { action: 'created', secretPath, secretCount: secrets.length }),
@@ -702,7 +716,7 @@ async function executeSyncOperation(ctx, apiUrl, baseHeaders, operation, i) {
702
716
  method: 'POST',
703
717
  url: `${apiUrl}/v2/folders`,
704
718
  headers: baseHeaders,
705
- body: { projectId, environment, name: toSlug(credentialName), path: folderPath },
719
+ body: { projectId, environment, name: toFolderName(credentialName), path: folderPath },
706
720
  });
707
721
  }
708
722
  catch (err) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tadnt2003/n8n-nodes-infisical",
3
- "version": "0.3.6",
3
+ "version": "0.3.8",
4
4
  "description": "n8n community node for Infisical - Secret management platform",
5
5
  "keywords": [
6
6
  "n8n-community-node-package",