mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-08-20 08:01:35 +00:00
chore: add windmill-utils-internal package (#6299)
* add utils package * naming * cleaning * add docs * remove log * use autogenerated types * remove old * fix * cleaning * add docs
This commit is contained in:
@@ -0,0 +1,86 @@
|
||||
# Dependencies
|
||||
node_modules/
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# Build outputs
|
||||
dist/
|
||||
build/
|
||||
*.tsbuildinfo
|
||||
|
||||
# Generated client
|
||||
src/gen/
|
||||
|
||||
# Environment variables
|
||||
.env
|
||||
.env.local
|
||||
.env.*.local
|
||||
|
||||
# IDE/Editor files
|
||||
.vscode/
|
||||
.idea/
|
||||
*.swp
|
||||
*.swo
|
||||
*~
|
||||
|
||||
# OS generated files
|
||||
.DS_Store
|
||||
.DS_Store?
|
||||
._*
|
||||
.Spotlight-V100
|
||||
.Trashes
|
||||
ehthumbs.db
|
||||
Thumbs.db
|
||||
|
||||
# Logs
|
||||
*.log
|
||||
logs/
|
||||
|
||||
# Runtime data
|
||||
pids/
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage/
|
||||
*.lcov
|
||||
|
||||
# nyc test coverage
|
||||
.nyc_output
|
||||
|
||||
# Dependency directories
|
||||
jspm_packages/
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Microbundle cache
|
||||
.rpt2_cache/
|
||||
.rts2_cache_cjs/
|
||||
.rts2_cache_es/
|
||||
.rts2_cache_umd/
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
|
||||
# parcel-bundler cache (https://parceljs.org/)
|
||||
.cache
|
||||
.parcel-cache
|
||||
|
||||
# TypeScript cache
|
||||
*.tsbuildinfo
|
||||
|
||||
# Temporary folders
|
||||
tmp/
|
||||
temp/
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
brew install gnu-sed
|
||||
|
||||
script_dirpath="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
output_dirpath="${script_dirpath}/src/gen"
|
||||
|
||||
rm -rf "${output_dirpath}"
|
||||
|
||||
npx --yes @hey-api/openapi-ts@0.53.1 --input "${script_dirpath}/../../backend/windmill-api/openapi.yaml" --output "${output_dirpath}" --useOptions --client legacy/fetch --schemas false
|
||||
cat <<EOF - "${output_dirpath}/core/OpenAPI.ts" > temp_file && mv temp_file "${output_dirpath}/core/OpenAPI.ts"
|
||||
const getEnv = (key: string) => {
|
||||
return process.env[key]
|
||||
};
|
||||
|
||||
const baseUrl = getEnv("BASE_INTERNAL_URL") ?? getEnv("BASE_URL") ?? "http://localhost:8000";
|
||||
const baseUrlApi = (baseUrl ?? '') + "/api";
|
||||
|
||||
EOF
|
||||
gsed -i 's/WITH_CREDENTIALS: false/WITH_CREDENTIALS: true/g' "${output_dirpath}/core/OpenAPI.ts"
|
||||
gsed -i 's/TOKEN: undefined/TOKEN: getEnv("WM_TOKEN")/g' "${output_dirpath}/core/OpenAPI.ts"
|
||||
gsed -i "s/BASE: '\/api'/BASE: baseUrlApi/g" "${output_dirpath}/core/OpenAPI.ts"
|
||||
|
||||
find "${output_dirpath}" -name "*.ts" -exec gsed -i -E "s/(import.*from[[:space:]]*['\"][^'\"]+)(['\"])/\1.ts\2/g" {} \;
|
||||
|
||||
find "${output_dirpath}" -name "*.ts" -exec gsed -i -E "s/(export.*from[[:space:]]*['\"][^'\"]+)(['\"])/\1.ts\2/g" {} \;
|
||||
Executable
+24
@@ -0,0 +1,24 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
script_dirpath="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
output_dirpath="${script_dirpath}/src/gen"
|
||||
|
||||
rm -rf "${output_dirpath}"
|
||||
|
||||
npx --yes @hey-api/openapi-ts@0.53.1 --input "${script_dirpath}/../../backend/windmill-api/openapi.yaml" --output "${output_dirpath}" --useOptions --client legacy/fetch --schemas false
|
||||
cat <<EOF - "${output_dirpath}/core/OpenAPI.ts" > temp_file && mv temp_file "${output_dirpath}/core/OpenAPI.ts"
|
||||
const getEnv = (key: string) => {
|
||||
return process.env[key]
|
||||
};
|
||||
|
||||
const baseUrl = getEnv("BASE_INTERNAL_URL") ?? getEnv("BASE_URL") ?? "http://localhost:8000";
|
||||
const baseUrlApi = (baseUrl ?? '') + "/api";
|
||||
|
||||
EOF
|
||||
sed -i 's/WITH_CREDENTIALS: false/WITH_CREDENTIALS: true/g' "${output_dirpath}/core/OpenAPI.ts"
|
||||
sed -i 's/TOKEN: undefined/TOKEN: getEnv("WM_TOKEN")/g' "${output_dirpath}/core/OpenAPI.ts"
|
||||
sed -i "s/BASE: '\/api'/BASE: baseUrlApi/g" "${output_dirpath}/core/OpenAPI.ts"
|
||||
|
||||
find "${output_dirpath}" -name "*.ts" -exec sed -i -E "s/(import.*from[[:space:]]*['\"][^'\"]+)(['\"])/\1.ts\2/g" {} \;
|
||||
|
||||
find "${output_dirpath}" -name "*.ts" -exec sed -i -E "s/(export.*from[[:space:]]*['\"][^'\"]+)(['\"])/\1.ts\2/g" {} \;
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
{
|
||||
"name": "windmill-utils-internal",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "windmill-utils-internal",
|
||||
"version": "1.0.0",
|
||||
"license": "Apache 2.0",
|
||||
"devDependencies": {
|
||||
"@types/node": "^24.1.0",
|
||||
"typescript": "^5.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "24.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.1.0.tgz",
|
||||
"integrity": "sha512-ut5FthK5moxFKH2T1CUOC6ctR67rQRvvHdFLCD2Ql6KXmMuCrjsSsRI9UsLCm9M18BMwClv4pn327UvB7eeO1w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"undici-types": "~7.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/typescript": {
|
||||
"version": "5.8.3",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz",
|
||||
"integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.17"
|
||||
}
|
||||
},
|
||||
"node_modules/undici-types": {
|
||||
"version": "7.8.0",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.8.0.tgz",
|
||||
"integrity": "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"name": "windmill-utils-internal",
|
||||
"version": "1.0.0",
|
||||
"description": "Internal utility functions for Windmill",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"scripts": {
|
||||
"build": "./gen_wm_client.sh && tsc",
|
||||
"build-mac": "./gen_wm_client-mac.sh && tsc",
|
||||
"prepublishOnly": "npm run build"
|
||||
},
|
||||
"keywords": [
|
||||
"windmill"
|
||||
],
|
||||
"author": "Ruben Fiszel",
|
||||
"license": "Apache 2.0",
|
||||
"devDependencies": {
|
||||
"@types/node": "^24.1.0",
|
||||
"typescript": "^5.0.0"
|
||||
},
|
||||
"files": [
|
||||
"dist/**/*"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
// constants.ts
|
||||
/**
|
||||
* Cross-platform path separator constants
|
||||
* Compatible with both Node.js and Deno environments
|
||||
*/
|
||||
|
||||
/**
|
||||
* Detects if the current platform is Windows by checking various environment indicators.
|
||||
* Uses multiple fallback methods to ensure compatibility across Node.js, Deno, and browser environments.
|
||||
*
|
||||
* @returns True if running on Windows, false otherwise
|
||||
*/
|
||||
const isWindows = (() => {
|
||||
// Try Node.js process.platform first (most reliable)
|
||||
if (typeof process !== "undefined" && process.platform) {
|
||||
return process.platform === "win32";
|
||||
}
|
||||
|
||||
// Try Deno specific detection via globalThis
|
||||
if (typeof globalThis !== "undefined" && "Deno" in globalThis) {
|
||||
return (globalThis as any).Deno?.build?.os === "windows";
|
||||
}
|
||||
|
||||
// Try navigator.platform (browser/some Deno contexts)
|
||||
// @ts-ignore
|
||||
if (typeof navigator !== "undefined" && navigator.platform) {
|
||||
// @ts-ignore
|
||||
return navigator.platform.toLowerCase().includes("win");
|
||||
}
|
||||
|
||||
// Fallback - assume Unix-like (safest default)
|
||||
return false;
|
||||
})();
|
||||
|
||||
/**
|
||||
* Path separator constant - equivalent to Deno's SEPARATOR or Node's path.sep
|
||||
* On Windows: "\"
|
||||
* On Unix-like systems: "/"
|
||||
*/
|
||||
export const SEP = isWindows ? "\\" : "/";
|
||||
|
||||
/**
|
||||
* Path delimiter constant for environment variables like PATH
|
||||
* On Windows: ";"
|
||||
* On Unix-like systems: ":"
|
||||
*/
|
||||
export const DELIMITER = isWindows ? ";" : ":";
|
||||
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* @fileoverview Main entry point for windmill-utils-internal package
|
||||
*
|
||||
* This module provides utilities for handling Windmill flows, scripts, and schemas:
|
||||
* - Inline script extraction and replacement
|
||||
* - Path utilities for different programming languages
|
||||
* - Schema parsing and conversion utilities
|
||||
* - Cross-platform path constants
|
||||
*/
|
||||
|
||||
export * from "./inline-scripts";
|
||||
export * from "./path-utils";
|
||||
export * from "./parse";
|
||||
export { SEP, DELIMITER } from "./constants";
|
||||
@@ -0,0 +1,107 @@
|
||||
import { assignPath } from "../path-utils";
|
||||
import { SEP } from "../constants";
|
||||
import { FlowModule } from "../gen/types.gen";
|
||||
|
||||
/**
|
||||
* Represents an inline script extracted from a flow module
|
||||
*/
|
||||
interface InlineScript {
|
||||
/** File path where the script content should be written */
|
||||
path: string;
|
||||
/** The actual script content */
|
||||
content: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts inline scripts from flow modules, converting them to separate files
|
||||
* and replacing the original content with file references.
|
||||
*
|
||||
* @param modules - Array of flow modules to process
|
||||
* @param mapping - Optional mapping of module IDs to custom file paths
|
||||
* @param defaultTs - Default TypeScript runtime to use ("bun" or "deno")
|
||||
* @returns Array of inline scripts with their paths and content
|
||||
*/
|
||||
export function extractInlineScripts(
|
||||
modules: FlowModule[],
|
||||
mapping: Record<string, string> = {},
|
||||
defaultTs?: "bun" | "deno"
|
||||
): InlineScript[] {
|
||||
return modules.flatMap((m) => {
|
||||
if (m.value.type == "rawscript") {
|
||||
const [basePath, ext] = assignPath(
|
||||
m.id,
|
||||
m.value.language,
|
||||
defaultTs
|
||||
);
|
||||
const path = mapping[m.id] ?? basePath + ext;
|
||||
const content = m.value.content;
|
||||
const r = [{ path: path, content: content }];
|
||||
m.value.content = "!inline " + path.replace(SEP, "/");
|
||||
const lock = m.value.lock;
|
||||
if (lock && lock != "") {
|
||||
const lockPath = basePath + "lock";
|
||||
m.value.lock = "!inline " + lockPath.replace(SEP, "/");
|
||||
r.push({ path: lockPath, content: lock });
|
||||
}
|
||||
return r;
|
||||
} else if (m.value.type == "forloopflow") {
|
||||
return extractInlineScripts(m.value.modules, mapping, defaultTs);
|
||||
} else if (m.value.type == "branchall") {
|
||||
return m.value.branches.flatMap((b) =>
|
||||
extractInlineScripts(b.modules, mapping, defaultTs)
|
||||
);
|
||||
} else if (m.value.type == "whileloopflow") {
|
||||
return extractInlineScripts(m.value.modules, mapping, defaultTs);
|
||||
} else if (m.value.type == "branchone") {
|
||||
return [
|
||||
...m.value.branches.flatMap((b) =>
|
||||
extractInlineScripts(b.modules, mapping, defaultTs)
|
||||
),
|
||||
...extractInlineScripts(m.value.default, mapping, defaultTs),
|
||||
];
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the current mapping of module IDs to file paths from flow modules
|
||||
* by analyzing existing inline script references.
|
||||
*
|
||||
* @param modules - Array of flow modules to analyze (can be undefined)
|
||||
* @param mapping - Existing mapping to extend (defaults to empty object)
|
||||
* @returns Record mapping module IDs to their corresponding file paths
|
||||
*/
|
||||
export function extractCurrentMapping(
|
||||
modules: FlowModule[] | undefined,
|
||||
mapping: Record<string, string> = {}
|
||||
): Record<string, string> {
|
||||
if (!modules || !Array.isArray(modules)) {
|
||||
return mapping;
|
||||
}
|
||||
|
||||
modules.forEach((m) => {
|
||||
if (!m?.value?.type) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (m.value.type === "rawscript") {
|
||||
if (m.value.content && m.value.content.startsWith("!inline ")) {
|
||||
mapping[m.id] = m.value.content.trim().split(" ")[1];
|
||||
}
|
||||
} else if (
|
||||
m.value.type === "forloopflow" ||
|
||||
m.value.type === "whileloopflow"
|
||||
) {
|
||||
extractCurrentMapping(m.value.modules, mapping);
|
||||
} else if (m.value.type === "branchall") {
|
||||
m.value.branches.forEach((b) => extractCurrentMapping(b.modules, mapping));
|
||||
} else if (m.value.type === "branchone") {
|
||||
m.value.branches.forEach((b) => extractCurrentMapping(b.modules, mapping));
|
||||
extractCurrentMapping(m.value.default, mapping);
|
||||
}
|
||||
});
|
||||
|
||||
return mapping;
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
export * from "./replacer";
|
||||
export * from "./extractor";
|
||||
@@ -0,0 +1,69 @@
|
||||
import { FlowModule } from "../gen/types.gen.ts";
|
||||
|
||||
/**
|
||||
* Replaces inline script references with actual file content from the filesystem.
|
||||
* This function recursively processes all flow modules and their nested structures.
|
||||
*
|
||||
* @param modules - Array of flow modules to process
|
||||
* @param fileReader - Function to read file content (typically fs.readFile or similar)
|
||||
* @param logger - Optional logger object with info and error methods
|
||||
* @param localPath - Base path for resolving relative file paths
|
||||
* @param removeLocks - Optional array of paths for which to remove lock files
|
||||
* @returns Promise that resolves when all inline scripts have been replaced
|
||||
*/
|
||||
export async function replaceInlineScripts(
|
||||
modules: FlowModule[],
|
||||
fileReader: (...args: any[]) => any,
|
||||
logger: {
|
||||
info: (...args: any[]) => void,
|
||||
error: (...args: any[]) => void,
|
||||
} = {
|
||||
info: () => {},
|
||||
error: () => {},
|
||||
},
|
||||
localPath: string,
|
||||
removeLocks?: string[]
|
||||
): Promise<void> {
|
||||
await Promise.all(modules.map(async (module) => {
|
||||
if (!module.value) {
|
||||
throw new Error(`Module value is undefined for module ${module.id}`);
|
||||
}
|
||||
|
||||
if (module.value.type === "rawscript") {
|
||||
if (module.value.content.startsWith("!inline")) {
|
||||
const path = module.value.content.split(" ")[1];
|
||||
try {
|
||||
module.value.content = await fileReader(path);
|
||||
} catch {
|
||||
logger.error(`Script file ${path} not found`);
|
||||
}
|
||||
const lock = module.value.lock;
|
||||
if (removeLocks && removeLocks.includes(path)) {
|
||||
module.value.lock = undefined;
|
||||
} else if (
|
||||
lock &&
|
||||
typeof lock == "string" &&
|
||||
lock.trimStart().startsWith("!inline ")
|
||||
) {
|
||||
const path = lock.split(" ")[1];
|
||||
try {
|
||||
module.value.lock = await fileReader(path);
|
||||
} catch {
|
||||
logger.error(`Lock file ${path} not found`);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (module.value.type === "forloopflow" || module.value.type === "whileloopflow") {
|
||||
await replaceInlineScripts(module.value.modules, fileReader, logger, localPath, removeLocks);
|
||||
} else if (module.value.type === "branchall") {
|
||||
await Promise.all(module.value.branches.map(async (branch) => {
|
||||
await replaceInlineScripts(branch.modules, fileReader, logger, localPath, removeLocks);
|
||||
}));
|
||||
} else if (module.value.type === "branchone") {
|
||||
await Promise.all(module.value.branches.map(async (branch) => {
|
||||
await replaceInlineScripts(branch.modules, fileReader, logger, localPath, removeLocks);
|
||||
}));
|
||||
await replaceInlineScripts(module.value.default, fileReader, logger, localPath, removeLocks);
|
||||
}
|
||||
}));
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export * from "./parse-schema";
|
||||
@@ -0,0 +1,243 @@
|
||||
/**
|
||||
* Type alias for enum values - can be an array of strings or undefined
|
||||
*/
|
||||
export type EnumType = string[] | undefined
|
||||
|
||||
/**
|
||||
* Represents a property in a JSON schema with various validation and display options
|
||||
*/
|
||||
export interface SchemaProperty {
|
||||
type: string | undefined
|
||||
description?: string
|
||||
pattern?: string
|
||||
default?: any
|
||||
enum?: EnumType
|
||||
contentEncoding?: 'base64' | 'binary'
|
||||
format?: string
|
||||
items?: {
|
||||
type?: 'string' | 'number' | 'bytes' | 'object' | 'resource'
|
||||
contentEncoding?: 'base64'
|
||||
enum?: string[]
|
||||
resourceType?: string
|
||||
properties?: { [name: string]: SchemaProperty }
|
||||
}
|
||||
min?: number
|
||||
max?: number
|
||||
currency?: string
|
||||
currencyLocale?: string
|
||||
multiselect?: boolean
|
||||
customErrorMessage?: string
|
||||
properties?: { [name: string]: SchemaProperty }
|
||||
required?: string[]
|
||||
showExpr?: string
|
||||
password?: boolean
|
||||
order?: string[]
|
||||
nullable?: boolean
|
||||
dateFormat?: string
|
||||
title?: string
|
||||
placeholder?: string
|
||||
oneOf?: SchemaProperty[]
|
||||
originalType?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts argument signature types to JSON schema properties.
|
||||
* This function handles various Windmill-specific types and converts them
|
||||
* to standard JSON schema format while preserving existing property metadata.
|
||||
*
|
||||
* @param t - The argument signature type definition (can be string or complex object types)
|
||||
* @param oldS - Existing schema property to update with new type information
|
||||
*/
|
||||
export function argSigToJsonSchemaType(
|
||||
t:
|
||||
| string
|
||||
| { resource: string | null }
|
||||
| {
|
||||
list:
|
||||
| (string | { name?: string; props?: { key: string; typ: any }[] })
|
||||
| { str: any }
|
||||
| { object: { name?: string; props?: { key: string; typ: any }[] } }
|
||||
| null
|
||||
}
|
||||
| { dynselect: string }
|
||||
| { str: string[] | null }
|
||||
| { object: { name?: string; props?: { key: string; typ: any }[] } }
|
||||
| {
|
||||
oneof: {
|
||||
label: string
|
||||
properties: { key: string; typ: any }[]
|
||||
}[]
|
||||
},
|
||||
oldS: SchemaProperty
|
||||
): void {
|
||||
const newS: SchemaProperty = { type: '' }
|
||||
if (t === 'int') {
|
||||
newS.type = 'integer'
|
||||
} else if (t === 'float') {
|
||||
newS.type = 'number'
|
||||
} else if (t === 'bool') {
|
||||
newS.type = 'boolean'
|
||||
} else if (t === 'email') {
|
||||
newS.type = 'string'
|
||||
newS.format = 'email'
|
||||
} else if (t === 'sql') {
|
||||
newS.type = 'string'
|
||||
newS.format = 'sql'
|
||||
} else if (t === 'yaml') {
|
||||
newS.type = 'string'
|
||||
newS.format = 'yaml'
|
||||
} else if (t === 'bytes') {
|
||||
newS.type = 'string'
|
||||
newS.contentEncoding = 'base64'
|
||||
newS.originalType = 'bytes'
|
||||
} else if (t === 'datetime') {
|
||||
newS.type = 'string'
|
||||
newS.format = 'date-time'
|
||||
} else if (typeof t !== 'string' && 'oneof' in t) {
|
||||
newS.type = 'object'
|
||||
if (t.oneof) {
|
||||
newS.oneOf = t.oneof.map((obj) => {
|
||||
const oldObjS = oldS.oneOf?.find((o) => o?.title === obj.label) ?? undefined
|
||||
const properties: Record<string, any> = {}
|
||||
for (const prop of obj.properties) {
|
||||
if (oldObjS?.properties && prop.key in oldObjS?.properties) {
|
||||
properties[prop.key] = oldObjS?.properties[prop.key]
|
||||
} else {
|
||||
properties[prop.key] = { description: '', type: '' }
|
||||
}
|
||||
argSigToJsonSchemaType(prop.typ, properties[prop.key])
|
||||
}
|
||||
return {
|
||||
type: 'object',
|
||||
title: obj.label,
|
||||
properties,
|
||||
order: oldObjS?.order ?? undefined
|
||||
}
|
||||
})
|
||||
}
|
||||
} else if (typeof t !== 'string' && `object` in t) {
|
||||
newS.type = 'object'
|
||||
if (t.object.name) {
|
||||
newS.format = `resource-${t.object.name}`
|
||||
}
|
||||
if (t.object.props) {
|
||||
const properties: Record<string, any> = {}
|
||||
for (const prop of t.object.props) {
|
||||
if (oldS.properties && prop.key in oldS.properties) {
|
||||
properties[prop.key] = oldS.properties[prop.key]
|
||||
} else {
|
||||
properties[prop.key] = { description: '', type: '' }
|
||||
}
|
||||
argSigToJsonSchemaType(prop.typ, properties[prop.key])
|
||||
}
|
||||
newS.properties = properties
|
||||
}
|
||||
} else if (typeof t !== 'string' && `str` in t) {
|
||||
newS.type = 'string'
|
||||
if (t.str) {
|
||||
newS.originalType = 'enum'
|
||||
newS.enum = t.str
|
||||
} else if (oldS.originalType == 'string' && oldS.enum) {
|
||||
newS.originalType = 'string'
|
||||
newS.enum = oldS.enum
|
||||
} else {
|
||||
newS.originalType = 'string'
|
||||
newS.enum = undefined
|
||||
}
|
||||
} else if (typeof t !== 'string' && `resource` in t) {
|
||||
newS.type = 'object'
|
||||
newS.format = `resource-${t.resource}`
|
||||
} else if (typeof t !== 'string' && `dynselect` in t) {
|
||||
newS.type = 'object'
|
||||
newS.format = `dynselect-${t.dynselect}`
|
||||
} else if (typeof t !== 'string' && `list` in t) {
|
||||
newS.type = 'array'
|
||||
if (t.list === 'int' || t.list === 'float') {
|
||||
newS.items = { type: 'number' }
|
||||
newS.originalType = 'number[]'
|
||||
} else if (t.list === 'bytes') {
|
||||
newS.items = { type: 'string', contentEncoding: 'base64' }
|
||||
newS.originalType = 'bytes[]'
|
||||
} else if (t.list && typeof t.list == 'object' && 'str' in t.list && t.list.str) {
|
||||
newS.items = { type: 'string', enum: t.list.str }
|
||||
newS.originalType = 'enum[]'
|
||||
} else if (t.list == 'string' || (t.list && typeof t.list == 'object' && 'str' in t.list)) {
|
||||
newS.items = { type: 'string', enum: oldS.items?.enum }
|
||||
newS.originalType = 'string[]'
|
||||
} else if (t.list && typeof t.list == 'object' && 'resource' in t.list && t.list.resource) {
|
||||
newS.items = {
|
||||
type: 'resource',
|
||||
resourceType: t.list.resource as string
|
||||
}
|
||||
newS.originalType = 'resource[]'
|
||||
} else if (t.list && typeof t.list == 'object' && 'object' in t.list && t.list.object) {
|
||||
if (t.list.object.name) {
|
||||
newS.format = `resource-${t.list.object.name}`
|
||||
}
|
||||
if (t.list.object.props && t.list.object.props.length > 0) {
|
||||
const properties: Record<string, any> = {}
|
||||
for (const prop of t.list.object.props) {
|
||||
properties[prop.key] = { description: '', type: '' }
|
||||
argSigToJsonSchemaType(prop.typ, properties[prop.key])
|
||||
}
|
||||
newS.items = { type: 'object', properties: properties }
|
||||
} else {
|
||||
newS.items = { type: 'object' }
|
||||
}
|
||||
newS.originalType = 'record[]'
|
||||
} else {
|
||||
newS.items = { type: 'object' }
|
||||
newS.originalType = 'object[]'
|
||||
}
|
||||
} else {
|
||||
newS.type = 'object'
|
||||
}
|
||||
|
||||
const preservedFields = [
|
||||
'description',
|
||||
'pattern',
|
||||
'min',
|
||||
'max',
|
||||
'currency',
|
||||
'currencyLocale',
|
||||
'multiselect',
|
||||
'customErrorMessage',
|
||||
'required',
|
||||
'showExpr',
|
||||
'password',
|
||||
'order',
|
||||
'dateFormat',
|
||||
'title',
|
||||
'placeholder'
|
||||
]
|
||||
|
||||
preservedFields.forEach((field) => {
|
||||
// @ts-ignore
|
||||
if (oldS[field] !== undefined) {
|
||||
// @ts-ignore
|
||||
newS[field] = oldS[field]
|
||||
}
|
||||
})
|
||||
|
||||
if (oldS.type != newS.type) {
|
||||
for (const prop of Object.getOwnPropertyNames(newS)) {
|
||||
if (prop != 'description') {
|
||||
// @ts-ignore
|
||||
delete oldS[prop]
|
||||
}
|
||||
}
|
||||
} else if ((oldS.format == 'date' || oldS.format === 'date-time') && newS.format == 'string') {
|
||||
newS.format = oldS.format
|
||||
} else if (newS.format == 'date-time' && oldS.format == 'date') {
|
||||
newS.format = 'date'
|
||||
} else if (oldS.items?.type != newS.items?.type) {
|
||||
delete oldS.items
|
||||
}
|
||||
|
||||
if (oldS.format && !newS.format) {
|
||||
oldS.format = undefined
|
||||
}
|
||||
|
||||
Object.assign(oldS, newS)
|
||||
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export * from "./path-assigner";
|
||||
@@ -0,0 +1,71 @@
|
||||
import { RawScript } from "../gen/types.gen";
|
||||
|
||||
/**
|
||||
* Union type of all supported programming languages in Windmill
|
||||
*/
|
||||
export type SupportedLanguage = RawScript["language"] | "frontend" | "bunnative" | "oracledb" | "rust" | "csharp" | "nu" | "ansible" | "java" | "duckdb";
|
||||
|
||||
/**
|
||||
* Mapping of supported languages to their file extensions
|
||||
*/
|
||||
export const LANGUAGE_EXTENSIONS: Record<SupportedLanguage, string> = {
|
||||
python3: "py",
|
||||
bun: "bun.ts",
|
||||
deno: "deno.ts",
|
||||
go: "go",
|
||||
bash: "sh",
|
||||
powershell: "ps1",
|
||||
postgresql: "pg.sql",
|
||||
mysql: "my.sql",
|
||||
bigquery: "bq.sql",
|
||||
oracledb: "odb.sql",
|
||||
snowflake: "sf.sql",
|
||||
mssql: "ms.sql",
|
||||
graphql: "gql",
|
||||
nativets: "native.ts",
|
||||
frontend: "frontend.js",
|
||||
php: "php",
|
||||
rust: "rs",
|
||||
csharp: "cs",
|
||||
nu: "nu",
|
||||
ansible: "playbook.yml",
|
||||
java: "java",
|
||||
duckdb: "duckdb.sql",
|
||||
bunnative: "ts"
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets the appropriate file extension for a given programming language.
|
||||
* Handles special cases for TypeScript variants based on the default runtime.
|
||||
*
|
||||
* @param language - The programming language to get extension for
|
||||
* @param defaultTs - Default TypeScript runtime ("bun" or "deno")
|
||||
* @returns File extension string (without the dot)
|
||||
*/
|
||||
export function getLanguageExtension(
|
||||
language: SupportedLanguage,
|
||||
defaultTs: "bun" | "deno" = "bun"
|
||||
): string {
|
||||
if (language === defaultTs || language === "bunnative") {
|
||||
return "ts";
|
||||
}
|
||||
return LANGUAGE_EXTENSIONS[language] || "no_ext";
|
||||
}
|
||||
|
||||
/**
|
||||
* Assigns a file path for an inline script based on its ID and language.
|
||||
* Returns both the base path and extension as separate components.
|
||||
*
|
||||
* @param id - Unique identifier for the script
|
||||
* @param language - Programming language of the script
|
||||
* @param defaultTs - Default TypeScript runtime ("bun" or "deno")
|
||||
* @returns Tuple containing [basePath, extension]
|
||||
*/
|
||||
export function assignPath(
|
||||
id: string,
|
||||
language: SupportedLanguage,
|
||||
defaultTs: "bun" | "deno" = "bun"
|
||||
): [string, string] {
|
||||
const ext = getLanguageExtension(language, defaultTs);
|
||||
return [`${id}.inline_script.`, ext];
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2020",
|
||||
"module": "commonjs",
|
||||
"lib": ["ES2020"],
|
||||
"declaration": true,
|
||||
"outDir": "./dist",
|
||||
"rootDir": "./src",
|
||||
"strict": true,
|
||||
"noUnusedLocals": false,
|
||||
"noUnusedParameters": false,
|
||||
"noImplicitReturns": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"moduleResolution": "node",
|
||||
"baseUrl": "./",
|
||||
"esModuleInterop": true,
|
||||
"experimentalDecorators": true,
|
||||
"emitDecoratorMetadata": true,
|
||||
"skipLibCheck": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"noEmit": true,
|
||||
"allowImportingTsExtensions": true
|
||||
},
|
||||
"include": [
|
||||
"src/**/*"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
"dist"
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user