This is an automated email from the ASF dual-hosted git repository.

pierrejeambrun pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/airflow.git


The following commit(s) were added to refs/heads/main by this push:
     new 6e0dd5500f8 Default the TypeScript task id to the handler function 
name (#73243)
6e0dd5500f8 is described below

commit 6e0dd5500f8b1b1c0d445484ce9ec8644af19987
Author: Pierre Jeambrun <[email protected]>
AuthorDate: Wed Sep 16 17:09:38 2026 +0200

    Default the TypeScript task id to the handler function name (#73243)
    
    * Default the TypeScript task id to the handler function name
    
    A native TypeScript Dag spells out both ends of every task name in
    TypeScript, so the task id can safely fall back to the handler's function
    name and authors need not repeat it. A mixed-language handler binds to a
    Python-owned task whose id rarely matches a TypeScript name, so it keeps
    recommending an explicit id.
    
    * Add a TaskSpec example to the native TypeScript Dag ADR
    
    From review: show how a task's id and options can be set through the
    TaskSpec, and fix a subject-verb agreement in the mixed-language note.
---
 ts-sdk/adr/0001-mixed-lang-dag-interface.md |  6 ++++-
 ts-sdk/adr/0002-native-dag-interface.md     | 40 ++++++++++++++++++++++++++---
 2 files changed, 41 insertions(+), 5 deletions(-)

diff --git a/ts-sdk/adr/0001-mixed-lang-dag-interface.md 
b/ts-sdk/adr/0001-mixed-lang-dag-interface.md
index f12226c9f27..ae026a856c2 100644
--- a/ts-sdk/adr/0001-mixed-lang-dag-interface.md
+++ b/ts-sdk/adr/0001-mixed-lang-dag-interface.md
@@ -31,7 +31,11 @@ Proposed. Revised after the review on #72047.
 2. **A bundle has one registration verb and serves itself.** 
`bundle.register(...)` takes Dags and
    task handlers alike, in any mixture, and `await bundle.serve()` starts the 
runtime over them.
    `Bundle` replaces `DagRegistry`, and the free `serveDags(registry)` 
function goes with it.
-3. **task_id is always written out**; nothing is derived from the handler's 
function name.
+3. **task_id is optional but should probably be written out here.** Most of 
the time because of
+   naming convention difference between python and typescript, names won't 
match. Omitting it defaults
+   the id to the handler's function name — `new TaskHandler(transformStep)` 
binds the `"transformStep"` fn to the
+   "transformStep" task. On the other hand `new TaskHandler("transform_step", 
transformStep)` will bind to
+   "transform_step" task.
 4. **A handler is a plain function of its own data**, destructured by name. 
`getContext()` and
    `getClient()` supply the rest, so nothing the SDK injects shares a 
namespace with an author's
    arguments.
diff --git a/ts-sdk/adr/0002-native-dag-interface.md 
b/ts-sdk/adr/0002-native-dag-interface.md
index 03c9a9e1197..f1ba74b4a5f 100644
--- a/ts-sdk/adr/0002-native-dag-interface.md
+++ b/ts-sdk/adr/0002-native-dag-interface.md
@@ -25,8 +25,10 @@ Proposed. Revised after the review on #72047.
 
 ## Decision
 
-1. **`dag.task(taskId, handler)` returns a factory.** Calling the factory both 
places the task in the
-   Dag and supplies its arguments by name — the shape Python TaskFlow uses for
+1. **`dag.task(handler)` returns a factory, and the task id is optional.** 
With no id the task takes
+   the handler's function name (`dag.task(extract)` → task `"extract"`); 
`dag.task(taskId, handler)`
+   sets it explicitly, which an anonymous handler must do. Calling the factory 
both places the task in
+   the Dag and supplies its arguments by name — the shape Python TaskFlow uses 
for
    `load(transformed=transform(...))`.
 2. **The call graph is the task graph.** `tsc` checks every wired key against 
the handler's own
    parameter type, and a `TaskRef` exists only once its producing call has 
returned, so a cycle
@@ -36,8 +38,8 @@ Proposed. Revised after the review on #72047.
 4. **`before` and `after` draw order-only edges** — the TypeScript pair for 
`>>` and `<<`, both
    variadic so one call fans out.
 5. **The Dag file owns Dag-level and task-level configuration.** `new 
Dag(dagId, spec)` carries the
-   schedule and the rest of `DagSpec`; `dag.task(taskId, handler, spec)` 
carries per-task options
-   such as retries. Python owns both in the mixed-language case
+   schedule and the rest of `DagSpec`; `dag.task(handler, spec)` — or 
`dag.task(taskId, handler, spec)` —
+   carries per-task options such as retries. Python owns both in the 
mixed-language case
    ([ADR-0001](0001-mixed-lang-dag-interface.md)), which is the difference 
between the two modes.
 6. **A handler is a plain function of its own data**; `getContext()` and 
`getClient()` supply the rest.
 7. **One registration verb**: `bundle.register(dag)`, the same call that takes 
task handlers, with
@@ -90,6 +92,30 @@ One statement per task, with each ref named, is the form to 
write. Nesting the c
 shorthand for a two-task chain, not the general shape: a Dag of twenty tasks 
reads as twenty flat
 statements, never as a twenty-deep expression.
 
+### Omitting the task id
+
+A native task defaults its id to the handler's function name, so a named 
handler needs none:
+
+```ts
+const extract = dag.task(async function extract(): Promise<number> {
+  return 42;
+});
+// task id "extract"
+```
+
+The id comes from the handler's *source* name, resolved when the bundle is 
packed and written into
+the registration — not from `handler.name` at runtime, which minification 
renames (see
+Implementation Notes). A handler with no source name — a bare anonymous arrow 
passed inline,
+`dag.task(async () => 42)` — has nothing to resolve and is a compile error 
until given an explicit
+id. This default is for native Dags, where both ends of every name are 
TypeScript; a mixed-language
+handler follows the same convention but should probably use the explicit form, 
more information in ([ADR-0001](0001-mixed-lang-dag-interface.md)).
+
+The `TaskSpec` also carries the task id, so it can be set alongside the other 
task options:
+
+```ts
+const extract = dag.task(async () => 42, { taskId: "extract", retries: 2 });
+```
+
 ### Order-only edges: `>>` and `<<`
 
 An edge that carries no data has no key to put in the wiring object, so it is 
drawn directly between
@@ -153,6 +179,12 @@ convention.
   (`ts-sdk/src/sdk/dag.ts`), so task fields land on a path that exists rather 
than a new one.
 - **A `TaskRef` is inert** — a handle for wiring, not a promise. Nothing in a 
Dag file executes a task
   body.
+- **A defaulted task id is resolved at pack time, not read at runtime.** 
esbuild renames function
+  identifiers, so `handler.name` in a packed bundle is the minified name, not 
the author's. The pack
+  step (`ts-sdk/src/cli/pack.ts`) therefore reads an omitted id from the 
handler's declared name in
+  source and writes it into the registration, rather than depending on 
`handler.name` or enabling
+  esbuild's `keepNames` across the whole bundle. A handler with no source name 
leaves nothing to
+  read, which is why an anonymous handler must state its id.
 - **`withArgNames` and the name folding behind it** 
([ADR-0001](0001-mixed-lang-dag-interface.md))
   exist for the mixed-language case and are never needed here: both ends of 
every name are
   TypeScript, so `tsc` checks the wiring end to end and there is no foreign 
name to reconcile.

Reply via email to