@workflow/web 5.0.0-beta.5 → 5.0.0-beta.51

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.
Files changed (48) hide show
  1. package/README.md +1 -0
  2. package/build/client/assets/app-9JVnf-IU.css +1 -0
  3. package/build/client/assets/arrow-up-right-D35zSWdr.js +15 -0
  4. package/build/client/assets/{entry.client-DtOpLtMQ.js → entry.client-DmlDFlyx.js} +25 -185
  5. package/build/client/assets/{highlighted-body-B3W2YXNL-L1CrwoL_.js → highlighted-body-B3W2YXNL-CaN05-wF.js} +2 -2
  6. package/build/client/assets/{home-B6UmDDP6.js → home-CERPMYFB.js} +1762 -291
  7. package/build/client/assets/index-HqNj_XIX.js +200 -0
  8. package/build/client/assets/{index-BsV8i_Jn.js → index-thGVuYbY.js} +19 -19
  9. package/build/client/assets/loader-circle-BS067e3l.js +12 -0
  10. package/build/client/assets/manifest-e59a5df7.js +1 -0
  11. package/build/client/assets/mermaid-3ZIDBTTL-D8CjRQta.css +1 -0
  12. package/build/client/assets/{mermaid-3ZIDBTTL-BQLU7GUr.js → mermaid-3ZIDBTTL-DX-qOY9c.js} +17613 -12954
  13. package/build/client/assets/root-Bl5xphvU.css +1 -0
  14. package/build/client/assets/{root-Yk3JVwUh.js → root-D0H-cabN.js} +17 -42
  15. package/build/client/assets/{run-detail-DMUaWXhb.js → run-detail-C7nNgL_7.js} +2215 -1599
  16. package/build/client/assets/run-detail-CWoGxu_0.css +1 -0
  17. package/build/client/assets/{workflow-graph-viewer-yqeN-U_2.js → workflow-graph-viewer-nSzDqm6s.js} +5111 -5254
  18. package/build/client/assets/zstd-B5R2yJjB.wasm +0 -0
  19. package/build/client/assets/zstd-browser-decoder-DtSP7rJ_.js +27 -0
  20. package/build/server/assets/{server-build-DtcOUEHS.js → app-8hyjFpMO.js} +77875 -59603
  21. package/build/server/assets/{highlighted-body-B3W2YXNL-CZjJIpdD.js → highlighted-body-B3W2YXNL-DY_BqM6l.js} +16 -8
  22. package/build/server/assets/index-Az8HzIf_.js +174 -0
  23. package/build/server/assets/index-BfLBLRRi.js +204 -0
  24. package/build/server/assets/index-CETb4fkq.js +1164 -0
  25. package/build/server/assets/index-CvyMHP_b.js +1262 -0
  26. package/build/server/assets/{mermaid-3ZIDBTTL-Cc8IPTBj.js → mermaid-3ZIDBTTL-Bca_z0-z.js} +17 -9
  27. package/build/server/assets/{token-DHnArVm7.js → token-BcUtceXk.js} +5 -2
  28. package/build/server/assets/{token-util-B3uKeZ0s.js → token-util-BgQCuyAc.js} +5 -2
  29. package/build/server/assets/websocket-server-D2905vlr.js +3669 -0
  30. package/build/server/assets/wrapper-BxY6HvLW.js +69 -0
  31. package/build/server/assets/ws-transport-nlG6b7qI.js +551 -0
  32. package/build/server/assets/zstd-browser-decoder-Ce8lEOjC.js +72 -0
  33. package/build/server/index.js +50 -2
  34. package/handler.d.ts +22 -0
  35. package/handler.js +186 -0
  36. package/package.json +27 -13
  37. package/registry.d.ts +29 -0
  38. package/registry.js +89 -0
  39. package/build/client/assets/encryption-CEh0YXii.js +0 -28
  40. package/build/client/assets/index-B4xuge0I.js +0 -249
  41. package/build/client/assets/manifest-e4c1c787.js +0 -1
  42. package/build/client/assets/mermaid-3ZIDBTTL-DKxHcEOp.css +0 -1
  43. package/build/client/assets/root-C0Tu5TVN.css +0 -1
  44. package/build/client/assets/server-build-DqaxIrF0.css +0 -1
  45. package/build/client/assets/worker-CqHKTA7D.js +0 -115
  46. package/build/server/assets/app-L1V62FgT.js +0 -9213
  47. package/build/server/assets/index-BwtNigf1.js +0 -80
  48. package/build/server/assets/index-meg9qk8y.js +0 -1282
@@ -1,6 +1,54 @@
1
- import { n } from "./assets/app-L1V62FgT.js";
1
+ import { createRequire as __wkfCreateRequire } from "node:module";
2
+ if (typeof require === "undefined") {
3
+ globalThis.require = __wkfCreateRequire(import.meta.url);
4
+ }
5
+ import { bu, bv } from "./assets/app-8hyjFpMO.js";
2
6
  import "express";
7
+ import "node:stream";
3
8
  import "stream";
9
+ import "util";
10
+ import "crypto";
11
+ import "async_hooks";
12
+ import "node:crypto";
13
+ import "node:process";
14
+ import "node:path";
15
+ import "node:url";
16
+ import "module";
17
+ import "node:fs/promises";
18
+ import "node:module";
19
+ import "node:util";
20
+ import "node:async_hooks";
21
+ import "node:fs";
22
+ import "node:os";
23
+ import "node:child_process";
24
+ import "node:timers/promises";
25
+ import "node:buffer";
26
+ import "node:http";
27
+ import "node:https";
28
+ import "node:zlib";
29
+ import "events";
30
+ import "node:assert";
31
+ import "node:net";
32
+ import "node:querystring";
33
+ import "node:events";
34
+ import "node:diagnostics_channel";
35
+ import "node:tls";
36
+ import "node:perf_hooks";
37
+ import "node:util/types";
38
+ import "node:sqlite";
39
+ import "node:worker_threads";
40
+ import "node:console";
41
+ import "node:timers";
42
+ import "node:dns";
43
+ import "path";
44
+ import "fs";
45
+ import "constants";
46
+ import "assert";
47
+ import "os";
48
+ import "assert/strict";
49
+ import "net";
50
+ import "node:assert/strict";
4
51
  export {
5
- n as app
52
+ bu as app,
53
+ bv as createFetchHandler
6
54
  };
package/handler.d.ts ADDED
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Options for {@link createWorkflowWebHandler}.
3
+ */
4
+ export interface CreateWorkflowWebHandlerOptions {
5
+ /**
6
+ * Mount path the dashboard is served under (e.g. `"/_workflow"`). Routing and
7
+ * asset URLs are reprefixed to this base. Defaults to `"/"` (root).
8
+ */
9
+ basename?: string;
10
+ }
11
+
12
+ /**
13
+ * Create a framework-agnostic Web `Request` -> `Response` handler that serves
14
+ * the Workflow observability UI (static client assets + React Router SSR)
15
+ * in-process, suitable for mounting inside another server under `basename`.
16
+ *
17
+ * Requires `@workflow/web` to have been built (`build/` must exist); throws
18
+ * otherwise.
19
+ */
20
+ export function createWorkflowWebHandler(
21
+ options?: CreateWorkflowWebHandlerOptions
22
+ ): Promise<(request: Request) => Promise<Response>>;
package/handler.js ADDED
@@ -0,0 +1,186 @@
1
+ /**
2
+ * Framework-agnostic, in-process entry point for @workflow/web.
3
+ *
4
+ * Unlike `server.js` (which starts a standalone Express HTTP server), this
5
+ * exports a single Web-standard fetch handler that another server can mount
6
+ * under an arbitrary base path — e.g. `@workflow/nitro` mounting the dashboard
7
+ * at `/_workflow` without spawning a second server/port.
8
+ *
9
+ * import { createWorkflowWebHandler } from "@workflow/web/handler";
10
+ * const handler = await createWorkflowWebHandler({ basename: "/_workflow" });
11
+ * const response = await handler(request); // (request: Request) => Response
12
+ *
13
+ * The handler serves both the prebuilt static client assets (from `build/client`)
14
+ * and the React Router SSR app. It reads the prebuilt `build/` as-is in every
15
+ * mode (it never runs Vite), so consumers must have built @workflow/web first
16
+ * (the published package ships `build/`).
17
+ */
18
+
19
+ import { existsSync } from 'node:fs';
20
+ import { readFile, stat } from 'node:fs/promises';
21
+ import path from 'node:path';
22
+ import { fileURLToPath, pathToFileURL } from 'node:url';
23
+ import { recordDashboard } from './registry.js';
24
+
25
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
26
+ const buildDir = path.resolve(__dirname, 'build');
27
+ const clientDir = path.join(buildDir, 'client');
28
+ const serverEntry = path.join(buildDir, 'server', 'index.js');
29
+
30
+ // Minimal extension -> MIME map for serving the static client build. A wrong
31
+ // Content-Type on the entry module breaks the whole app, so be explicit.
32
+ const MIME_TYPES = {
33
+ '.js': 'text/javascript',
34
+ '.mjs': 'text/javascript',
35
+ '.css': 'text/css',
36
+ '.json': 'application/json',
37
+ '.map': 'application/json',
38
+ '.html': 'text/html',
39
+ '.ico': 'image/x-icon',
40
+ '.svg': 'image/svg+xml',
41
+ '.png': 'image/png',
42
+ '.jpg': 'image/jpeg',
43
+ '.jpeg': 'image/jpeg',
44
+ '.gif': 'image/gif',
45
+ '.webp': 'image/webp',
46
+ '.avif': 'image/avif',
47
+ '.woff': 'font/woff',
48
+ '.woff2': 'font/woff2',
49
+ '.ttf': 'font/ttf',
50
+ '.otf': 'font/otf',
51
+ '.txt': 'text/plain',
52
+ '.wasm': 'application/wasm',
53
+ };
54
+
55
+ /** Normalize a mount path: `/` or empty -> "" (root); otherwise strip trailing slash. */
56
+ function normalizeBasename(basename) {
57
+ if (!basename || basename === '/') return '';
58
+ return basename.endsWith('/') ? basename.slice(0, -1) : basename;
59
+ }
60
+
61
+ // One handler per basename, lazily constructed and memoized so repeated
62
+ // requests reuse the same React Router handler and module import.
63
+ const handlerCache = new Map();
64
+
65
+ /**
66
+ * @param {{ basename?: string }} [options]
67
+ * @returns {Promise<(request: Request) => Promise<Response>>}
68
+ */
69
+ export async function createWorkflowWebHandler(options = {}) {
70
+ const basename = normalizeBasename(options.basename ?? '/');
71
+ let promise = handlerCache.get(basename);
72
+ if (!promise) {
73
+ promise = buildHandler(basename).catch((err) => {
74
+ handlerCache.delete(basename);
75
+ throw err;
76
+ });
77
+ handlerCache.set(basename, promise);
78
+ }
79
+ return promise;
80
+ }
81
+
82
+ async function buildHandler(basename) {
83
+ if (!existsSync(serverEntry) || !existsSync(clientDir)) {
84
+ throw new Error(
85
+ '@workflow/web has not been built (missing build/). ' +
86
+ 'Run `pnpm --filter @workflow/web build` before embedding the dashboard.'
87
+ );
88
+ }
89
+
90
+ const mod = await import(pathToFileURL(serverEntry).href);
91
+ if (typeof mod.createFetchHandler !== 'function') {
92
+ throw new Error(
93
+ '@workflow/web build does not export createFetchHandler; rebuild the package.'
94
+ );
95
+ }
96
+ const ssr = mod.createFetchHandler(basename || '/');
97
+
98
+ return async (request) => {
99
+ // Advertise this dashboard so the CLI can defer to it (best-effort, once).
100
+ // Done on first request because that's when the public origin is known.
101
+ try {
102
+ const origin = new URL(request.url).origin;
103
+ recordDashboard({
104
+ url: origin + basename,
105
+ basename,
106
+ world:
107
+ process.env.WORKFLOW_TARGET_WORLD ||
108
+ (process.env.VERCEL_DEPLOYMENT_ID ? 'vercel' : 'local'),
109
+ });
110
+ } catch {
111
+ // ignore — registration must never affect request handling
112
+ }
113
+
114
+ const staticResponse = await tryServeStatic(request, basename);
115
+ if (staticResponse) return staticResponse;
116
+ return ssr(request);
117
+ };
118
+ }
119
+
120
+ /**
121
+ * Resolve a request to a path-within-the-client-build (relative to `clientDir`),
122
+ * stripping `basename` and guarding against traversal. Returns null when the
123
+ * request can't map to a client file (so it should fall through to the SSR
124
+ * handler) — e.g. the index, a directory, or an out-of-tree path.
125
+ */
126
+ function resolveClientFile(request, basename) {
127
+ if (request.method !== 'GET' && request.method !== 'HEAD') return null;
128
+
129
+ let pathname;
130
+ try {
131
+ pathname = decodeURIComponent(new URL(request.url).pathname);
132
+ } catch {
133
+ return null;
134
+ }
135
+
136
+ if (basename) {
137
+ if (!pathname.startsWith(`${basename}/`)) return null; // index/other -> SSR
138
+ pathname = pathname.slice(basename.length);
139
+ }
140
+
141
+ const relative = pathname.replace(/^\/+/, '');
142
+ if (!relative || relative.endsWith('/')) return null;
143
+
144
+ const filePath = path.join(clientDir, relative);
145
+ // Guard against path traversal escaping the client build directory.
146
+ if (filePath !== clientDir && !filePath.startsWith(clientDir + path.sep)) {
147
+ return null;
148
+ }
149
+ return { filePath, relative };
150
+ }
151
+
152
+ /**
153
+ * Serve a file from the prebuilt client bundle if the request maps to one;
154
+ * otherwise return null so the request falls through to the SSR handler.
155
+ */
156
+ async function tryServeStatic(request, basename) {
157
+ const resolved = resolveClientFile(request, basename);
158
+ if (!resolved) return null;
159
+ const { filePath, relative } = resolved;
160
+
161
+ let stats;
162
+ try {
163
+ stats = await stat(filePath);
164
+ } catch {
165
+ return null;
166
+ }
167
+ if (!stats.isFile()) return null;
168
+
169
+ const ext = path.extname(filePath).toLowerCase();
170
+ const headers = new Headers({
171
+ 'Content-Type': MIME_TYPES[ext] ?? 'application/octet-stream',
172
+ // Hashed assets are content-addressed and safe to cache forever; other
173
+ // client files (e.g. favicon) get a short cache.
174
+ 'Cache-Control': relative.startsWith('assets/')
175
+ ? 'public, max-age=31536000, immutable'
176
+ : 'public, max-age=3600',
177
+ });
178
+
179
+ if (request.method === 'HEAD') {
180
+ headers.set('Content-Length', String(stats.size));
181
+ return new Response(null, { status: 200, headers });
182
+ }
183
+
184
+ const body = await readFile(filePath);
185
+ return new Response(body, { status: 200, headers });
186
+ }
package/package.json CHANGED
@@ -1,13 +1,17 @@
1
1
  {
2
2
  "name": "@workflow/web",
3
3
  "description": "Workflow Observability UI",
4
- "version": "5.0.0-beta.5",
4
+ "version": "5.0.0-beta.51",
5
5
  "type": "module",
6
6
  "private": false,
7
7
  "files": [
8
8
  "build",
9
9
  "server.js",
10
10
  "server.d.ts",
11
+ "handler.js",
12
+ "handler.d.ts",
13
+ "registry.js",
14
+ "registry.d.ts",
11
15
  "package.json"
12
16
  ],
13
17
  "exports": {
@@ -15,6 +19,14 @@
15
19
  "types": "./server.d.ts",
16
20
  "default": "./server.js"
17
21
  },
22
+ "./handler": {
23
+ "types": "./handler.d.ts",
24
+ "default": "./handler.js"
25
+ },
26
+ "./registry": {
27
+ "types": "./registry.d.ts",
28
+ "default": "./registry.js"
29
+ },
18
30
  "./package.json": "./package.json"
19
31
  },
20
32
  "publishConfig": {
@@ -27,10 +39,11 @@
27
39
  "directory": "packages/web"
28
40
  },
29
41
  "dependencies": {
30
- "express": "^4.21.0"
42
+ "@workflow/world-local": "5.0.0-beta.44",
43
+ "express": "^5.2.1",
44
+ "swr": "^2.3.6"
31
45
  },
32
46
  "devDependencies": {
33
- "@testing-library/react": "^16.3.0",
34
47
  "@biomejs/biome": "^2.4.4",
35
48
  "@radix-ui/react-alert-dialog": "1.1.5",
36
49
  "@radix-ui/react-checkbox": "1.1.4",
@@ -45,12 +58,19 @@
45
58
  "@react-router/dev": "7.13.1",
46
59
  "@react-router/express": "7.13.1",
47
60
  "@react-router/node": "7.13.1",
48
- "@vercel/react-router": "1.2.6",
49
61
  "@tailwindcss/vite": "4",
62
+ "@testing-library/react": "^16.3.0",
50
63
  "@types/express": "^5",
51
64
  "@types/node": "22.19.0",
52
65
  "@types/react": "19",
53
66
  "@types/react-dom": "19",
67
+ "@vercel/react-router": "1.2.6",
68
+ "@workflow/core": "5.0.0-beta.51",
69
+ "@workflow/errors": "5.0.0-beta.21",
70
+ "@workflow/utils": "5.0.0-beta.10",
71
+ "@workflow/web-shared": "5.0.0-beta.51",
72
+ "@workflow/world": "5.0.0-beta.35",
73
+ "@workflow/world-vercel": "5.0.0-beta.46",
54
74
  "@xyflow/react": "12.10.1",
55
75
  "cbor-x": "^1",
56
76
  "class-variance-authority": "0.7.1",
@@ -59,6 +79,7 @@
59
79
  "date-fns": "4.1.0",
60
80
  "geist": "^1.7.0",
61
81
  "isbot": "^5",
82
+ "jsdom": "^26",
62
83
  "lucide-react": "0.575.0",
63
84
  "next-themes": "0.4.6",
64
85
  "react": "19.1.0",
@@ -68,16 +89,9 @@
68
89
  "sonner": "2.0.7",
69
90
  "tailwind-merge": "3.5.0",
70
91
  "tailwindcss": "4",
71
- "typescript": "^5.9.3",
92
+ "typescript": "^6.0.3",
72
93
  "vite": "^6",
73
- "vitest": "^3",
74
- "jsdom": "^26",
75
- "@workflow/core": "5.0.0-beta.5",
76
- "@workflow/errors": "5.0.0-beta.2",
77
- "@workflow/utils": "5.0.0-beta.2",
78
- "@workflow/web-shared": "5.0.0-beta.5",
79
- "@workflow/world": "5.0.0-beta.2",
80
- "@workflow/world-vercel": "5.0.0-beta.4"
94
+ "vitest": "^3.2.7"
81
95
  },
82
96
  "scripts": {
83
97
  "dev": "react-router dev",
package/registry.d.ts ADDED
@@ -0,0 +1,29 @@
1
+ /** A recorded embedded-dashboard instance. */
2
+ export interface DashboardRegistryEntry {
3
+ /** Public base URL the dashboard is reachable at (e.g. `http://localhost:3000/_workflow`). */
4
+ url: string;
5
+ /** Mount path the dashboard is served under (e.g. `/_workflow`; `""` at root). */
6
+ basename: string;
7
+ /** World backend identifier, for display (e.g. `local`, `@workflow/world-postgres`). */
8
+ world: string;
9
+ /** PID of the process hosting the dashboard. */
10
+ pid: number;
11
+ /** ISO timestamp the entry was recorded. */
12
+ startedAt: string;
13
+ }
14
+
15
+ /** Absolute path of the registry file for the current working directory. */
16
+ export function dashboardRegistryPath(): string;
17
+
18
+ /** Read the registry entries (always returns an array; never throws). */
19
+ export function readDashboardRegistry(): DashboardRegistryEntry[];
20
+
21
+ /**
22
+ * Record this process's embedded dashboard (idempotent per process). Entirely
23
+ * best-effort — never throws.
24
+ */
25
+ export function recordDashboard(entry: {
26
+ url: string;
27
+ basename?: string;
28
+ world?: string;
29
+ }): void;
package/registry.js ADDED
@@ -0,0 +1,89 @@
1
+ /**
2
+ * Best-effort registry of live, embedded observability dashboards.
3
+ *
4
+ * When the dashboard is embedded in another server (e.g. `@workflow/nitro` at
5
+ * `/_workflow`), the running process records its public URL here. The CLI
6
+ * (`workflow web` / `inspect … --web`) reads this so it can point the user at
7
+ * an already-running dashboard instead of spawning a redundant standalone
8
+ * server on a fixed port.
9
+ *
10
+ * This is a coordination hint, never a source of truth: entries may be stale
11
+ * (a SIGKILL'd dev server can't clean up), so consumers MUST health-check a URL
12
+ * before trusting it. A missing or corrupt registry is treated as "none".
13
+ *
14
+ * The file is keyed by the current working directory so multiple projects don't
15
+ * collide, while multiple dev servers for the same project share one file
16
+ * (keyed by pid within it).
17
+ */
18
+
19
+ import { createHash } from 'node:crypto';
20
+ import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
21
+ import os from 'node:os';
22
+ import path from 'node:path';
23
+
24
+ /** Absolute path of the registry file for the current working directory. */
25
+ export function dashboardRegistryPath() {
26
+ const key = createHash('sha256')
27
+ .update(process.cwd())
28
+ .digest('hex')
29
+ .slice(0, 16);
30
+ return path.join(os.tmpdir(), 'workflow-observability', `${key}.json`);
31
+ }
32
+
33
+ /** Read the registry entries (always returns an array; never throws). */
34
+ export function readDashboardRegistry() {
35
+ try {
36
+ const parsed = JSON.parse(readFileSync(dashboardRegistryPath(), 'utf8'));
37
+ return Array.isArray(parsed) ? parsed : [];
38
+ } catch {
39
+ return [];
40
+ }
41
+ }
42
+
43
+ function writeEntries(entries) {
44
+ const file = dashboardRegistryPath();
45
+ mkdirSync(path.dirname(file), { recursive: true });
46
+ writeFileSync(file, JSON.stringify(entries));
47
+ }
48
+
49
+ let recorded = false;
50
+
51
+ /**
52
+ * Record this process's embedded dashboard in the registry (idempotent per
53
+ * process). Call once the public origin is known (e.g. from the first request).
54
+ * Entirely best-effort — never throws.
55
+ *
56
+ * @param {{ url: string, basename?: string, world?: string }} entry
57
+ */
58
+ export function recordDashboard(entry) {
59
+ if (recorded) return;
60
+ recorded = true;
61
+ try {
62
+ const others = readDashboardRegistry().filter(
63
+ (e) => e && e.pid !== process.pid
64
+ );
65
+ others.push({
66
+ url: entry.url,
67
+ basename: entry.basename ?? '',
68
+ world: entry.world ?? '',
69
+ pid: process.pid,
70
+ startedAt: new Date().toISOString(),
71
+ });
72
+ writeEntries(others);
73
+ // Best-effort cleanup on normal exit. Signal-terminated processes (Ctrl+C,
74
+ // SIGKILL) won't run this — consumers prune via health checks instead. We
75
+ // intentionally don't hook SIGINT/SIGTERM so we don't interfere with the
76
+ // host framework's own shutdown handling.
77
+ process.once('exit', () => {
78
+ try {
79
+ writeEntries(
80
+ readDashboardRegistry().filter((e) => e && e.pid !== process.pid)
81
+ );
82
+ } catch {
83
+ // ignore
84
+ }
85
+ });
86
+ } catch {
87
+ // best-effort: a failed write must never break request handling
88
+ }
89
+ }
@@ -1,28 +0,0 @@
1
- import { W as WorkflowRuntimeError } from "./index-B4xuge0I.js";
2
- import "./index-BsV8i_Jn.js";
3
- const NONCE_LENGTH = 12;
4
- const TAG_LENGTH = 128;
5
- const KEY_LENGTH = 32;
6
- async function importKey(raw) {
7
- if (raw.byteLength !== KEY_LENGTH) {
8
- throw new WorkflowRuntimeError(`Encryption key must be exactly ${KEY_LENGTH} bytes, got ${raw.byteLength}`);
9
- }
10
- return globalThis.crypto.subtle.importKey("raw", raw, "AES-GCM", false, [
11
- "encrypt",
12
- "decrypt"
13
- ]);
14
- }
15
- async function decrypt(key, data) {
16
- const minLength = NONCE_LENGTH + TAG_LENGTH / 8;
17
- if (data.byteLength < minLength) {
18
- throw new WorkflowRuntimeError(`Encrypted data too short: expected at least ${minLength} bytes, got ${data.byteLength}`);
19
- }
20
- const nonce = data.subarray(0, NONCE_LENGTH);
21
- const ciphertext = data.subarray(NONCE_LENGTH);
22
- const plaintext = await globalThis.crypto.subtle.decrypt({ name: "AES-GCM", iv: nonce, tagLength: TAG_LENGTH }, key, ciphertext);
23
- return new Uint8Array(plaintext);
24
- }
25
- export {
26
- decrypt,
27
- importKey
28
- };