@nodetool-ai/nodes-utils 0.8.1
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.
- package/README.md +74 -0
- package/dist/.nodetool-build-stamp +1 -0
- package/dist/base64.d.ts +12 -0
- package/dist/base64.d.ts.map +1 -0
- package/dist/base64.js +25 -0
- package/dist/base64.js.map +1 -0
- package/dist/index.d.ts +29 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +26 -0
- package/dist/index.js.map +1 -0
- package/dist/model-bytes.d.ts +22 -0
- package/dist/model-bytes.d.ts.map +1 -0
- package/dist/model-bytes.js +54 -0
- package/dist/model-bytes.js.map +1 -0
- package/dist/node-only-modules.d.ts +19 -0
- package/dist/node-only-modules.d.ts.map +1 -0
- package/dist/node-only-modules.js +30 -0
- package/dist/node-only-modules.js.map +1 -0
- package/dist/platform-tags.d.ts +71 -0
- package/dist/platform-tags.d.ts.map +1 -0
- package/dist/platform-tags.js +120 -0
- package/dist/platform-tags.js.map +1 -0
- package/dist/save-target.d.ts +115 -0
- package/dist/save-target.d.ts.map +1 -0
- package/dist/save-target.js +202 -0
- package/dist/save-target.js.map +1 -0
- package/dist/template.d.ts +33 -0
- package/dist/template.d.ts.map +1 -0
- package/dist/template.js +97 -0
- package/dist/template.js.map +1 -0
- package/package.json +63 -0
package/README.md
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# @nodetool-ai/nodes-utils
|
|
2
|
+
|
|
3
|
+
Shared helpers for node packages: platform tags and lazy Node-only module loaders for [NodeTool](https://nodetool.ai).
|
|
4
|
+
|
|
5
|
+
A small utility crate every `*-nodes` package depends on instead of duplicating helpers or pulling the whole base-nodes barrel: platform-tagging helpers that stamp `static platforms` onto node arrays, lazy loaders for `node:` built-ins so non-portable paths don't block module init on non-Node runtimes, template variable substitution, Buffer-free base64, and the destination rules every `Save*File` node shares.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @nodetool-ai/nodes-utils
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Exported symbols
|
|
14
|
+
|
|
15
|
+
| Symbol | Kind | Description |
|
|
16
|
+
| --- | --- | --- |
|
|
17
|
+
| `tagAsServer` | function | Mark a `_NODES` array as server-only |
|
|
18
|
+
| `tagAsNode` | function | Mark nodes as requiring the Node runtime |
|
|
19
|
+
| `tagAsHybrid` | function | Mark nodes as running on server or browser |
|
|
20
|
+
| `tagAsBrowserGpu` | function | Mark nodes as needing browser WebGPU |
|
|
21
|
+
| `tagAsUniversal` | function | Mark nodes as runnable on any platform |
|
|
22
|
+
| `tagAsContentCard` | function | Tag nodes surfaced as content cards |
|
|
23
|
+
| `loadNodeFsPromises` | function | Lazily import `node:fs/promises` |
|
|
24
|
+
| `loadNodeFsSync` | function | Lazily import `node:fs` |
|
|
25
|
+
| `loadNodePath` | function | Lazily import `node:path` |
|
|
26
|
+
| `loadNodeOs` | function | Lazily import `node:os` |
|
|
27
|
+
| `loadNodeUrl` | function | Lazily import `node:url` |
|
|
28
|
+
| `renderTemplate` | function | Substitute `{{ variable }}` / `{variable}` placeholders |
|
|
29
|
+
| `referencedVariables` | function | List variable names referenced in a template |
|
|
30
|
+
| `base64ToBytes` | function | Decode base64 to `Uint8Array` (Node + browser) |
|
|
31
|
+
| `bytesToBase64` | function | Encode bytes to base64 (Node + browser) |
|
|
32
|
+
| `resolveSaveTarget` | function | Folder + filename → an absolute path, directory created, name numbered on collision |
|
|
33
|
+
| `resolveSaveFolder` | function | Pick the workspace folder or the node's own `folder` property |
|
|
34
|
+
| `uniqueFilePath` | function | `name.ext` → `name_1.ext`, `name_2.ext`, … past what is already there |
|
|
35
|
+
| `folderPathOf` | function | Read a folder property (path, `file://` URI or folder ref) |
|
|
36
|
+
| `SAVE_TO_WORKSPACE_TITLE` | const | Title every save node shows on the workspace toggle |
|
|
37
|
+
| `SAVE_TO_WORKSPACE_DESCRIPTION` | const | Description every save node shows on the workspace toggle |
|
|
38
|
+
| `VISIBLE_WHEN_NOT_SAVING_TO_WORKSPACE` | const | `json_schema_extra` (`visible_when`) that shows the folder field only while the toggle is off |
|
|
39
|
+
|
|
40
|
+
## Usage
|
|
41
|
+
|
|
42
|
+
```ts
|
|
43
|
+
import {
|
|
44
|
+
tagAsServer,
|
|
45
|
+
renderTemplate,
|
|
46
|
+
loadNodePath
|
|
47
|
+
} from "@nodetool-ai/nodes-utils";
|
|
48
|
+
|
|
49
|
+
tagAsServer(_NODES);
|
|
50
|
+
|
|
51
|
+
const text = renderTemplate("Hello {{ name }}", { name: "world" });
|
|
52
|
+
|
|
53
|
+
// Only touches node:path when this path actually runs
|
|
54
|
+
const path = await loadNodePath();
|
|
55
|
+
const full = path.join(dir, "file.txt");
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### Saving a file from a node
|
|
59
|
+
|
|
60
|
+
```ts
|
|
61
|
+
// Writes into the run's workspace folder while `save_to_workspace` is on, and
|
|
62
|
+
// numbers the name (`out_1.png`) rather than overwriting what is there.
|
|
63
|
+
const target = await resolveSaveTarget({
|
|
64
|
+
folder: this.folder,
|
|
65
|
+
filename: dateName(String(this.filename || "out.png")),
|
|
66
|
+
saveToWorkspace: this.save_to_workspace,
|
|
67
|
+
workspaceDir: context?.workspaceDir
|
|
68
|
+
});
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Links
|
|
72
|
+
|
|
73
|
+
- [NodeTool](https://nodetool.ai)
|
|
74
|
+
- [GitHub](https://github.com/nodetool-ai/nodetool)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
2026-09-28T09:19:08.910Z
|
package/dist/base64.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Buffer-free base64 helpers shared by the `*-nodes` packages.
|
|
3
|
+
*
|
|
4
|
+
* `atob`/`btoa` are global on Node ≥ 16 and in every browser, so these work
|
|
5
|
+
* in both environments without touching `node:buffer` (which the web bundle
|
|
6
|
+
* stubs out).
|
|
7
|
+
*/
|
|
8
|
+
/** Decode a base64 string to bytes without `Buffer` (works on Node and browser). */
|
|
9
|
+
export declare function base64ToBytes(b64: string): Uint8Array;
|
|
10
|
+
/** Encode bytes to a base64 string without `Buffer`. */
|
|
11
|
+
export declare function bytesToBase64(bytes: Uint8Array): string;
|
|
12
|
+
//# sourceMappingURL=base64.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"base64.d.ts","sourceRoot":"","sources":["../src/base64.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,oFAAoF;AACpF,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,CAKrD;AAED,wDAAwD;AACxD,wBAAgB,aAAa,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAOvD"}
|
package/dist/base64.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Buffer-free base64 helpers shared by the `*-nodes` packages.
|
|
3
|
+
*
|
|
4
|
+
* `atob`/`btoa` are global on Node ≥ 16 and in every browser, so these work
|
|
5
|
+
* in both environments without touching `node:buffer` (which the web bundle
|
|
6
|
+
* stubs out).
|
|
7
|
+
*/
|
|
8
|
+
/** Decode a base64 string to bytes without `Buffer` (works on Node and browser). */
|
|
9
|
+
export function base64ToBytes(b64) {
|
|
10
|
+
const binary = atob(b64);
|
|
11
|
+
const out = new Uint8Array(binary.length);
|
|
12
|
+
for (let i = 0; i < binary.length; i++)
|
|
13
|
+
out[i] = binary.charCodeAt(i);
|
|
14
|
+
return out;
|
|
15
|
+
}
|
|
16
|
+
/** Encode bytes to a base64 string without `Buffer`. */
|
|
17
|
+
export function bytesToBase64(bytes) {
|
|
18
|
+
let binary = "";
|
|
19
|
+
const chunk = 0x8000;
|
|
20
|
+
for (let i = 0; i < bytes.length; i += chunk) {
|
|
21
|
+
binary += String.fromCharCode(...bytes.subarray(i, i + chunk));
|
|
22
|
+
}
|
|
23
|
+
return btoa(binary);
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=base64.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"base64.js","sourceRoot":"","sources":["../src/base64.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,oFAAoF;AACpF,MAAM,UAAU,aAAa,CAAC,GAAW;IACvC,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC;IACzB,MAAM,GAAG,GAAG,IAAI,UAAU,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IAC1C,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE;QAAE,GAAG,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;IACtE,OAAO,GAAG,CAAC;AACb,CAAC;AAED,wDAAwD;AACxD,MAAM,UAAU,aAAa,CAAC,KAAiB;IAC7C,IAAI,MAAM,GAAG,EAAE,CAAC;IAChB,MAAM,KAAK,GAAG,MAAM,CAAC;IACrB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,IAAI,KAAK,EAAE,CAAC;QAC7C,MAAM,IAAI,MAAM,CAAC,YAAY,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC;IACjE,CAAC;IACD,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC;AACtB,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @nodetool-ai/nodes-utils — shared helpers for every `*-nodes` package.
|
|
3
|
+
*
|
|
4
|
+
* A few small concerns live here so each per-domain package can depend on
|
|
5
|
+
* a single utility crate instead of duplicating these helpers or pulling
|
|
6
|
+
* the entire base-nodes barrel just to access them:
|
|
7
|
+
*
|
|
8
|
+
* - `platform-tags.ts` — `tagAsServer` / `tagAsHybrid` / `tagAsUniversal`
|
|
9
|
+
* helpers that stamp `static platforms` on `_NODES` arrays.
|
|
10
|
+
* - `node-only-modules.ts` — lazy loaders for `node:fs` / `node:path` /
|
|
11
|
+
* etc. so non-portable code paths don't block module init on
|
|
12
|
+
* non-Node runtimes.
|
|
13
|
+
* - `template.ts` — `{{ variable }}` / `{variable}` substitution shared by
|
|
14
|
+
* the Prompt, Format Text and Agent nodes.
|
|
15
|
+
* - `base64.ts` — Buffer-free base64 encode/decode usable in Node and the
|
|
16
|
+
* browser bundle alike.
|
|
17
|
+
* - `save-target.ts` — the `save_to_workspace` toggle and the numbered
|
|
18
|
+
* filenames every `Save*File` node shares.
|
|
19
|
+
*/
|
|
20
|
+
export { tagAsServer, tagAsNode, tagAsHybrid, tagAsBrowserGpu, tagAsUniversal, tagAsContentCard } from "./platform-tags.js";
|
|
21
|
+
export { loadNodeFsPromises, loadNodeFsSync, loadNodePath, loadNodeOs, loadNodeUrl } from "./node-only-modules.js";
|
|
22
|
+
export { renderTemplate, referencedVariables } from "./template.js";
|
|
23
|
+
export type { TemplateValue, TemplateVars } from "./template.js";
|
|
24
|
+
export { base64ToBytes, bytesToBase64 } from "./base64.js";
|
|
25
|
+
export { resolveModelBytes } from "./model-bytes.js";
|
|
26
|
+
export type { ModelBytesRefLike } from "./model-bytes.js";
|
|
27
|
+
export { SAVE_TO_WORKSPACE_TITLE, SAVE_TO_WORKSPACE_DESCRIPTION, VISIBLE_WHEN_NOT_SAVING_TO_WORKSPACE, folderPathOf, resolveSaveFolder, saveFilename, uniqueFilePath, resolveSaveTarget, writeSavedFile } from "./save-target.js";
|
|
28
|
+
export type { SaveFilenameOptions, SaveFolderOptions, SaveTargetOptions, SaveWorkspace } from "./save-target.js";
|
|
29
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EACL,WAAW,EACX,SAAS,EACT,WAAW,EACX,eAAe,EACf,cAAc,EACd,gBAAgB,EACjB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EACL,kBAAkB,EAClB,cAAc,EACd,YAAY,EACZ,UAAU,EACV,WAAW,EACZ,MAAM,wBAAwB,CAAC;AAEhC,OAAO,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AACpE,YAAY,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAEjE,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE3D,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,YAAY,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAE1D,OAAO,EACL,uBAAuB,EACvB,6BAA6B,EAC7B,oCAAoC,EACpC,YAAY,EACZ,iBAAiB,EACjB,YAAY,EACZ,cAAc,EACd,iBAAiB,EACjB,cAAc,EACf,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EACV,mBAAmB,EACnB,iBAAiB,EACjB,iBAAiB,EACjB,aAAa,EACd,MAAM,kBAAkB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @nodetool-ai/nodes-utils — shared helpers for every `*-nodes` package.
|
|
3
|
+
*
|
|
4
|
+
* A few small concerns live here so each per-domain package can depend on
|
|
5
|
+
* a single utility crate instead of duplicating these helpers or pulling
|
|
6
|
+
* the entire base-nodes barrel just to access them:
|
|
7
|
+
*
|
|
8
|
+
* - `platform-tags.ts` — `tagAsServer` / `tagAsHybrid` / `tagAsUniversal`
|
|
9
|
+
* helpers that stamp `static platforms` on `_NODES` arrays.
|
|
10
|
+
* - `node-only-modules.ts` — lazy loaders for `node:fs` / `node:path` /
|
|
11
|
+
* etc. so non-portable code paths don't block module init on
|
|
12
|
+
* non-Node runtimes.
|
|
13
|
+
* - `template.ts` — `{{ variable }}` / `{variable}` substitution shared by
|
|
14
|
+
* the Prompt, Format Text and Agent nodes.
|
|
15
|
+
* - `base64.ts` — Buffer-free base64 encode/decode usable in Node and the
|
|
16
|
+
* browser bundle alike.
|
|
17
|
+
* - `save-target.ts` — the `save_to_workspace` toggle and the numbered
|
|
18
|
+
* filenames every `Save*File` node shares.
|
|
19
|
+
*/
|
|
20
|
+
export { tagAsServer, tagAsNode, tagAsHybrid, tagAsBrowserGpu, tagAsUniversal, tagAsContentCard } from "./platform-tags.js";
|
|
21
|
+
export { loadNodeFsPromises, loadNodeFsSync, loadNodePath, loadNodeOs, loadNodeUrl } from "./node-only-modules.js";
|
|
22
|
+
export { renderTemplate, referencedVariables } from "./template.js";
|
|
23
|
+
export { base64ToBytes, bytesToBase64 } from "./base64.js";
|
|
24
|
+
export { resolveModelBytes } from "./model-bytes.js";
|
|
25
|
+
export { SAVE_TO_WORKSPACE_TITLE, SAVE_TO_WORKSPACE_DESCRIPTION, VISIBLE_WHEN_NOT_SAVING_TO_WORKSPACE, folderPathOf, resolveSaveFolder, saveFilename, uniqueFilePath, resolveSaveTarget, writeSavedFile } from "./save-target.js";
|
|
26
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EACL,WAAW,EACX,SAAS,EACT,WAAW,EACX,eAAe,EACf,cAAc,EACd,gBAAgB,EACjB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EACL,kBAAkB,EAClB,cAAc,EACd,YAAY,EACZ,UAAU,EACV,WAAW,EACZ,MAAM,wBAAwB,CAAC;AAEhC,OAAO,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAGpE,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE3D,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAGrD,OAAO,EACL,uBAAuB,EACvB,6BAA6B,EAC7B,oCAAoC,EACpC,YAAY,EACZ,iBAAiB,EACjB,YAAY,EACZ,cAAc,EACd,iBAAiB,EACjB,cAAc,EACf,MAAM,kBAAkB,CAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Browser-safe model-bytes resolution (inline data → storage → uri fetch).
|
|
3
|
+
*
|
|
4
|
+
* Moved here from `packages/video-nodes/src/nodes/model3d/render.ts` so the
|
|
5
|
+
* Blender nodes and `RenderToImage` share it. Stays browser-safe: no
|
|
6
|
+
* `node:path` at module scope — the one Node-only read (`file://`) loads
|
|
7
|
+
* `node:fs/promises` and `node:url` through a dynamic import that only runs
|
|
8
|
+
* when `IS_NODE` is set.
|
|
9
|
+
*/
|
|
10
|
+
import type { ProcessingContext } from "@nodetool-ai/runtime";
|
|
11
|
+
/**
|
|
12
|
+
* The fields of a model ref this resolution reads. Mirrors
|
|
13
|
+
* `Model3DRefLike` in video-nodes; node props arrive as `any`, so anything
|
|
14
|
+
* outside this shape is a runtime edge the null checks below absorb.
|
|
15
|
+
*/
|
|
16
|
+
export interface ModelBytesRefLike {
|
|
17
|
+
data?: Uint8Array | string;
|
|
18
|
+
uri?: string;
|
|
19
|
+
}
|
|
20
|
+
/** Browser-safe model-bytes resolution (inline data → storage → uri fetch). */
|
|
21
|
+
export declare function resolveModelBytes(model: ModelBytesRefLike | null | undefined, context?: ProcessingContext): Promise<Uint8Array>;
|
|
22
|
+
//# sourceMappingURL=model-bytes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"model-bytes.d.ts","sourceRoot":"","sources":["../src/model-bytes.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAKH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAI9D;;;;GAIG;AACH,MAAM,WAAW,iBAAiB;IAChC,IAAI,CAAC,EAAE,UAAU,GAAG,MAAM,CAAC;IAC3B,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,+EAA+E;AAC/E,wBAAsB,iBAAiB,CACrC,KAAK,EAAE,iBAAiB,GAAG,IAAI,GAAG,SAAS,EAC3C,OAAO,CAAC,EAAE,iBAAiB,GAC1B,OAAO,CAAC,UAAU,CAAC,CAyCrB"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Browser-safe model-bytes resolution (inline data → storage → uri fetch).
|
|
3
|
+
*
|
|
4
|
+
* Moved here from `packages/video-nodes/src/nodes/model3d/render.ts` so the
|
|
5
|
+
* Blender nodes and `RenderToImage` share it. Stays browser-safe: no
|
|
6
|
+
* `node:path` at module scope — the one Node-only read (`file://`) loads
|
|
7
|
+
* `node:fs/promises` and `node:url` through a dynamic import that only runs
|
|
8
|
+
* when `IS_NODE` is set.
|
|
9
|
+
*/
|
|
10
|
+
import { IS_NODE } from "@nodetool-ai/config";
|
|
11
|
+
import { isNonEmptyString } from "@nodetool-ai/protocol";
|
|
12
|
+
import { fetchExternalMedia } from "@nodetool-ai/runtime";
|
|
13
|
+
import { base64ToBytes } from "./base64.js";
|
|
14
|
+
/** Browser-safe model-bytes resolution (inline data → storage → uri fetch). */
|
|
15
|
+
export async function resolveModelBytes(model, context) {
|
|
16
|
+
if (model === null || model === undefined)
|
|
17
|
+
return new Uint8Array();
|
|
18
|
+
const ref = model;
|
|
19
|
+
if (ref.data instanceof Uint8Array && ref.data.length > 0) {
|
|
20
|
+
return ref.data;
|
|
21
|
+
}
|
|
22
|
+
if (ref.data !== undefined && isNonEmptyString(ref.data)) {
|
|
23
|
+
return base64ToBytes(ref.data);
|
|
24
|
+
}
|
|
25
|
+
const uri = ref.uri ?? "";
|
|
26
|
+
if (uri === "")
|
|
27
|
+
return new Uint8Array();
|
|
28
|
+
if (uri.startsWith("data:")) {
|
|
29
|
+
const comma = uri.indexOf(",");
|
|
30
|
+
if (comma === -1)
|
|
31
|
+
throw new Error(`Malformed data URI: ${uri.slice(0, 32)}…`);
|
|
32
|
+
return base64ToBytes(uri.slice(comma + 1));
|
|
33
|
+
}
|
|
34
|
+
if (context?.storage) {
|
|
35
|
+
const stored = await context.storage.retrieve(uri);
|
|
36
|
+
if (stored && stored.length > 0)
|
|
37
|
+
return new Uint8Array(stored);
|
|
38
|
+
}
|
|
39
|
+
if (uri.startsWith("file://") && IS_NODE) {
|
|
40
|
+
const { readFile } = await import("node:fs/promises");
|
|
41
|
+
const { fileURLToPath } = await import("node:url");
|
|
42
|
+
return new Uint8Array(await readFile(fileURLToPath(uri)));
|
|
43
|
+
}
|
|
44
|
+
if (uri.startsWith("http://") || uri.startsWith("https://")) {
|
|
45
|
+
// Caller-supplied media uri — the media-ref egress policy decides.
|
|
46
|
+
const res = await fetchExternalMedia(uri);
|
|
47
|
+
if (!res.ok) {
|
|
48
|
+
throw new Error(`Failed to fetch model (${res.status}): ${uri}`);
|
|
49
|
+
}
|
|
50
|
+
return new Uint8Array(await res.arrayBuffer());
|
|
51
|
+
}
|
|
52
|
+
return new Uint8Array();
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=model-bytes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"model-bytes.js","sourceRoot":"","sources":["../src/model-bytes.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAC9C,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAG1D,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAY5C,+EAA+E;AAC/E,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,KAA2C,EAC3C,OAA2B;IAE3B,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,IAAI,UAAU,EAAE,CAAC;IACnE,MAAM,GAAG,GAAG,KAAK,CAAC;IAElB,IAAI,GAAG,CAAC,IAAI,YAAY,UAAU,IAAI,GAAG,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC1D,OAAO,GAAG,CAAC,IAAI,CAAC;IAClB,CAAC;IACD,IAAI,GAAG,CAAC,IAAI,KAAK,SAAS,IAAI,gBAAgB,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;QACzD,OAAO,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACjC,CAAC;IAED,MAAM,GAAG,GAAG,GAAG,CAAC,GAAG,IAAI,EAAE,CAAC;IAC1B,IAAI,GAAG,KAAK,EAAE;QAAE,OAAO,IAAI,UAAU,EAAE,CAAC;IAExC,IAAI,GAAG,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;QAC5B,MAAM,KAAK,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QAC/B,IAAI,KAAK,KAAK,CAAC,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,uBAAuB,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC;QAC9E,OAAO,aAAa,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC;IAC7C,CAAC;IAED,IAAI,OAAO,EAAE,OAAO,EAAE,CAAC;QACrB,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;QACnD,IAAI,MAAM,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC;YAAE,OAAO,IAAI,UAAU,CAAC,MAAM,CAAC,CAAC;IACjE,CAAC;IAED,IAAI,GAAG,CAAC,UAAU,CAAC,SAAS,CAAC,IAAI,OAAO,EAAE,CAAC;QACzC,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,MAAM,CAAC,kBAAkB,CAAC,CAAC;QACtD,MAAM,EAAE,aAAa,EAAE,GAAG,MAAM,MAAM,CAAC,UAAU,CAAC,CAAC;QACnD,OAAO,IAAI,UAAU,CAAC,MAAM,QAAQ,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;IAC5D,CAAC;IAED,IAAI,GAAG,CAAC,UAAU,CAAC,SAAS,CAAC,IAAI,GAAG,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC5D,mEAAmE;QACnE,MAAM,GAAG,GAAG,MAAM,kBAAkB,CAAC,GAAG,CAAC,CAAC;QAC1C,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;YACZ,MAAM,IAAI,KAAK,CAAC,0BAA0B,GAAG,CAAC,MAAM,MAAM,GAAG,EAAE,CAAC,CAAC;QACnE,CAAC;QACD,OAAO,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,WAAW,EAAE,CAAC,CAAC;IACjD,CAAC;IAED,OAAO,IAAI,UAAU,EAAE,CAAC;AAC1B,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lazy loaders for Node.js built-in modules that block static portability.
|
|
3
|
+
*
|
|
4
|
+
* Nodes that call into these should declare `static platforms = ["node"]`
|
|
5
|
+
* — the module remains loadable on every runtime (the dynamic import is
|
|
6
|
+
* never evaluated at module-init time), but the specific node fails at
|
|
7
|
+
* call time if invoked off-Node. The platform filter prevents that from
|
|
8
|
+
* happening in normal use.
|
|
9
|
+
*
|
|
10
|
+
* Centralised here so the classifier can attribute the dynamic-import
|
|
11
|
+
* blocker to a single source path; per-module imports of these helpers
|
|
12
|
+
* are relative and don't appear in the external-import set.
|
|
13
|
+
*/
|
|
14
|
+
export declare function loadNodeFsPromises(): Promise<typeof import("node:fs").promises>;
|
|
15
|
+
export declare function loadNodeFsSync(): Promise<typeof import("node:fs")>;
|
|
16
|
+
export declare function loadNodePath(): Promise<typeof import("node:path")>;
|
|
17
|
+
export declare function loadNodeOs(): Promise<typeof import("node:os")>;
|
|
18
|
+
export declare function loadNodeUrl(): Promise<typeof import("node:url")>;
|
|
19
|
+
//# sourceMappingURL=node-only-modules.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"node-only-modules.d.ts","sourceRoot":"","sources":["../src/node-only-modules.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,wBAAsB,kBAAkB,IAAI,OAAO,CACjD,cAAc,SAAS,EAAE,QAAQ,CAClC,CAGA;AAED,wBAAsB,cAAc,IAAI,OAAO,CAAC,cAAc,SAAS,CAAC,CAAC,CAExE;AAED,wBAAsB,YAAY,IAAI,OAAO,CAAC,cAAc,WAAW,CAAC,CAAC,CAExE;AAED,wBAAsB,UAAU,IAAI,OAAO,CAAC,cAAc,SAAS,CAAC,CAAC,CAEpE;AAED,wBAAsB,WAAW,IAAI,OAAO,CAAC,cAAc,UAAU,CAAC,CAAC,CAEtE"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lazy loaders for Node.js built-in modules that block static portability.
|
|
3
|
+
*
|
|
4
|
+
* Nodes that call into these should declare `static platforms = ["node"]`
|
|
5
|
+
* — the module remains loadable on every runtime (the dynamic import is
|
|
6
|
+
* never evaluated at module-init time), but the specific node fails at
|
|
7
|
+
* call time if invoked off-Node. The platform filter prevents that from
|
|
8
|
+
* happening in normal use.
|
|
9
|
+
*
|
|
10
|
+
* Centralised here so the classifier can attribute the dynamic-import
|
|
11
|
+
* blocker to a single source path; per-module imports of these helpers
|
|
12
|
+
* are relative and don't appear in the external-import set.
|
|
13
|
+
*/
|
|
14
|
+
export async function loadNodeFsPromises() {
|
|
15
|
+
const mod = await import("node:fs");
|
|
16
|
+
return mod.promises;
|
|
17
|
+
}
|
|
18
|
+
export async function loadNodeFsSync() {
|
|
19
|
+
return import("node:fs");
|
|
20
|
+
}
|
|
21
|
+
export async function loadNodePath() {
|
|
22
|
+
return import("node:path");
|
|
23
|
+
}
|
|
24
|
+
export async function loadNodeOs() {
|
|
25
|
+
return import("node:os");
|
|
26
|
+
}
|
|
27
|
+
export async function loadNodeUrl() {
|
|
28
|
+
return import("node:url");
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=node-only-modules.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"node-only-modules.js","sourceRoot":"","sources":["../src/node-only-modules.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,MAAM,CAAC,KAAK,UAAU,kBAAkB;IAGtC,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,SAAS,CAAC,CAAC;IACpC,OAAO,GAAG,CAAC,QAAQ,CAAC;AACtB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,cAAc;IAClC,OAAO,MAAM,CAAC,SAAS,CAAC,CAAC;AAC3B,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,YAAY;IAChC,OAAO,MAAM,CAAC,WAAW,CAAC,CAAC;AAC7B,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU;IAC9B,OAAO,MAAM,CAAC,SAAS,CAAC,CAAC;AAC3B,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW;IAC/B,OAAO,MAAM,CAAC,UAAU,CAAC,CAAC;AAC5B,CAAC"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Platform tagging helpers for base-nodes.
|
|
3
|
+
*
|
|
4
|
+
* `tagAsServer(classes)` stamps `static platforms = SERVER_PLATFORMS` on
|
|
5
|
+
* every class in a registration array — declaring support for `node`,
|
|
6
|
+
* `workers`, and `edge` but **not** `browser`. This is the right default
|
|
7
|
+
* for nodes that make outbound HTTP calls or read env secrets (most LLM,
|
|
8
|
+
* HTTP, search, and integration nodes) — they can't be safely shipped to
|
|
9
|
+
* a browser tab without a separate CORS/secrets story.
|
|
10
|
+
*
|
|
11
|
+
* `tagAsHybrid(classes)` stamps `["node", "browser"]` — for nodes with a
|
|
12
|
+
* Node execution path AND a browser one but no V8-isolate path, typical
|
|
13
|
+
* of WebGPU shader nodes (Workers and Edge runtimes lack WebGPU).
|
|
14
|
+
*
|
|
15
|
+
* `tagAsUniversal(classes)` stamps `ALL_PLATFORMS` — for nodes that
|
|
16
|
+
* genuinely work on every runtime including the browser. Use sparingly:
|
|
17
|
+
* the node must handle CORS / secret-exposure / bundle-size implications
|
|
18
|
+
* itself.
|
|
19
|
+
*
|
|
20
|
+
* All three helpers leave alone any class that already declares its own
|
|
21
|
+
* `static platforms` — per-class overrides always win. The mutation is a
|
|
22
|
+
* one-time `Object.defineProperty` at module load.
|
|
23
|
+
*/
|
|
24
|
+
import type { NodeClass } from "@nodetool-ai/node-sdk";
|
|
25
|
+
/** Tag every class in `classes` as supporting node + workers + edge. */
|
|
26
|
+
export declare function tagAsServer<T extends readonly NodeClass[]>(classes: T): T;
|
|
27
|
+
/**
|
|
28
|
+
* Tag every class in `classes` as supporting **only** the node tier — the
|
|
29
|
+
* (otherwise implicit) most-restrictive default. Use for nodes that spawn a
|
|
30
|
+
* subprocess (ffmpeg, ffprobe, yt-dlp, …) or write os.tmpdir() temp files:
|
|
31
|
+
* the workers (Cloudflare V8) and edge (Vercel/Deno) tiers have no subprocess
|
|
32
|
+
* and no filesystem, so offering them there is a guaranteed runtime failure.
|
|
33
|
+
* Stamping it explicitly documents the intent rather than relying on the
|
|
34
|
+
* unset default. Leaves any class with its own `static platforms` alone.
|
|
35
|
+
*/
|
|
36
|
+
export declare function tagAsNode<T extends readonly NodeClass[]>(classes: T): T;
|
|
37
|
+
/**
|
|
38
|
+
* Tag every class in `classes` as supporting node + browser (no V8 server
|
|
39
|
+
* isolates). Use for WebGPU / Canvas / DOM-backed nodes that have both a
|
|
40
|
+
* Node fallback and a browser-native execution path.
|
|
41
|
+
*/
|
|
42
|
+
export declare function tagAsHybrid<T extends readonly NodeClass[]>(classes: T): T;
|
|
43
|
+
/**
|
|
44
|
+
* Tag every class in `classes` as supporting every defined platform.
|
|
45
|
+
* Reserve for pure-JS nodes with no network calls, no secrets, and no
|
|
46
|
+
* platform-specific side effects (e.g. text/data transforms).
|
|
47
|
+
*/
|
|
48
|
+
export declare function tagAsUniversal<T extends readonly NodeClass[]>(classes: T): T;
|
|
49
|
+
/**
|
|
50
|
+
* Like {@link tagAsHybrid} (node + browser) but additionally marks the classes
|
|
51
|
+
* as requiring a WebGPU device in the browser. WebGPU shader nodes use this so
|
|
52
|
+
* the in-browser runner routes graphs containing them to the server — where a
|
|
53
|
+
* GPU is always available via Dawn — when `navigator.gpu` is missing, instead
|
|
54
|
+
* of failing the run. Server execution is unchanged.
|
|
55
|
+
*
|
|
56
|
+
* `requiresGpu` is stamped only on classes that end up browser-capable: a class
|
|
57
|
+
* already tagged server keeps its platforms and is left unmarked (it can't run
|
|
58
|
+
* in the browser anyway). Per-class `static requiresGpu` overrides win.
|
|
59
|
+
*/
|
|
60
|
+
export declare function tagAsBrowserGpu<T extends readonly NodeClass[]>(classes: T): T;
|
|
61
|
+
/**
|
|
62
|
+
* Stamp `static body = "content_card"` on every class in `classes` so the
|
|
63
|
+
* editor renders them as a media content card (the image/video/audio preview
|
|
64
|
+
* body) instead of the generic input/output layout. Leaves any class that
|
|
65
|
+
* already declares its own `static body` alone — per-class overrides win.
|
|
66
|
+
*
|
|
67
|
+
* Apply to media-output node groups, composing with a platform tagger:
|
|
68
|
+
* `tagAsHybrid(tagAsContentCard([...]))`.
|
|
69
|
+
*/
|
|
70
|
+
export declare function tagAsContentCard<T extends readonly NodeClass[]>(classes: T): T;
|
|
71
|
+
//# sourceMappingURL=platform-tags.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"platform-tags.d.ts","sourceRoot":"","sources":["../src/platform-tags.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAyBvD,wEAAwE;AACxE,wBAAgB,WAAW,CAAC,CAAC,SAAS,SAAS,SAAS,EAAE,EAAE,OAAO,EAAE,CAAC,GAAG,CAAC,CAEzE;AAED;;;;;;;;GAQG;AACH,wBAAgB,SAAS,CAAC,CAAC,SAAS,SAAS,SAAS,EAAE,EAAE,OAAO,EAAE,CAAC,GAAG,CAAC,CAEvE;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,CAAC,SAAS,SAAS,SAAS,EAAE,EAAE,OAAO,EAAE,CAAC,GAAG,CAAC,CAEzE;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,CAAC,SAAS,SAAS,SAAS,EAAE,EAAE,OAAO,EAAE,CAAC,GAAG,CAAC,CAE5E;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAAC,CAAC,SAAS,SAAS,SAAS,EAAE,EAAE,OAAO,EAAE,CAAC,GAAG,CAAC,CAc7E;AAED;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,SAAS,SAAS,SAAS,EAAE,EAAE,OAAO,EAAE,CAAC,GAAG,CAAC,CAW9E"}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Platform tagging helpers for base-nodes.
|
|
3
|
+
*
|
|
4
|
+
* `tagAsServer(classes)` stamps `static platforms = SERVER_PLATFORMS` on
|
|
5
|
+
* every class in a registration array — declaring support for `node`,
|
|
6
|
+
* `workers`, and `edge` but **not** `browser`. This is the right default
|
|
7
|
+
* for nodes that make outbound HTTP calls or read env secrets (most LLM,
|
|
8
|
+
* HTTP, search, and integration nodes) — they can't be safely shipped to
|
|
9
|
+
* a browser tab without a separate CORS/secrets story.
|
|
10
|
+
*
|
|
11
|
+
* `tagAsHybrid(classes)` stamps `["node", "browser"]` — for nodes with a
|
|
12
|
+
* Node execution path AND a browser one but no V8-isolate path, typical
|
|
13
|
+
* of WebGPU shader nodes (Workers and Edge runtimes lack WebGPU).
|
|
14
|
+
*
|
|
15
|
+
* `tagAsUniversal(classes)` stamps `ALL_PLATFORMS` — for nodes that
|
|
16
|
+
* genuinely work on every runtime including the browser. Use sparingly:
|
|
17
|
+
* the node must handle CORS / secret-exposure / bundle-size implications
|
|
18
|
+
* itself.
|
|
19
|
+
*
|
|
20
|
+
* All three helpers leave alone any class that already declares its own
|
|
21
|
+
* `static platforms` — per-class overrides always win. The mutation is a
|
|
22
|
+
* one-time `Object.defineProperty` at module load.
|
|
23
|
+
*/
|
|
24
|
+
import { ALL_PLATFORMS, DEFAULT_PLATFORMS, NODE_AND_BROWSER_PLATFORMS, SERVER_PLATFORMS } from "@nodetool-ai/protocol";
|
|
25
|
+
function tagWith(classes, platforms) {
|
|
26
|
+
for (const cls of classes) {
|
|
27
|
+
if (cls.platforms !== undefined)
|
|
28
|
+
continue;
|
|
29
|
+
Object.defineProperty(cls, "platforms", {
|
|
30
|
+
value: platforms,
|
|
31
|
+
writable: false,
|
|
32
|
+
configurable: true,
|
|
33
|
+
enumerable: true
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
return classes;
|
|
37
|
+
}
|
|
38
|
+
/** Tag every class in `classes` as supporting node + workers + edge. */
|
|
39
|
+
export function tagAsServer(classes) {
|
|
40
|
+
return tagWith(classes, SERVER_PLATFORMS);
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Tag every class in `classes` as supporting **only** the node tier — the
|
|
44
|
+
* (otherwise implicit) most-restrictive default. Use for nodes that spawn a
|
|
45
|
+
* subprocess (ffmpeg, ffprobe, yt-dlp, …) or write os.tmpdir() temp files:
|
|
46
|
+
* the workers (Cloudflare V8) and edge (Vercel/Deno) tiers have no subprocess
|
|
47
|
+
* and no filesystem, so offering them there is a guaranteed runtime failure.
|
|
48
|
+
* Stamping it explicitly documents the intent rather than relying on the
|
|
49
|
+
* unset default. Leaves any class with its own `static platforms` alone.
|
|
50
|
+
*/
|
|
51
|
+
export function tagAsNode(classes) {
|
|
52
|
+
return tagWith(classes, DEFAULT_PLATFORMS);
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Tag every class in `classes` as supporting node + browser (no V8 server
|
|
56
|
+
* isolates). Use for WebGPU / Canvas / DOM-backed nodes that have both a
|
|
57
|
+
* Node fallback and a browser-native execution path.
|
|
58
|
+
*/
|
|
59
|
+
export function tagAsHybrid(classes) {
|
|
60
|
+
return tagWith(classes, NODE_AND_BROWSER_PLATFORMS);
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Tag every class in `classes` as supporting every defined platform.
|
|
64
|
+
* Reserve for pure-JS nodes with no network calls, no secrets, and no
|
|
65
|
+
* platform-specific side effects (e.g. text/data transforms).
|
|
66
|
+
*/
|
|
67
|
+
export function tagAsUniversal(classes) {
|
|
68
|
+
return tagWith(classes, ALL_PLATFORMS);
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Like {@link tagAsHybrid} (node + browser) but additionally marks the classes
|
|
72
|
+
* as requiring a WebGPU device in the browser. WebGPU shader nodes use this so
|
|
73
|
+
* the in-browser runner routes graphs containing them to the server — where a
|
|
74
|
+
* GPU is always available via Dawn — when `navigator.gpu` is missing, instead
|
|
75
|
+
* of failing the run. Server execution is unchanged.
|
|
76
|
+
*
|
|
77
|
+
* `requiresGpu` is stamped only on classes that end up browser-capable: a class
|
|
78
|
+
* already tagged server keeps its platforms and is left unmarked (it can't run
|
|
79
|
+
* in the browser anyway). Per-class `static requiresGpu` overrides win.
|
|
80
|
+
*/
|
|
81
|
+
export function tagAsBrowserGpu(classes) {
|
|
82
|
+
const tagged = tagWith(classes, NODE_AND_BROWSER_PLATFORMS);
|
|
83
|
+
for (const cls of tagged) {
|
|
84
|
+
const platforms = cls.platforms;
|
|
85
|
+
if (!platforms?.includes("browser"))
|
|
86
|
+
continue;
|
|
87
|
+
if (cls.requiresGpu !== undefined)
|
|
88
|
+
continue;
|
|
89
|
+
Object.defineProperty(cls, "requiresGpu", {
|
|
90
|
+
value: true,
|
|
91
|
+
writable: false,
|
|
92
|
+
configurable: true,
|
|
93
|
+
enumerable: true
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
return tagged;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Stamp `static body = "content_card"` on every class in `classes` so the
|
|
100
|
+
* editor renders them as a media content card (the image/video/audio preview
|
|
101
|
+
* body) instead of the generic input/output layout. Leaves any class that
|
|
102
|
+
* already declares its own `static body` alone — per-class overrides win.
|
|
103
|
+
*
|
|
104
|
+
* Apply to media-output node groups, composing with a platform tagger:
|
|
105
|
+
* `tagAsHybrid(tagAsContentCard([...]))`.
|
|
106
|
+
*/
|
|
107
|
+
export function tagAsContentCard(classes) {
|
|
108
|
+
for (const cls of classes) {
|
|
109
|
+
if (cls.body !== undefined)
|
|
110
|
+
continue;
|
|
111
|
+
Object.defineProperty(cls, "body", {
|
|
112
|
+
value: "content_card",
|
|
113
|
+
writable: false,
|
|
114
|
+
configurable: true,
|
|
115
|
+
enumerable: true
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
return classes;
|
|
119
|
+
}
|
|
120
|
+
//# sourceMappingURL=platform-tags.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"platform-tags.js","sourceRoot":"","sources":["../src/platform-tags.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAGH,OAAO,EACL,aAAa,EACb,iBAAiB,EACjB,0BAA0B,EAC1B,gBAAgB,EAEjB,MAAM,uBAAuB,CAAC;AAE/B,SAAS,OAAO,CACd,OAAU,EACV,SAA8B;IAE9B,KAAK,MAAM,GAAG,IAAI,OAAO,EAAE,CAAC;QAC1B,IAAI,GAAG,CAAC,SAAS,KAAK,SAAS;YAAE,SAAS;QAC1C,MAAM,CAAC,cAAc,CAAC,GAAG,EAAE,WAAW,EAAE;YACtC,KAAK,EAAE,SAAS;YAChB,QAAQ,EAAE,KAAK;YACf,YAAY,EAAE,IAAI;YAClB,UAAU,EAAE,IAAI;SACjB,CAAC,CAAC;IACL,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,wEAAwE;AACxE,MAAM,UAAU,WAAW,CAAiC,OAAU;IACpE,OAAO,OAAO,CAAC,OAAO,EAAE,gBAAgB,CAAC,CAAC;AAC5C,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,SAAS,CAAiC,OAAU;IAClE,OAAO,OAAO,CAAC,OAAO,EAAE,iBAAiB,CAAC,CAAC;AAC7C,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,WAAW,CAAiC,OAAU;IACpE,OAAO,OAAO,CAAC,OAAO,EAAE,0BAA0B,CAAC,CAAC;AACtD,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAAiC,OAAU;IACvE,OAAO,OAAO,CAAC,OAAO,EAAE,aAAa,CAAC,CAAC;AACzC,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,eAAe,CAAiC,OAAU;IACxE,MAAM,MAAM,GAAG,OAAO,CAAC,OAAO,EAAE,0BAA0B,CAAC,CAAC;IAC5D,KAAK,MAAM,GAAG,IAAI,MAAM,EAAE,CAAC;QACzB,MAAM,SAAS,GAAG,GAAG,CAAC,SAAS,CAAC;QAChC,IAAI,CAAC,SAAS,EAAE,QAAQ,CAAC,SAAS,CAAC;YAAE,SAAS;QAC9C,IAAI,GAAG,CAAC,WAAW,KAAK,SAAS;YAAE,SAAS;QAC5C,MAAM,CAAC,cAAc,CAAC,GAAG,EAAE,aAAa,EAAE;YACxC,KAAK,EAAE,IAAI;YACX,QAAQ,EAAE,KAAK;YACf,YAAY,EAAE,IAAI;YAClB,UAAU,EAAE,IAAI;SACjB,CAAC,CAAC;IACL,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,gBAAgB,CAAiC,OAAU;IACzE,KAAK,MAAM,GAAG,IAAI,OAAO,EAAE,CAAC;QAC1B,IAAI,GAAG,CAAC,IAAI,KAAK,SAAS;YAAE,SAAS;QACrC,MAAM,CAAC,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE;YACjC,KAAK,EAAE,cAAc;YACrB,QAAQ,EAAE,KAAK;YACf,YAAY,EAAE,IAAI;YAClB,UAAU,EAAE,IAAI;SACjB,CAAC,CAAC;IACL,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC"}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/** Title every save node shows on the workspace toggle. */
|
|
2
|
+
export declare const SAVE_TO_WORKSPACE_TITLE = "Save to workspace";
|
|
3
|
+
/** Description every save node shows on the workspace toggle. */
|
|
4
|
+
export declare const SAVE_TO_WORKSPACE_DESCRIPTION = "Write the file into this workflow's workspace folder and number it (name_1, name_2, \u2026) instead of overwriting. Turn this off to choose a folder yourself.";
|
|
5
|
+
/**
|
|
6
|
+
* `json_schema_extra` for a folder property that only applies while the
|
|
7
|
+
* workspace toggle is off. The editor hides the field rather than showing a
|
|
8
|
+
* path that has no effect.
|
|
9
|
+
*/
|
|
10
|
+
export declare const VISIBLE_WHEN_NOT_SAVING_TO_WORKSPACE: {
|
|
11
|
+
readonly visible_when: {
|
|
12
|
+
readonly property: "save_to_workspace";
|
|
13
|
+
readonly equals: false;
|
|
14
|
+
};
|
|
15
|
+
};
|
|
16
|
+
/** A folder ref — what a `folder`-typed property carries. */
|
|
17
|
+
interface FolderRefValue {
|
|
18
|
+
uri?: string;
|
|
19
|
+
}
|
|
20
|
+
/** Every shape a node's `folder` property arrives in. */
|
|
21
|
+
export type FolderValue = string | FolderRefValue | null | undefined;
|
|
22
|
+
/**
|
|
23
|
+
* Read a folder property, which may be a plain path, a `file://` URI or a
|
|
24
|
+
* folder ref object. Returns "" when nothing usable is set.
|
|
25
|
+
*/
|
|
26
|
+
export declare function folderPathOf(raw: FolderValue): string;
|
|
27
|
+
/**
|
|
28
|
+
* The slice of the run's workspace a save node needs.
|
|
29
|
+
*
|
|
30
|
+
* Structural rather than imported: `@nodetool-ai/nodes-utils` sits below
|
|
31
|
+
* `@nodetool-ai/runtime` in the dependency order, and a `ProcessingContext`'s
|
|
32
|
+
* `workspace` satisfies this shape.
|
|
33
|
+
*/
|
|
34
|
+
export interface SaveWorkspace {
|
|
35
|
+
localDir: string | null;
|
|
36
|
+
exists(path: string): Promise<boolean>;
|
|
37
|
+
write(path: string, data: Uint8Array | string, contentType?: string): Promise<void>;
|
|
38
|
+
}
|
|
39
|
+
export interface SaveFolderOptions {
|
|
40
|
+
/** The node's `folder` property, whatever shape it arrived in. */
|
|
41
|
+
folder: FolderValue;
|
|
42
|
+
/** The node's `save_to_workspace` property. */
|
|
43
|
+
saveToWorkspace: boolean | undefined;
|
|
44
|
+
/** `context.workspace` — null when the run has no workspace. */
|
|
45
|
+
workspace?: SaveWorkspace | null;
|
|
46
|
+
/**
|
|
47
|
+
* `context.workspaceDir`. Only meaningful for a workspace that is a real
|
|
48
|
+
* folder; a cloud run has none and saves through `workspace` instead.
|
|
49
|
+
*/
|
|
50
|
+
workspaceDir?: string | null;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Pick the destination folder.
|
|
54
|
+
*
|
|
55
|
+
* With the toggle on, the workspace folder wins over the `folder` property —
|
|
56
|
+
* the editor hides that field while the toggle is on, so a value left there is
|
|
57
|
+
* a leftover, not a choice. A run with no workspace assigned has nowhere to put
|
|
58
|
+
* the file, so it falls back to the folder and finally to the working
|
|
59
|
+
* directory rather than failing the run.
|
|
60
|
+
*/
|
|
61
|
+
export declare function resolveSaveFolder(opts: SaveFolderOptions): string;
|
|
62
|
+
/** What a save node calls its file when the user leaves the name empty. */
|
|
63
|
+
export interface SaveFilenameOptions {
|
|
64
|
+
/** The node's filename property, with date tokens already expanded. */
|
|
65
|
+
filename: string;
|
|
66
|
+
/** The name to use when `filename` is blank. Carries its own extension. */
|
|
67
|
+
fallback: string;
|
|
68
|
+
/** Extension (with the dot) to append when the name carries none. */
|
|
69
|
+
extension?: string;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* The name a save node writes under.
|
|
73
|
+
*
|
|
74
|
+
* A blank name used to reach `path.resolve(folder, "")`, which is the folder
|
|
75
|
+
* itself: the file landed *beside* the workspace, named after it, with no
|
|
76
|
+
* extension. And a name typed without one ("render") produced a file no image
|
|
77
|
+
* viewer would open. Both are answered here rather than in each node.
|
|
78
|
+
*/
|
|
79
|
+
export declare function saveFilename(opts: SaveFilenameOptions): string;
|
|
80
|
+
/**
|
|
81
|
+
* Return a path nothing occupies: `target` itself when free, otherwise
|
|
82
|
+
* `name_1.ext`, `name_2.ext`, … in the same directory.
|
|
83
|
+
*/
|
|
84
|
+
export declare function uniqueFilePath(target: string): Promise<string>;
|
|
85
|
+
export interface SaveTargetOptions extends SaveFolderOptions {
|
|
86
|
+
/** The node's filename property, with date tokens already expanded. */
|
|
87
|
+
filename: string;
|
|
88
|
+
/** Skip the numbered suffix and write over an existing file. */
|
|
89
|
+
overwrite?: boolean;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Resolve folder + filename into an absolute path ready to write to: the
|
|
93
|
+
* directory exists when this returns, and the name does not collide unless the
|
|
94
|
+
* caller asked to overwrite.
|
|
95
|
+
*/
|
|
96
|
+
export declare function resolveSaveTarget(opts: SaveTargetOptions): Promise<string>;
|
|
97
|
+
/**
|
|
98
|
+
* Write a saved file where the node's properties say it goes, and report the
|
|
99
|
+
* path it landed on.
|
|
100
|
+
*
|
|
101
|
+
* With the workspace toggle on this writes through the workspace, so the same
|
|
102
|
+
* node produces a file in the user's folder locally and an object in their
|
|
103
|
+
* workspace prefix in the cloud. The name is deduplicated the same way in both
|
|
104
|
+
* cases — a run that saves twice in one second must not silently overwrite its
|
|
105
|
+
* first output.
|
|
106
|
+
*
|
|
107
|
+
* With the toggle off the node is naming a host folder, which only exists on a
|
|
108
|
+
* local install; a cloud run has no such folder and says so rather than writing
|
|
109
|
+
* somewhere the user will never find.
|
|
110
|
+
*/
|
|
111
|
+
export declare function writeSavedFile(opts: SaveTargetOptions & {
|
|
112
|
+
bytes: Uint8Array | string;
|
|
113
|
+
}): Promise<string>;
|
|
114
|
+
export {};
|
|
115
|
+
//# sourceMappingURL=save-target.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"save-target.d.ts","sourceRoot":"","sources":["../src/save-target.ts"],"names":[],"mappings":"AAiBA,2DAA2D;AAC3D,eAAO,MAAM,uBAAuB,sBAAsB,CAAC;AAE3D,iEAAiE;AACjE,eAAO,MAAM,6BAA6B,mKACmH,CAAC;AAE9J;;;;GAIG;AACH,eAAO,MAAM,oCAAoC;aAC/C,YAAY;iBAAI,QAAQ,EAAE,mBAAmB;iBAAE,MAAM;;CAC7C,CAAC;AAaX,6DAA6D;AAC7D,UAAU,cAAc;IACtB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,yDAAyD;AACzD,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,cAAc,GAAG,IAAI,GAAG,SAAS,CAAC;AAcrE;;;GAGG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,WAAW,GAAG,MAAM,CAIrD;AAED;;;;;;GAMG;AACH,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACvC,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,GAAG,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACrF;AAED,MAAM,WAAW,iBAAiB;IAChC,kEAAkE;IAClE,MAAM,EAAE,WAAW,CAAC;IACpB,+CAA+C;IAC/C,eAAe,EAAE,OAAO,GAAG,SAAS,CAAC;IACrC,gEAAgE;IAChE,SAAS,CAAC,EAAE,aAAa,GAAG,IAAI,CAAC;IACjC;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B;AAED;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,iBAAiB,GAAG,MAAM,CAOjE;AAED,2EAA2E;AAC3E,MAAM,WAAW,mBAAmB;IAClC,uEAAuE;IACvE,QAAQ,EAAE,MAAM,CAAC;IACjB,2EAA2E;IAC3E,QAAQ,EAAE,MAAM,CAAC;IACjB,qEAAqE;IACrE,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,mBAAmB,GAAG,MAAM,CAO9D;AAED;;;GAGG;AACH,wBAAsB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAmBpE;AAoBD,MAAM,WAAW,iBAAkB,SAAQ,iBAAiB;IAC1D,uEAAuE;IACvE,QAAQ,EAAE,MAAM,CAAC;IACjB,gEAAgE;IAChE,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED;;;;GAIG;AACH,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,iBAAiB,GACtB,OAAO,CAAC,MAAM,CAAC,CAOjB;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAsB,cAAc,CAClC,IAAI,EAAE,iBAAiB,GAAG;IAAE,KAAK,EAAE,UAAU,GAAG,MAAM,CAAA;CAAE,GACvD,OAAO,CAAC,MAAM,CAAC,CA4BjB"}
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Destination resolution shared by every node that writes a file to disk
|
|
3
|
+
* (`nodetool.image.SaveImageFile`, `SaveAudioFile`, `SaveTextFile`,
|
|
4
|
+
* `SaveVideoFile`, `SaveDocumentFile`, `SaveModel3DFile`).
|
|
5
|
+
*
|
|
6
|
+
* Two things every one of them needs and each used to solve on its own:
|
|
7
|
+
*
|
|
8
|
+
* - **Where to write.** Without a folder they fell back to `.` — the server
|
|
9
|
+
* process working directory, which is almost never where a user wants
|
|
10
|
+
* output. The `save_to_workspace` toggle points them at the run's workspace
|
|
11
|
+
* folder instead.
|
|
12
|
+
* - **What to call it.** Filenames carry second-granularity date tokens, so
|
|
13
|
+
* two saves in the same second collided and one silently overwrote the
|
|
14
|
+
* other. `uniqueFilePath` numbers them `name_1.ext`, `name_2.ext`, …
|
|
15
|
+
*/
|
|
16
|
+
import { loadNodeFsPromises, loadNodePath } from "./node-only-modules.js";
|
|
17
|
+
/** Title every save node shows on the workspace toggle. */
|
|
18
|
+
export const SAVE_TO_WORKSPACE_TITLE = "Save to workspace";
|
|
19
|
+
/** Description every save node shows on the workspace toggle. */
|
|
20
|
+
export const SAVE_TO_WORKSPACE_DESCRIPTION = "Write the file into this workflow's workspace folder and number it (name_1, name_2, …) instead of overwriting. Turn this off to choose a folder yourself.";
|
|
21
|
+
/**
|
|
22
|
+
* `json_schema_extra` for a folder property that only applies while the
|
|
23
|
+
* workspace toggle is off. The editor hides the field rather than showing a
|
|
24
|
+
* path that has no effect.
|
|
25
|
+
*/
|
|
26
|
+
export const VISIBLE_WHEN_NOT_SAVING_TO_WORKSPACE = {
|
|
27
|
+
visible_when: { property: "save_to_workspace", equals: false }
|
|
28
|
+
};
|
|
29
|
+
/** Turn a `file://` URI into a plain path; leave a plain path alone. */
|
|
30
|
+
function toPath(value) {
|
|
31
|
+
if (!value.startsWith("file://"))
|
|
32
|
+
return value;
|
|
33
|
+
const rest = value.slice("file://".length);
|
|
34
|
+
try {
|
|
35
|
+
return decodeURIComponent(rest);
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
return rest;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
function isPathString(value) {
|
|
42
|
+
return typeof value === "string";
|
|
43
|
+
}
|
|
44
|
+
function isFolderRef(value) {
|
|
45
|
+
return typeof value === "object" && value !== null;
|
|
46
|
+
}
|
|
47
|
+
function isSetString(value) {
|
|
48
|
+
return value !== undefined && value.trim() !== "";
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Read a folder property, which may be a plain path, a `file://` URI or a
|
|
52
|
+
* folder ref object. Returns "" when nothing usable is set.
|
|
53
|
+
*/
|
|
54
|
+
export function folderPathOf(raw) {
|
|
55
|
+
if (isPathString(raw))
|
|
56
|
+
return toPath(raw.trim());
|
|
57
|
+
if (isFolderRef(raw) && isSetString(raw.uri))
|
|
58
|
+
return toPath(raw.uri.trim());
|
|
59
|
+
return "";
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Pick the destination folder.
|
|
63
|
+
*
|
|
64
|
+
* With the toggle on, the workspace folder wins over the `folder` property —
|
|
65
|
+
* the editor hides that field while the toggle is on, so a value left there is
|
|
66
|
+
* a leftover, not a choice. A run with no workspace assigned has nowhere to put
|
|
67
|
+
* the file, so it falls back to the folder and finally to the working
|
|
68
|
+
* directory rather than failing the run.
|
|
69
|
+
*/
|
|
70
|
+
export function resolveSaveFolder(opts) {
|
|
71
|
+
const folder = folderPathOf(opts.folder);
|
|
72
|
+
const workspaceDir = opts.workspace?.localDir ?? opts.workspaceDir ?? null;
|
|
73
|
+
if (opts.saveToWorkspace === true && workspaceDir) {
|
|
74
|
+
return workspaceDir;
|
|
75
|
+
}
|
|
76
|
+
return folder || workspaceDir || ".";
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* The name a save node writes under.
|
|
80
|
+
*
|
|
81
|
+
* A blank name used to reach `path.resolve(folder, "")`, which is the folder
|
|
82
|
+
* itself: the file landed *beside* the workspace, named after it, with no
|
|
83
|
+
* extension. And a name typed without one ("render") produced a file no image
|
|
84
|
+
* viewer would open. Both are answered here rather than in each node.
|
|
85
|
+
*/
|
|
86
|
+
export function saveFilename(opts) {
|
|
87
|
+
const name = opts.filename.trim() || opts.fallback.trim();
|
|
88
|
+
const ext = opts.extension;
|
|
89
|
+
if (!ext)
|
|
90
|
+
return name;
|
|
91
|
+
const dot = name.lastIndexOf(".");
|
|
92
|
+
const hasExtension = dot > 0 && dot < name.length - 1;
|
|
93
|
+
return hasExtension ? name : `${name}${ext}`;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Return a path nothing occupies: `target` itself when free, otherwise
|
|
97
|
+
* `name_1.ext`, `name_2.ext`, … in the same directory.
|
|
98
|
+
*/
|
|
99
|
+
export async function uniqueFilePath(target) {
|
|
100
|
+
const fs = await loadNodeFsPromises();
|
|
101
|
+
const path = await loadNodePath();
|
|
102
|
+
const exists = async (p) => {
|
|
103
|
+
try {
|
|
104
|
+
await fs.access(p);
|
|
105
|
+
return true;
|
|
106
|
+
}
|
|
107
|
+
catch {
|
|
108
|
+
return false;
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
if (!(await exists(target)))
|
|
112
|
+
return target;
|
|
113
|
+
const dir = path.dirname(target);
|
|
114
|
+
const ext = path.extname(target);
|
|
115
|
+
const base = path.basename(target, ext);
|
|
116
|
+
for (let i = 1;; i++) {
|
|
117
|
+
const candidate = path.join(dir, `${base}_${i}${ext}`);
|
|
118
|
+
if (!(await exists(candidate)))
|
|
119
|
+
return candidate;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Refuse a blank name instead of resolving it to the destination folder.
|
|
124
|
+
*
|
|
125
|
+
* Every save node supplies a fallback via {@link saveFilename}; one that
|
|
126
|
+
* forgets must fail loudly rather than write a file over the folder's own
|
|
127
|
+
* name, one level up from where the user asked for it.
|
|
128
|
+
*/
|
|
129
|
+
function requireFilename(filename) {
|
|
130
|
+
const name = filename.trim();
|
|
131
|
+
if (!name) {
|
|
132
|
+
throw new Error("This save node resolved to an empty filename. Set a filename on the " +
|
|
133
|
+
"node.");
|
|
134
|
+
}
|
|
135
|
+
return name;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Resolve folder + filename into an absolute path ready to write to: the
|
|
139
|
+
* directory exists when this returns, and the name does not collide unless the
|
|
140
|
+
* caller asked to overwrite.
|
|
141
|
+
*/
|
|
142
|
+
export async function resolveSaveTarget(opts) {
|
|
143
|
+
const fs = await loadNodeFsPromises();
|
|
144
|
+
const path = await loadNodePath();
|
|
145
|
+
const folder = resolveSaveFolder(opts);
|
|
146
|
+
const target = path.resolve(folder, requireFilename(opts.filename));
|
|
147
|
+
await fs.mkdir(path.dirname(target), { recursive: true });
|
|
148
|
+
return opts.overwrite === true ? target : uniqueFilePath(target);
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Write a saved file where the node's properties say it goes, and report the
|
|
152
|
+
* path it landed on.
|
|
153
|
+
*
|
|
154
|
+
* With the workspace toggle on this writes through the workspace, so the same
|
|
155
|
+
* node produces a file in the user's folder locally and an object in their
|
|
156
|
+
* workspace prefix in the cloud. The name is deduplicated the same way in both
|
|
157
|
+
* cases — a run that saves twice in one second must not silently overwrite its
|
|
158
|
+
* first output.
|
|
159
|
+
*
|
|
160
|
+
* With the toggle off the node is naming a host folder, which only exists on a
|
|
161
|
+
* local install; a cloud run has no such folder and says so rather than writing
|
|
162
|
+
* somewhere the user will never find.
|
|
163
|
+
*/
|
|
164
|
+
export async function writeSavedFile(opts) {
|
|
165
|
+
const workspace = opts.workspace ?? null;
|
|
166
|
+
const isVirtual = workspace !== null && workspace.localDir === null;
|
|
167
|
+
if (opts.saveToWorkspace === true && workspace) {
|
|
168
|
+
if (isVirtual) {
|
|
169
|
+
const filename = requireFilename(opts.filename);
|
|
170
|
+
const target = opts.overwrite === true
|
|
171
|
+
? filename
|
|
172
|
+
: await uniqueWorkspacePath(workspace, filename);
|
|
173
|
+
await workspace.write(target, opts.bytes);
|
|
174
|
+
return target;
|
|
175
|
+
}
|
|
176
|
+
// A local workspace is a real folder, so the file goes there by path —
|
|
177
|
+
// which is what a user browsing their workspace on disk expects.
|
|
178
|
+
}
|
|
179
|
+
else if (isVirtual) {
|
|
180
|
+
throw new Error("This node writes to a folder on the machine running the workflow, and " +
|
|
181
|
+
"this run's workspace is cloud storage with no such folder. Turn on " +
|
|
182
|
+
'"Save to workspace" to write into the workspace instead.');
|
|
183
|
+
}
|
|
184
|
+
const fs = await loadNodeFsPromises();
|
|
185
|
+
const target = await resolveSaveTarget(opts);
|
|
186
|
+
await fs.writeFile(target, opts.bytes);
|
|
187
|
+
return target;
|
|
188
|
+
}
|
|
189
|
+
/** `name.ext`, `name_1.ext`, … — the first one the workspace does not hold. */
|
|
190
|
+
async function uniqueWorkspacePath(workspace, filename) {
|
|
191
|
+
if (!(await workspace.exists(filename)))
|
|
192
|
+
return filename;
|
|
193
|
+
const dot = filename.lastIndexOf(".");
|
|
194
|
+
const base = dot > 0 ? filename.slice(0, dot) : filename;
|
|
195
|
+
const ext = dot > 0 ? filename.slice(dot) : "";
|
|
196
|
+
for (let i = 1;; i++) {
|
|
197
|
+
const candidate = `${base}_${i}${ext}`;
|
|
198
|
+
if (!(await workspace.exists(candidate)))
|
|
199
|
+
return candidate;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
//# sourceMappingURL=save-target.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"save-target.js","sourceRoot":"","sources":["../src/save-target.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared `{{ variable }}` / `{variable}` template substitution.
|
|
3
|
+
*
|
|
4
|
+
* Used by the Prompt and Format Text nodes and by the Agent node so they all
|
|
5
|
+
* resolve placeholders the same way:
|
|
6
|
+
* - `{{ name }}` and `{name}` are replaced with the matching variable value.
|
|
7
|
+
* - `{{ name|filter1|filter2 }}` applies Jinja-style filters in order
|
|
8
|
+
* (upper, lower, capitalize, title, trim, truncate(n), default(val)).
|
|
9
|
+
* - Placeholders whose variable is absent are left intact.
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Names of the variables a template references via `{{ name }}` /
|
|
13
|
+
* `{{ name|filter }}` / `{name}`. De-duplicated, in first-seen order. Used by
|
|
14
|
+
* the Prompt node to know which variable channels to await before rendering.
|
|
15
|
+
*/
|
|
16
|
+
export declare function referencedVariables(template: string): string[];
|
|
17
|
+
/**
|
|
18
|
+
* One template variable's value. The renderer substitutes `String(value ?? "")`
|
|
19
|
+
* and then runs string filters over the result, so the contract is "a scalar
|
|
20
|
+
* with a meaningful string form"; `null` and `undefined` render as the empty
|
|
21
|
+
* string. Callers holding a richer value stringify it at their own boundary,
|
|
22
|
+
* where they know what its string form should be — the renderer's
|
|
23
|
+
* `[object Object]` is never that.
|
|
24
|
+
*/
|
|
25
|
+
export type TemplateValue = string | number | boolean | null | undefined;
|
|
26
|
+
/** The variable bag `renderTemplate` substitutes from. */
|
|
27
|
+
export type TemplateVars = Record<string, TemplateValue>;
|
|
28
|
+
/**
|
|
29
|
+
* Substitute `{{ variable }}` / `{variable}` placeholders in `template` with
|
|
30
|
+
* values from `vars`. Unknown placeholders are left intact.
|
|
31
|
+
*/
|
|
32
|
+
export declare function renderTemplate(template: string, vars: TemplateVars): string;
|
|
33
|
+
//# sourceMappingURL=template.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"template.d.ts","sourceRoot":"","sources":["../src/template.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAoCH;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,EAAE,CAe9D;AAED;;;;;;;GAOG;AACH,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,GAAG,SAAS,CAAC;AAEzE,0DAA0D;AAC1D,MAAM,MAAM,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;AAEzD;;;GAGG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,YAAY,GAAG,MAAM,CA6B3E"}
|
package/dist/template.js
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared `{{ variable }}` / `{variable}` template substitution.
|
|
3
|
+
*
|
|
4
|
+
* Used by the Prompt and Format Text nodes and by the Agent node so they all
|
|
5
|
+
* resolve placeholders the same way:
|
|
6
|
+
* - `{{ name }}` and `{name}` are replaced with the matching variable value.
|
|
7
|
+
* - `{{ name|filter1|filter2 }}` applies Jinja-style filters in order
|
|
8
|
+
* (upper, lower, capitalize, title, trim, truncate(n), default(val)).
|
|
9
|
+
* - Placeholders whose variable is absent are left intact.
|
|
10
|
+
*/
|
|
11
|
+
function toTitleCase(value) {
|
|
12
|
+
return value.replace(/\w\S*/g, (txt) => txt.charAt(0).toUpperCase() + txt.slice(1).toLowerCase());
|
|
13
|
+
}
|
|
14
|
+
function applyFilter(value, filter) {
|
|
15
|
+
const trimmed = filter.trim();
|
|
16
|
+
if (trimmed === "upper")
|
|
17
|
+
return value.toUpperCase();
|
|
18
|
+
if (trimmed === "lower")
|
|
19
|
+
return value.toLowerCase();
|
|
20
|
+
if (trimmed === "capitalize") {
|
|
21
|
+
return value.length === 0
|
|
22
|
+
? ""
|
|
23
|
+
: value.charAt(0).toUpperCase() + value.slice(1).toLowerCase();
|
|
24
|
+
}
|
|
25
|
+
if (trimmed === "title")
|
|
26
|
+
return toTitleCase(value);
|
|
27
|
+
if (trimmed === "trim")
|
|
28
|
+
return value.trim();
|
|
29
|
+
const truncateMatch = trimmed.match(/^truncate\((\d+)\)$/);
|
|
30
|
+
if (truncateMatch) {
|
|
31
|
+
const n = Number(truncateMatch[1]);
|
|
32
|
+
return value.length <= n ? value : value.slice(0, n) + "...";
|
|
33
|
+
}
|
|
34
|
+
const defaultMatch = trimmed.match(/^default\((.+)\)$/);
|
|
35
|
+
if (defaultMatch) {
|
|
36
|
+
return value === "" ? defaultMatch[1].replace(/^['"]|['"]$/g, "") : value;
|
|
37
|
+
}
|
|
38
|
+
return value;
|
|
39
|
+
}
|
|
40
|
+
function applyFilters(value, filters) {
|
|
41
|
+
return filters.reduce((result, f) => applyFilter(result, f), value);
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Names of the variables a template references via `{{ name }}` /
|
|
45
|
+
* `{{ name|filter }}` / `{name}`. De-duplicated, in first-seen order. Used by
|
|
46
|
+
* the Prompt node to know which variable channels to await before rendering.
|
|
47
|
+
*/
|
|
48
|
+
export function referencedVariables(template) {
|
|
49
|
+
const names = new Set();
|
|
50
|
+
// {{ name|filter... }} — leading identifier before any filter.
|
|
51
|
+
for (const match of template.matchAll(/\{\{([^{}]+?)\}\}/g)) {
|
|
52
|
+
const name = match[1].split("|")[0].trim();
|
|
53
|
+
if (name)
|
|
54
|
+
names.add(name);
|
|
55
|
+
}
|
|
56
|
+
// {name} — single brace, not adjacent to another brace.
|
|
57
|
+
for (const match of template.matchAll(/(?<!\{)\{([^{}]+?)\}(?!\})/g)) {
|
|
58
|
+
const name = match[1].trim();
|
|
59
|
+
// Skip anything that looks like a filter expression; single-brace refs are
|
|
60
|
+
// bare names only.
|
|
61
|
+
if (name && !name.includes("|"))
|
|
62
|
+
names.add(name);
|
|
63
|
+
}
|
|
64
|
+
return [...names];
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Substitute `{{ variable }}` / `{variable}` placeholders in `template` with
|
|
68
|
+
* values from `vars`. Unknown placeholders are left intact.
|
|
69
|
+
*/
|
|
70
|
+
export function renderTemplate(template, vars) {
|
|
71
|
+
let result = template;
|
|
72
|
+
// {{ var|filter1|filter2 }}. The content class excludes both braces so the
|
|
73
|
+
// lazy repetition can't scan across `{`/`}` runs — without that, inputs like
|
|
74
|
+
// `{{{{{{...` give quadratic backtracking (CodeQL polynomial-ReDoS). A
|
|
75
|
+
// placeholder expression never legitimately contains a brace. Surrounding
|
|
76
|
+
// whitespace is handled by the per-part trim() below.
|
|
77
|
+
result = result.replace(/\{\{([^{}]+?)\}\}/g, (match, expr) => {
|
|
78
|
+
const parts = expr.split("|").map((p) => p.trim());
|
|
79
|
+
const varName = parts[0];
|
|
80
|
+
if (varName in vars) {
|
|
81
|
+
const strValue = String(vars[varName] ?? "");
|
|
82
|
+
return parts.length > 1
|
|
83
|
+
? applyFilters(strValue, parts.slice(1))
|
|
84
|
+
: strValue;
|
|
85
|
+
}
|
|
86
|
+
return match;
|
|
87
|
+
});
|
|
88
|
+
// {var} (single brace, no filters)
|
|
89
|
+
for (const [key, value] of Object.entries(vars)) {
|
|
90
|
+
const strValue = String(value ?? "");
|
|
91
|
+
const escapedKey = key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
92
|
+
const single = new RegExp(`(?<!\\{)\\{${escapedKey}\\}(?!\\})`, "g");
|
|
93
|
+
result = result.replace(single, () => strValue);
|
|
94
|
+
}
|
|
95
|
+
return result;
|
|
96
|
+
}
|
|
97
|
+
//# sourceMappingURL=template.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"template.js","sourceRoot":"","sources":["../src/template.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@nodetool-ai/nodes-utils",
|
|
3
|
+
"version": "0.8.1",
|
|
4
|
+
"description": "Shared helpers for node packages: platform tags and lazy Node-only module loaders",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"engines": {
|
|
7
|
+
"node": ">=22.0.0"
|
|
8
|
+
},
|
|
9
|
+
"main": "dist/index.js",
|
|
10
|
+
"types": "dist/index.d.ts",
|
|
11
|
+
"scripts": {
|
|
12
|
+
"build": "node ../../scripts/build-typescript-workspace.mjs",
|
|
13
|
+
"test": "node ../../scripts/run-vitest.mjs run",
|
|
14
|
+
"test:watch": "node ../../scripts/run-vitest.mjs",
|
|
15
|
+
"lint": "node ../../scripts/run-tsc.mjs --noEmit"
|
|
16
|
+
},
|
|
17
|
+
"dependencies": {
|
|
18
|
+
"@nodetool-ai/config": "^0.8.1",
|
|
19
|
+
"@nodetool-ai/node-sdk": "^0.8.1",
|
|
20
|
+
"@nodetool-ai/protocol": "^0.8.1",
|
|
21
|
+
"@nodetool-ai/runtime": "^0.8.1"
|
|
22
|
+
},
|
|
23
|
+
"devDependencies": {
|
|
24
|
+
"@types/node": "^22.0.0",
|
|
25
|
+
"typescript": "npm:@typescript/typescript6@6.0.2",
|
|
26
|
+
"vitest": "^5.0.1"
|
|
27
|
+
},
|
|
28
|
+
"exports": {
|
|
29
|
+
".": {
|
|
30
|
+
"nodetool-dev": "./src/index.ts",
|
|
31
|
+
"types": "./dist/index.d.ts",
|
|
32
|
+
"import": "./dist/index.js",
|
|
33
|
+
"default": "./dist/index.js"
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
"license": "MIT",
|
|
37
|
+
"repository": {
|
|
38
|
+
"type": "git",
|
|
39
|
+
"url": "https://github.com/nodetool-ai/nodetool.git",
|
|
40
|
+
"directory": "packages/nodes-utils"
|
|
41
|
+
},
|
|
42
|
+
"publishConfig": {
|
|
43
|
+
"access": "public"
|
|
44
|
+
},
|
|
45
|
+
"files": [
|
|
46
|
+
"dist",
|
|
47
|
+
"README.md"
|
|
48
|
+
],
|
|
49
|
+
"homepage": "https://nodetool.ai",
|
|
50
|
+
"bugs": {
|
|
51
|
+
"url": "https://github.com/nodetool-ai/nodetool/issues"
|
|
52
|
+
},
|
|
53
|
+
"keywords": [
|
|
54
|
+
"nodetool",
|
|
55
|
+
"ai",
|
|
56
|
+
"workflow",
|
|
57
|
+
"workflow-automation",
|
|
58
|
+
"typescript",
|
|
59
|
+
"utilities",
|
|
60
|
+
"helpers",
|
|
61
|
+
"platform"
|
|
62
|
+
]
|
|
63
|
+
}
|