From 549fdf8ed797f6880f4214e5e185091bafb9d8fc Mon Sep 17 00:00:00 2001 From: Sohum Trivedi Date: Tue, 14 Jul 2026 07:40:23 -0700 Subject: [PATCH] Fix Workflow interface to use array types instead of single-element tuples The exported `Workflow` interface declared `tags`, `arguments`, and `shells` as single-element tuple types (`[string]`, `[Argument]`, `[Shell]`) rather than arrays. A tuple type of length one only accepts exactly one element, so under `strict` type-checking any workflow with two or more tags, arguments, or shells fails to type-check against the published `warp-workflows` types -- including the repo's own documented examples (FORMAT.md's three-argument for-loop and two-tag list, and the README example's `shells: []`, which a one-tuple rejects for having too few elements). 108 of the 332 shipped specs have two or more arguments. Change the three fields to array types (`string[]`, `Argument[]`, `Shell[]`). This is a declaration-only change; runtime is unaffected because index.ts casts the yaml-loader output with `as Workflow`. dist/index.d.ts regenerates from this source with the corrected array types. Add a type-level regression test (build_ts/type-tests/workflow-types.test-d.ts) that constructs Workflow literals mirroring the documented multi-value and empty-shells examples, compiled via a new tsconfig.typetest.json through the `check-types` script. It fails to compile before this change and passes after. An `exclude` entry keeps the fixture out of the emitted dist bundle. --- build_ts/index.ts | 6 +-- build_ts/package.json | 1 + build_ts/tsconfig.json | 3 +- build_ts/tsconfig.typetest.json | 9 ++++ build_ts/type-tests/workflow-types.test-d.ts | 48 ++++++++++++++++++++ 5 files changed, 63 insertions(+), 4 deletions(-) create mode 100644 build_ts/tsconfig.typetest.json create mode 100644 build_ts/type-tests/workflow-types.test-d.ts diff --git a/build_ts/index.ts b/build_ts/index.ts index 311bef4..8a72839 100644 --- a/build_ts/index.ts +++ b/build_ts/index.ts @@ -2,13 +2,13 @@ export interface Workflow { slug: WorkflowSlug; name: string; command: string; - tags?: [string]; + tags?: string[]; description?: string; - arguments?: [Argument]; + arguments?: Argument[]; source_url?: string; author?: string; author_url?: string; - shells?: [Shell]; + shells?: Shell[]; relative_git_url: string; } diff --git a/build_ts/package.json b/build_ts/package.json index c6e5790..6cf11c5 100644 --- a/build_ts/package.json +++ b/build_ts/package.json @@ -6,6 +6,7 @@ "types": "dist/index.d.ts", "scripts": { "build": "tsc && webpack", + "check-types": "tsc -p tsconfig.typetest.json", "prepare": "npm run build" }, "author": "Warp Team (eng@warp.dev)", diff --git a/build_ts/tsconfig.json b/build_ts/tsconfig.json index 4682020..b50d848 100644 --- a/build_ts/tsconfig.json +++ b/build_ts/tsconfig.json @@ -9,5 +9,6 @@ "strict": true, "esModuleInterop": true, "moduleResolution": "node" - } + }, + "exclude": ["node_modules", "dist", "type-tests"] } diff --git a/build_ts/tsconfig.typetest.json b/build_ts/tsconfig.typetest.json new file mode 100644 index 0000000..8270b93 --- /dev/null +++ b/build_ts/tsconfig.typetest.json @@ -0,0 +1,9 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "noEmit": true, + "declaration": false, + "sourceMap": false + }, + "include": ["index.ts", "type-tests/**/*.ts"] +} diff --git a/build_ts/type-tests/workflow-types.test-d.ts b/build_ts/type-tests/workflow-types.test-d.ts new file mode 100644 index 0000000..12aa056 --- /dev/null +++ b/build_ts/type-tests/workflow-types.test-d.ts @@ -0,0 +1,48 @@ +// Type-level regression test for the exported `Workflow` interface. +// +// These declarations do not run; they exist so that `tsc` fails to compile if +// the multi-value fields (`tags`, `arguments`, `shells`) are ever narrowed back +// to single-element tuple types. Every literal below mirrors an example that is +// already documented in FORMAT.md / README.md or shipped under specs/. +// +// Compile with: npm run check-types + +import { Argument, Shell, Workflow } from "../index"; + +// Mirrors FORMAT.md's own for-loop example, which documents a command with the +// three arguments `variable`, `sequence`, and `command`, alongside a two-tag +// list and two valid shells. Under the old `[string]` / `[Argument]` / `[Shell]` +// tuple types this fails to type-check even though it is the documented format. +const multiValued: Workflow = { + slug: "for-loop", + name: "For loop", + command: "for {{variable}} in {{sequence}}; do\n {{command}}\ndone", + tags: ["shell", "loops"], + description: "Iterate over a sequence", + arguments: [ + { name: "variable" }, + { name: "sequence" }, + { name: "command" }, + ], + shells: [Shell.Bash, Shell.Zsh], + relative_git_url: "/specs/shell/for_loop.yaml", +}; + +// Mirrors the README's example workflow, which uses a single tag/argument and +// an empty `shells: []` (valid in all shells). A single-element tuple type would +// reject `shells: []` because it requires exactly one element. +const singleValued: Workflow = { + slug: "brew-rmtree", + name: "Uninstall a Homebrew package and all of its dependencies", + command: "brew tap beeftornado/rmtree\nbrew rmtree {{package_name}}", + tags: ["homebrew"], + arguments: [{ name: "package_name" }], + shells: [], + relative_git_url: "/specs/homebrew/brew_rmtree.yaml", +}; + +// Reference the declarations so `noUnusedLocals`-style checks stay quiet and the +// `Argument` import is exercised. +const _arg: Argument = multiValued.arguments![0]; +void _arg; +void singleValued;