@n8n/workflow-sdk 0.37.0 → 0.38.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.
@@ -108,6 +108,7 @@ export interface VersionGroup {
108
108
  export interface OutputSchema {
109
109
  resource: string;
110
110
  operation: string;
111
+ variant?: string;
111
112
  schema: JsonSchema;
112
113
  }
113
114
  export interface JsonSchema {
@@ -131,6 +132,7 @@ export declare function padVersion(version: number): string;
131
132
  export declare function computeSchemaCorpusHash(baseDirs?: string[]): string;
132
133
  export declare function jsonSchemaToTypeScript(schema: JsonSchema, indent?: number): string;
133
134
  export declare function findSchemaForOperation(schemas: OutputSchema[], resource: string, operation: string): OutputSchema | undefined;
135
+ export declare function findVariantSchemasForOperation(schemas: OutputSchema[], resource: string, operation: string): OutputSchema[];
134
136
  export interface ConfigTypeInfo {
135
137
  typeName: string;
136
138
  resource?: string;
@@ -172,7 +174,7 @@ export interface DiscriminatorTree {
172
174
  }
173
175
  export declare function buildDiscriminatorTree(combinations: DiscriminatorCombination[]): DiscriminatorTree;
174
176
  export declare function generateSharedFile(node: NodeTypeDescription, version: number, _importDepth?: number): string;
175
- export declare function generateDiscriminatorFile(node: NodeTypeDescription, version: number, combo: DiscriminatorCombination, props: NodeProperty[], schema?: JsonSchema, _importDepth?: number): string;
177
+ export declare function generateDiscriminatorFile(node: NodeTypeDescription, version: number, combo: DiscriminatorCombination, props: NodeProperty[], schema?: JsonSchema, _importDepth?: number, variantSchemas?: OutputSchema[]): string;
176
178
  export declare function generateResourceIndexFile(node: NodeTypeDescription, version: number, resource: string, operations: string[]): string;
177
179
  export declare function generateSplitVersionIndexFile(node: NodeTypeDescription, version: number, tree: DiscriminatorTree): string;
178
180
  export declare function planSplitVersionFiles(node: NodeTypeDescription, version: number): Map<string, string>;
@@ -43,6 +43,7 @@ exports.padVersion = padVersion;
43
43
  exports.computeSchemaCorpusHash = computeSchemaCorpusHash;
44
44
  exports.jsonSchemaToTypeScript = jsonSchemaToTypeScript;
45
45
  exports.findSchemaForOperation = findSchemaForOperation;
46
+ exports.findVariantSchemasForOperation = findVariantSchemasForOperation;
46
47
  exports.narrowDisplayOptionsByDisabled = narrowDisplayOptionsByDisabled;
47
48
  exports.tryMergeUxForkVariants = tryMergeUxForkVariants;
48
49
  exports.mapPropertyType = mapPropertyType;
@@ -509,19 +510,21 @@ function collectSchemasFromVersionDir(versionDir, schemas, seen) {
509
510
  for (const opEntry of operations) {
510
511
  if (!opEntry.isFile() || !opEntry.name.endsWith('.json'))
511
512
  continue;
512
- const operationName = opEntry.name.replace('.json', '');
513
- if (seen.has(`${entry.name}/${operationName}`))
513
+ const fileKey = opEntry.name.replace('.json', '');
514
+ if (seen.has(`${entry.name}/${fileKey}`))
514
515
  continue;
515
516
  const schemaPath = path.join(resourceDir, opEntry.name);
517
+ const [operationName, variant] = splitOperationVariant(fileKey);
516
518
  try {
517
519
  const schemaContent = fs.readFileSync(schemaPath, 'utf-8');
518
520
  const schema = JSON.parse(schemaContent);
519
521
  schemas.push({
520
522
  resource: entry.name,
521
523
  operation: operationName,
524
+ ...(variant ? { variant } : {}),
522
525
  schema,
523
526
  });
524
- seen.add(`${entry.name}/${operationName}`);
527
+ seen.add(`${entry.name}/${fileKey}`);
525
528
  }
526
529
  catch {
527
530
  }
@@ -621,6 +624,10 @@ function orderedVersionDirectories(schemaDir, version) {
621
624
  return [];
622
625
  }
623
626
  }
627
+ function indexSignatureType(additionalType, declaredTypes, hasOptional) {
628
+ const members = [additionalType, ...declaredTypes, ...(hasOptional ? ['undefined'] : [])];
629
+ return [...new Set(members)].join(' | ');
630
+ }
624
631
  function jsonSchemaToTypeScript(schema, indent = 0) {
625
632
  const indentStr = INDENT.repeat(indent);
626
633
  const nextIndent = INDENT.repeat(indent + 1);
@@ -675,12 +682,19 @@ function jsonSchemaToTypeScript(schema, indent = 0) {
675
682
  case 'object':
676
683
  if (schema.properties && Object.keys(schema.properties).length > 0) {
677
684
  const required = new Set(schema.required ?? []);
685
+ const declaredTypes = [];
678
686
  const props = Object.entries(schema.properties).map(([key, propSchema]) => {
679
687
  const optional = required.has(key) ? '' : '?';
680
688
  const quotedKey = needsQuoting(key) ? `'${key}'` : key;
681
689
  const propType = jsonSchemaToTypeScript(propSchema, indent + 1);
690
+ declaredTypes.push(propType);
682
691
  return `${nextIndent}${quotedKey}${optional}: ${propType};`;
683
692
  });
693
+ if (typeof schema.additionalProperties === 'object') {
694
+ const additionalType = jsonSchemaToTypeScript(schema.additionalProperties, indent + 1);
695
+ const hasOptional = Object.keys(schema.properties).some((key) => !required.has(key));
696
+ props.push(`${nextIndent}[key: string]: ${indexSignatureType(additionalType, declaredTypes, hasOptional)};`);
697
+ }
684
698
  return `{\n${props.join('\n')}\n${indentStr}}`;
685
699
  }
686
700
  if (schema.additionalProperties) {
@@ -695,7 +709,20 @@ function jsonSchemaToTypeScript(schema, indent = 0) {
695
709
  }
696
710
  }
697
711
  function findSchemaForOperation(schemas, resource, operation) {
698
- return schemas.find((s) => s.resource.toLowerCase() === resource.toLowerCase() && s.operation === operation);
712
+ return schemas.find((s) => !s.variant &&
713
+ s.resource.toLowerCase() === resource.toLowerCase() &&
714
+ s.operation === operation);
715
+ }
716
+ function splitOperationVariant(fileKey) {
717
+ const dot = fileKey.indexOf('.');
718
+ if (dot <= 0)
719
+ return [fileKey, undefined];
720
+ return [fileKey.slice(0, dot), fileKey.slice(dot + 1)];
721
+ }
722
+ function findVariantSchemasForOperation(schemas, resource, operation) {
723
+ return schemas.filter((s) => s.variant !== undefined &&
724
+ s.resource.toLowerCase() === resource.toLowerCase() &&
725
+ s.operation === operation);
699
726
  }
700
727
  function generateResourceLocatorType(prop) {
701
728
  if (prop.modes && prop.modes.length > 0) {
@@ -1871,7 +1898,104 @@ function generateSharedFile(node, version, _importDepth = 5) {
1871
1898
  lines.push('}');
1872
1899
  return lines.join('\n');
1873
1900
  }
1874
- function generateDiscriminatorFile(node, version, combo, props, schema, _importDepth = 5) {
1901
+ function parseParameterVariant(variant) {
1902
+ const separator = variant.indexOf('-');
1903
+ if (separator <= 0)
1904
+ return undefined;
1905
+ return { param: variant.slice(0, separator), value: variant.slice(separator + 1) };
1906
+ }
1907
+ function resolveParameterVariants(props, variantSchemas) {
1908
+ const parsed = variantSchemas.map((s) => ({
1909
+ ...parseParameterVariant(s.variant ?? ''),
1910
+ schema: s.schema,
1911
+ }));
1912
+ const param = parsed[0]?.param;
1913
+ if (!param || parsed.some((entry) => entry.param !== param || entry.value === undefined)) {
1914
+ return undefined;
1915
+ }
1916
+ const property = props.find((p) => p.name === param);
1917
+ const valueType = property ? getParameterValueType(property) : undefined;
1918
+ if (!property || !valueType)
1919
+ return undefined;
1920
+ const defaultValue = String(property.default);
1921
+ const others = parsed.map((entry) => ({ value: entry.value, schema: entry.schema }));
1922
+ if (others.some((entry) => entry.value === defaultValue || !isTypeSafeValue(valueType, entry.value))) {
1923
+ return undefined;
1924
+ }
1925
+ const suffixes = [defaultValue, ...others.map((entry) => entry.value)].map((value) => parameterValueSuffix(valueType, value));
1926
+ if (new Set(suffixes).size !== suffixes.length)
1927
+ return undefined;
1928
+ return {
1929
+ param,
1930
+ valueType,
1931
+ defaultValue,
1932
+ others: others.sort((a, b) => a.value.localeCompare(b.value)),
1933
+ };
1934
+ }
1935
+ function getParameterValueType(property) {
1936
+ if (property.type === 'boolean') {
1937
+ return typeof property.default === 'boolean' ? 'boolean' : undefined;
1938
+ }
1939
+ if (property.type !== 'options')
1940
+ return undefined;
1941
+ if (typeof property.default === 'number')
1942
+ return 'number';
1943
+ if (typeof property.default === 'boolean')
1944
+ return 'boolean';
1945
+ return typeof property.default === 'string' ? 'string' : undefined;
1946
+ }
1947
+ function parameterValueSuffix(valueType, value) {
1948
+ if (valueType === 'number')
1949
+ return value.replace('-', 'Minus').replace('.', 'Point');
1950
+ return toPascalCase(value);
1951
+ }
1952
+ function isTypeSafeValue(valueType, value) {
1953
+ if (valueType === 'boolean')
1954
+ return value === 'true' || value === 'false';
1955
+ if (valueType === 'number')
1956
+ return Number.isFinite(Number(value)) && String(Number(value)) === value;
1957
+ return /^[\w-]+$/.test(value);
1958
+ }
1959
+ function parameterValueLiteral(variants, value) {
1960
+ return variants.valueType === 'string' ? `'${value}'` : value;
1961
+ }
1962
+ function generateParameterVariantTypes(variants, names, nodeBody) {
1963
+ const { param, defaultValue, others } = variants;
1964
+ const suffixOf = (value) => `${toPascalCase(param)}${parameterValueSuffix(variants.valueType, value)}`;
1965
+ const outputOf = (value) => `${names.base}${suffixOf(value)}Output`;
1966
+ const nodeOf = (value) => `${names.base}${suffixOf(value)}Node`;
1967
+ const lines = [];
1968
+ for (const { value, schema: variantSchema } of others) {
1969
+ lines.push(`export type ${outputOf(value)} = ${jsonSchemaToTypeScript(variantSchema)};`, '');
1970
+ }
1971
+ const members = [
1972
+ {
1973
+ node: nodeOf(defaultValue),
1974
+ constraint: `${param}?: ${parameterValueLiteral(variants, defaultValue)}`,
1975
+ output: names.output,
1976
+ },
1977
+ ...others.map(({ value }) => ({
1978
+ node: nodeOf(value),
1979
+ constraint: `${param}: ${parameterValueLiteral(variants, value)}`,
1980
+ output: outputOf(value),
1981
+ })),
1982
+ ];
1983
+ const allOutputs = members.map((member) => member.output).join(' | ');
1984
+ members.push({
1985
+ node: `${names.base}${toPascalCase(param)}ExpressionNode`,
1986
+ constraint: `${param}: Expression<${variants.valueType}>`,
1987
+ output: allOutputs,
1988
+ });
1989
+ for (const member of members) {
1990
+ lines.push(...nodeBody(member.node, `{ ${member.constraint} }`, member.output), '');
1991
+ }
1992
+ lines.push(`export type ${names.node} =`);
1993
+ members.forEach((member, index) => {
1994
+ lines.push(`${INDENT}| ${member.node}${index === members.length - 1 ? ';' : ''}`);
1995
+ });
1996
+ return lines;
1997
+ }
1998
+ function generateDiscriminatorFile(node, version, combo, props, schema, _importDepth = 5, variantSchemas = []) {
1875
1999
  const prefix = getPackagePrefix(node.name);
1876
2000
  const nodeName = prefix + toPascalCase(getNodeBaseName(node.name));
1877
2001
  const versionSuffix = versionToTypeName(version);
@@ -1976,23 +2100,35 @@ function generateDiscriminatorFile(node, version, combo, props, schema, _importD
1976
2100
  lines.push('');
1977
2101
  }
1978
2102
  }
1979
- lines.push(`export type ${nodeTypeName} = {`);
1980
- lines.push(`${INDENT}type: '${node.name}';`);
1981
- lines.push(`${INDENT}version: ${version};`);
1982
- if (isTrigger) {
1983
- lines.push(`${INDENT}isTrigger: true;`);
1984
- }
1985
2103
  const hasRequiredSubnodes = aiInputTypes.some((input) => input.required);
1986
- const configType = buildNodeConfigType(configName, {
1987
- credentialsTypeName: node.credentials && node.credentials.length > 0 ? 'Credentials' : undefined,
1988
- subnodeConfigTypeName: subnodeConfigTypeName ?? undefined,
1989
- subnodesRequired: hasRequiredSubnodes,
1990
- });
1991
- lines.push(`${INDENT}config: ${configType};`);
1992
- if (schema) {
1993
- lines.push(`${INDENT}output?: Items<${outputTypeName}>;`);
2104
+ const nodeBody = (typeName, paramsConstraint, outputType) => {
2105
+ const paramsType = paramsConstraint ? `${configName} & ${paramsConstraint}` : configName;
2106
+ const configType = buildNodeConfigType(paramsType, {
2107
+ credentialsTypeName: node.credentials && node.credentials.length > 0 ? 'Credentials' : undefined,
2108
+ subnodeConfigTypeName: subnodeConfigTypeName ?? undefined,
2109
+ subnodesRequired: hasRequiredSubnodes,
2110
+ });
2111
+ return [
2112
+ `export type ${typeName} = {`,
2113
+ `${INDENT}type: '${node.name}';`,
2114
+ `${INDENT}version: ${version};`,
2115
+ ...(isTrigger ? [`${INDENT}isTrigger: true;`] : []),
2116
+ `${INDENT}config: ${configType};`,
2117
+ ...(outputType ? [`${INDENT}output?: Items<${outputType}>;`] : []),
2118
+ '};',
2119
+ ];
2120
+ };
2121
+ const variants = schema ? resolveParameterVariants(props, variantSchemas) : undefined;
2122
+ if (variants) {
2123
+ lines.push(...generateParameterVariantTypes(variants, {
2124
+ base: `${nodeName}${versionSuffix}${comboSuffix}`,
2125
+ output: outputTypeName,
2126
+ node: nodeTypeName,
2127
+ }, nodeBody));
2128
+ }
2129
+ else {
2130
+ lines.push(...nodeBody(nodeTypeName, undefined, schema ? outputTypeName : undefined));
1994
2131
  }
1995
- lines.push('};');
1996
2132
  return lines.join('\n');
1997
2133
  }
1998
2134
  function generateResourceIndexFile(node, version, resource, operations) {
@@ -2102,7 +2238,7 @@ function planSplitVersionFiles(node, version) {
2102
2238
  const fileName = `operation_${toSnakeCase(operation)}.ts`;
2103
2239
  const filePath = `${resourceDir}/${fileName}`;
2104
2240
  const matchingSchema = findSchemaForOperation(outputSchemas, resource, operation);
2105
- const content = generateDiscriminatorFile(node, version, combo, props, matchingSchema?.schema, 6);
2241
+ const content = generateDiscriminatorFile(node, version, combo, props, matchingSchema?.schema, 6, findVariantSchemasForOperation(outputSchemas, resource, operation));
2106
2242
  files.set(filePath, content);
2107
2243
  }
2108
2244
  const filteredOperations = operations.filter((op) => !isCustomApiCall(op));