windmill-utils-internal 1.3.4 → 1.3.6

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.
@@ -1,13 +1,16 @@
1
1
  import { PathAssigner } from "../path-utils/path-assigner";
2
- import { FlowModule } from "../gen/types.gen";
2
+ import { FlowModule, ScriptLang } from "../gen/types.gen";
3
3
  /**
4
4
  * Represents an inline script extracted from a flow module
5
5
  */
6
- interface InlineScript {
6
+ export interface InlineScript {
7
7
  /** File path where the script content should be written */
8
8
  path: string;
9
9
  /** The actual script content */
10
10
  content: string;
11
+ /** The script language */
12
+ language: ScriptLang;
13
+ is_lock: boolean;
11
14
  }
12
15
  /**
13
16
  * Options for extractInlineScripts function
@@ -37,5 +40,4 @@ export declare function extractInlineScripts(modules: FlowModule[], mapping?: Re
37
40
  * @param mapping - Existing mapping to extend (defaults to empty object)
38
41
  * @returns Record mapping module IDs to their corresponding file paths
39
42
  */
40
- export declare function extractCurrentMapping(modules: FlowModule[] | undefined, mapping?: Record<string, string>): Record<string, string>;
41
- export {};
43
+ export declare function extractCurrentMapping(modules: FlowModule[] | undefined, mapping?: Record<string, string>, failureModule?: FlowModule, preprocessorModule?: FlowModule): Record<string, string>;
@@ -6,14 +6,15 @@ const path_assigner_1 = require("../path-utils/path-assigner");
6
6
  function extractRawscriptInline(id, summary, rawscript, mapping, separator, assigner) {
7
7
  const [basePath, ext] = assigner.assignPath(summary ?? id, rawscript.language);
8
8
  const path = mapping[id] ?? basePath + ext;
9
+ const language = rawscript.language;
9
10
  const content = rawscript.content;
10
- const r = [{ path: path, content: content }];
11
+ const r = [{ path: path, content: content, language, is_lock: false }];
11
12
  rawscript.content = "!inline " + path.replaceAll(separator, "/");
12
13
  const lock = rawscript.lock;
13
14
  if (lock && lock != "") {
14
15
  const lockPath = basePath + "lock";
15
16
  rawscript.lock = "!inline " + lockPath.replaceAll(separator, "/");
16
- r.push({ path: lockPath, content: lock });
17
+ r.push({ path: lockPath, content: lock, language, is_lock: true });
17
18
  }
18
19
  return r;
19
20
  }
@@ -74,7 +75,13 @@ function extractInlineScripts(modules, mapping = {}, separator = "/", defaultTs,
74
75
  * @param mapping - Existing mapping to extend (defaults to empty object)
75
76
  * @returns Record mapping module IDs to their corresponding file paths
76
77
  */
77
- function extractCurrentMapping(modules, mapping = {}) {
78
+ function extractCurrentMapping(modules, mapping = {}, failureModule, preprocessorModule) {
79
+ if (failureModule) {
80
+ extractCurrentMapping([failureModule], mapping);
81
+ }
82
+ if (preprocessorModule) {
83
+ extractCurrentMapping([preprocessorModule], mapping);
84
+ }
78
85
  if (!modules || !Array.isArray(modules)) {
79
86
  return mapping;
80
87
  }
@@ -1,4 +1,10 @@
1
- import { FlowModule } from "../gen/types.gen";
1
+ import { FlowModule, FlowValue, RawScript } from "../gen/types.gen";
2
+ export type LocalScriptInfo = {
3
+ content: string;
4
+ language: RawScript["language"];
5
+ lock?: string;
6
+ tag?: string;
7
+ };
2
8
  /**
3
9
  * Replaces inline script references with actual file content from the filesystem.
4
10
  * This function recursively processes all flow modules and their nested structures.
@@ -14,3 +20,24 @@ export declare function replaceInlineScripts(modules: FlowModule[], fileReader:
14
20
  info: (message: string) => void;
15
21
  error: (message: string) => void;
16
22
  } | undefined, localPath: string, separator?: string, removeLocks?: string[]): Promise<void>;
23
+ /**
24
+ * Replaces PathScript ("script" type) modules with RawScript ("rawscript" type) using local file content.
25
+ * This is used during flow preview so that local script changes are tested instead of remote versions.
26
+ *
27
+ * @param modules - Array of flow modules to process
28
+ * @param scriptReader - Function that takes a script path and returns local content/language/lock, or undefined if not found locally
29
+ * @param logger - Logger for info/error messages
30
+ */
31
+ export declare function replacePathScriptsWithLocal(modules: FlowModule[], scriptReader: (scriptPath: string) => Promise<LocalScriptInfo | undefined>, logger?: {
32
+ info: (message: string) => void;
33
+ error: (message: string) => void;
34
+ }): Promise<void>;
35
+ /**
36
+ * Replaces all PathScript modules in a flow value (modules, failure_module, preprocessor_module)
37
+ * with RawScript using local file content.
38
+ */
39
+ export declare function replaceAllPathScriptsWithLocal(flowValue: FlowValue, scriptReader: (scriptPath: string) => Promise<LocalScriptInfo | undefined>, logger?: {
40
+ info: (message: string) => void;
41
+ error: (message: string) => void;
42
+ }): Promise<void>;
43
+ export declare function collectPathScriptPaths(flowValue: FlowValue): string[];
@@ -1,6 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.replaceInlineScripts = replaceInlineScripts;
4
+ exports.replacePathScriptsWithLocal = replacePathScriptsWithLocal;
5
+ exports.replaceAllPathScriptsWithLocal = replaceAllPathScriptsWithLocal;
6
+ exports.collectPathScriptPaths = collectPathScriptPaths;
4
7
  async function replaceRawscriptInline(id, rawscript, fileReader, logger, separator, removeLocks) {
5
8
  if (!rawscript.content || !rawscript.content.startsWith("!inline")) {
6
9
  return;
@@ -86,3 +89,134 @@ async function replaceInlineScripts(modules, fileReader, logger = {
86
89
  }
87
90
  }));
88
91
  }
92
+ /**
93
+ * Replaces PathScript ("script" type) modules with RawScript ("rawscript" type) using local file content.
94
+ * This is used during flow preview so that local script changes are tested instead of remote versions.
95
+ *
96
+ * @param modules - Array of flow modules to process
97
+ * @param scriptReader - Function that takes a script path and returns local content/language/lock, or undefined if not found locally
98
+ * @param logger - Logger for info/error messages
99
+ */
100
+ async function replacePathScriptsWithLocal(modules, scriptReader, logger = {
101
+ info: () => { },
102
+ error: () => { },
103
+ }) {
104
+ await Promise.all(modules.map(async (module) => {
105
+ if (!module.value) {
106
+ return;
107
+ }
108
+ if (module.value.type === "script") {
109
+ const scriptPath = module.value.path;
110
+ const localScript = await scriptReader(scriptPath);
111
+ if (localScript) {
112
+ const pathScript = module.value;
113
+ module.value = {
114
+ type: "rawscript",
115
+ content: localScript.content,
116
+ language: localScript.language,
117
+ lock: localScript.lock,
118
+ path: scriptPath,
119
+ input_transforms: pathScript.input_transforms,
120
+ tag: pathScript.tag_override ?? localScript.tag,
121
+ };
122
+ }
123
+ }
124
+ else if (module.value.type === "forloopflow" || module.value.type === "whileloopflow") {
125
+ await replacePathScriptsWithLocal(module.value.modules, scriptReader, logger);
126
+ }
127
+ else if (module.value.type === "branchall") {
128
+ await Promise.all(module.value.branches.map(async (branch) => {
129
+ await replacePathScriptsWithLocal(branch.modules, scriptReader, logger);
130
+ }));
131
+ }
132
+ else if (module.value.type === "branchone") {
133
+ await Promise.all(module.value.branches.map(async (branch) => {
134
+ await replacePathScriptsWithLocal(branch.modules, scriptReader, logger);
135
+ }));
136
+ await replacePathScriptsWithLocal(module.value.default, scriptReader, logger);
137
+ }
138
+ else if (module.value.type === "aiagent") {
139
+ await Promise.all((module.value.tools ?? []).map(async (tool) => {
140
+ const toolValue = tool.value;
141
+ if (!toolValue || toolValue.tool_type !== "flowmodule" || toolValue.type !== "script") {
142
+ return;
143
+ }
144
+ const localScript = await scriptReader(toolValue.path);
145
+ if (localScript) {
146
+ tool.value = {
147
+ tool_type: "flowmodule",
148
+ type: "rawscript",
149
+ content: localScript.content,
150
+ language: localScript.language,
151
+ lock: localScript.lock,
152
+ path: toolValue.path,
153
+ input_transforms: toolValue.input_transforms,
154
+ tag: toolValue.tag_override ?? localScript.tag,
155
+ };
156
+ }
157
+ }));
158
+ }
159
+ }));
160
+ }
161
+ function collectPathScriptPathsFromModules(modules, paths) {
162
+ for (const module of modules) {
163
+ if (!module.value) {
164
+ continue;
165
+ }
166
+ if (module.value.type === "script") {
167
+ paths.add(module.value.path);
168
+ }
169
+ else if (module.value.type === "forloopflow" ||
170
+ module.value.type === "whileloopflow") {
171
+ collectPathScriptPathsFromModules(module.value.modules, paths);
172
+ }
173
+ else if (module.value.type === "branchall") {
174
+ for (const branch of module.value.branches) {
175
+ collectPathScriptPathsFromModules(branch.modules, paths);
176
+ }
177
+ }
178
+ else if (module.value.type === "branchone") {
179
+ for (const branch of module.value.branches) {
180
+ collectPathScriptPathsFromModules(branch.modules, paths);
181
+ }
182
+ collectPathScriptPathsFromModules(module.value.default, paths);
183
+ }
184
+ else if (module.value.type === "aiagent") {
185
+ for (const tool of module.value.tools ?? []) {
186
+ const toolValue = tool.value;
187
+ if (toolValue &&
188
+ toolValue.tool_type === "flowmodule" &&
189
+ toolValue.type === "script") {
190
+ paths.add(toolValue.path);
191
+ }
192
+ }
193
+ }
194
+ }
195
+ }
196
+ /**
197
+ * Replaces all PathScript modules in a flow value (modules, failure_module, preprocessor_module)
198
+ * with RawScript using local file content.
199
+ */
200
+ async function replaceAllPathScriptsWithLocal(flowValue, scriptReader, logger = {
201
+ info: () => { },
202
+ error: () => { },
203
+ }) {
204
+ await replacePathScriptsWithLocal(flowValue.modules, scriptReader, logger);
205
+ if (flowValue.failure_module) {
206
+ await replacePathScriptsWithLocal([flowValue.failure_module], scriptReader, logger);
207
+ }
208
+ if (flowValue.preprocessor_module) {
209
+ await replacePathScriptsWithLocal([flowValue.preprocessor_module], scriptReader, logger);
210
+ }
211
+ }
212
+ function collectPathScriptPaths(flowValue) {
213
+ const paths = new Set();
214
+ collectPathScriptPathsFromModules(flowValue.modules, paths);
215
+ if (flowValue.failure_module) {
216
+ collectPathScriptPathsFromModules([flowValue.failure_module], paths);
217
+ }
218
+ if (flowValue.preprocessor_module) {
219
+ collectPathScriptPathsFromModules([flowValue.preprocessor_module], paths);
220
+ }
221
+ return [...paths];
222
+ }
@@ -29,6 +29,11 @@ export declare const EXTENSION_TO_LANGUAGE: Record<string, SupportedLanguage>;
29
29
  * @returns The language, or undefined if not recognized
30
30
  */
31
31
  export declare function getLanguageFromExtension(ext: string, defaultTs?: "bun" | "deno"): SupportedLanguage | undefined;
32
+ /**
33
+ * Sanitizes a summary string for use as a filesystem-safe name.
34
+ * Removes or replaces characters that are invalid on common filesystems.
35
+ */
36
+ export declare function sanitizeForFilesystem(summary: string): string;
32
37
  export interface PathAssigner {
33
38
  assignPath(summary: string | undefined, language: SupportedLanguage): [string, string];
34
39
  }
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.EXTENSION_TO_LANGUAGE = exports.LANGUAGE_EXTENSIONS = void 0;
4
4
  exports.getLanguageExtension = getLanguageExtension;
5
5
  exports.getLanguageFromExtension = getLanguageFromExtension;
6
+ exports.sanitizeForFilesystem = sanitizeForFilesystem;
6
7
  exports.newPathAssigner = newPathAssigner;
7
8
  exports.newRawAppPathAssigner = newRawAppPathAssigner;
8
9
  const INLINE_SCRIPT_PREFIX = "inline_script";
@@ -100,6 +101,21 @@ function getLanguageFromExtension(ext, defaultTs = "bun") {
100
101
  }
101
102
  return undefined;
102
103
  }
104
+ /**
105
+ * Sanitizes a summary string for use as a filesystem-safe name.
106
+ * Removes or replaces characters that are invalid on common filesystems.
107
+ */
108
+ function sanitizeForFilesystem(summary) {
109
+ return summary
110
+ .toLowerCase()
111
+ .replaceAll(" ", "_")
112
+ // Remove characters invalid on Windows/Unix/Mac: / \ : * ? " < > |
113
+ // Also remove control characters (0x00-0x1F) and DEL (0x7F)
114
+ // deno-lint-ignore no-control-regex
115
+ .replace(/[/\\:*?"<>|\x00-\x1f\x7f]/g, "")
116
+ // Trim leading/trailing dots and underscores (hidden files, Windows edge cases)
117
+ .replace(/^[._]+|[._]+$/g, "");
118
+ }
103
119
  /**
104
120
  * Creates a new path assigner for inline scripts.
105
121
  *
@@ -117,7 +133,7 @@ function newPathAssigner(defaultTs, options) {
117
133
  const seen_names = new Set();
118
134
  function assignPath(summary, language) {
119
135
  let name;
120
- name = summary?.toLowerCase()?.replaceAll(" ", "_") ?? "";
136
+ name = summary ? sanitizeForFilesystem(summary) : "";
121
137
  let original_name = name;
122
138
  if (name == "") {
123
139
  original_name = INLINE_SCRIPT_PREFIX;
@@ -148,7 +164,7 @@ function newRawAppPathAssigner(defaultTs) {
148
164
  const seen_names = new Set();
149
165
  function assignPath(summary, language) {
150
166
  let name;
151
- name = summary?.toLowerCase()?.replaceAll(" ", "_") ?? "";
167
+ name = summary ? sanitizeForFilesystem(summary) : "";
152
168
  let original_name = name;
153
169
  if (name == "") {
154
170
  original_name = "runnable";
@@ -1,67 +1,23 @@
1
- // Runtime detection
2
- // @ts-ignore - Cross-platform runtime detection
3
- const isDeno = typeof Deno !== "undefined";
4
- // @ts-ignore - Cross-platform runtime detection
5
- const isNode = typeof process !== "undefined" && process.versions?.node;
1
+ import { stat, mkdir } from "node:fs/promises";
6
2
  export const WINDMILL_CONFIG_DIR = "windmill";
7
3
  export const WINDMILL_ACTIVE_WORKSPACE_FILE = "activeWorkspace";
8
4
  export const WINDMILL_WORKSPACE_CONFIG_FILE = "remotes.ndjson";
9
5
  export const INSTANCES_CONFIG_FILE = "instances.ndjson";
10
6
  export const WINDMILL_ACTIVE_INSTANCE_FILE = "activeInstance";
11
- // Cross-platform environment variable access
12
7
  function getEnv(key) {
13
- if (isDeno) {
14
- // @ts-ignore - Deno API
15
- return Deno.env.get(key);
16
- }
17
- else {
18
- // @ts-ignore - Node API
19
- return process.env[key];
20
- }
8
+ return process.env[key];
21
9
  }
22
- // Cross-platform OS detection with normalization
23
10
  function getOS() {
24
- if (isDeno) {
25
- // @ts-ignore - Deno API
26
- return Deno.build.os;
27
- }
28
- else if (isNode) {
29
- // @ts-ignore - Node API
30
- const platform = process.platform;
31
- switch (platform) {
32
- case "linux": return "linux";
33
- case "darwin": return "darwin";
34
- case "win32": return "windows"; // Normalize win32 to windows
35
- default: return null;
36
- }
37
- }
38
- return null;
39
- }
40
- // Cross-platform file system operations
41
- async function stat(path) {
42
- if (isDeno) {
43
- // @ts-ignore - Deno API
44
- return await Deno.stat(path);
45
- }
46
- else {
47
- // @ts-ignore - Node API
48
- const fs = await import('fs/promises');
49
- return await fs.stat(path);
50
- }
51
- }
52
- async function mkdir(path, options) {
53
- if (isDeno) {
54
- // @ts-ignore - Deno API
55
- await Deno.mkdir(path, options);
56
- }
57
- else {
58
- // @ts-ignore - Node API
59
- const fs = await import('fs/promises');
60
- await fs.mkdir(path, options);
11
+ const platform = process.platform;
12
+ switch (platform) {
13
+ case "linux": return "linux";
14
+ case "darwin": return "darwin";
15
+ case "win32": return "windows";
16
+ default: return null;
61
17
  }
62
18
  }
63
19
  function throwIfNotDirectory(fileInfo) {
64
- if (!fileInfo.isDirectory) {
20
+ if (!fileInfo.isDirectory()) {
65
21
  throw new Error("Path is not a directory");
66
22
  }
67
23
  }
@@ -120,18 +76,8 @@ async function ensureDir(dir) {
120
76
  return;
121
77
  }
122
78
  catch (err) {
123
- // Check for file not found error in cross-platform way
124
- if (isDeno) {
125
- // @ts-ignore - Deno API
126
- if (!(err instanceof Deno.errors.NotFound)) {
127
- throw err;
128
- }
129
- }
130
- else {
131
- // Node.js error codes
132
- if (err.code !== 'ENOENT') {
133
- throw err;
134
- }
79
+ if (err.code !== 'ENOENT') {
80
+ throw err;
135
81
  }
136
82
  }
137
83
  // The dir doesn't exist. Create it.
@@ -140,18 +86,8 @@ async function ensureDir(dir) {
140
86
  await mkdir(dir, { recursive: true });
141
87
  }
142
88
  catch (err) {
143
- // Check for already exists error in cross-platform way
144
- if (isDeno) {
145
- // @ts-ignore - Deno API
146
- if (!(err instanceof Deno.errors.AlreadyExists)) {
147
- throw err;
148
- }
149
- }
150
- else {
151
- // Node.js error codes
152
- if (err.code !== 'EEXIST') {
153
- throw err;
154
- }
89
+ if (err.code !== 'EEXIST') {
90
+ throw err;
155
91
  }
156
92
  const fileInfo = await stat(dir);
157
93
  throwIfNotDirectory(fileInfo);
@@ -26,7 +26,7 @@ export const OpenAPI = {
26
26
  PASSWORD: undefined,
27
27
  TOKEN: getEnv("WM_TOKEN"),
28
28
  USERNAME: undefined,
29
- VERSION: '1.628.3',
29
+ VERSION: '1.666.0',
30
30
  WITH_CREDENTIALS: true,
31
31
  interceptors: {
32
32
  request: new Interceptors(),