Skip to content

chore: drop chalk in favor of node:util styleText - #1934

Draft
toiroakr wants to merge 946 commits into
mainfrom
chore/remove-chalk
Draft

chore: drop chalk in favor of node:util styleText#1934
toiroakr wants to merge 946 commits into
mainfrom
chore/remove-chalk

Conversation

@toiroakr

@toiroakr toiroakr commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Remove chalk from the runtime dependencies of @tailor-platform/sdk, @tailor-platform/sdk-codemod, @tailor-platform/sdk-plugin-seed and @tailor-platform/sdk-plugin-tailordb-erd. It was used purely for terminal styling, which node:util styleText covers. chalk@5 is now absent from the lockfile; the remaining chalk@4 comes from madge/ora in devDependencies only, so the published dependency closure is chalk-free.

  • Decide colour support per output stream. chalk resolved one level from stdout and applied it to every string regardless of destination, and styleText validates against stdout by default, so redirecting stdout dropped the colours from diagnostics that were still going to a terminal, while redirecting stderr wrote escape codes into the log file. Style functions now always emit escapes and each write boundary renders for the stream it writes to — stderr for log lines, spinner frames and codemod diffs, stdout for logger.out data and the seed confirmation prompt. Detection is delegated to styleText itself, so the TTY / NO_COLOR / FORCE_COLOR rules stay Node's.

    stdout stderr before (chalk and stdout-only detection) after
    TTY TTY both coloured unchanged
    pipe TTY diagnostics lose colour diagnostics keep colour
    TTY pipe escape codes land in the redirected log log stays plain
    pipe pipe plain unchanged
  • One implementation of that rule. It started as four copies, one per package that writes styled output, and now lives in @tailor-platform/shared — a private package consumed as a devDependency and bundled into each consumer, wired the same way as @tailor-platform/tailor-proto. Nothing about it reaches consumers of the published packages: the built output inlines it, and only sourcemaps mention the package name. Subpaths (./color today) keep it from becoming a junk drawer.

  • Declare the runtime each package needs. styleText's stream / validateStream options arrived in Node 20.12, but only the sdk package declared an engine range, and the codemod was still built for node18. @tailor-platform/sdk-codemod, @tailor-platform/sdk-plugin-seed and @tailor-platform/sdk-plugin-tailordb-erd now declare node >=22.15.0 / bun >=1.2.0 like the sdk package, so an old runtime reports a version mismatch at install time rather than a missing function at the first coloured line.

  • Drop the now-dead chalk entry from the CLI no-restricted-imports rule; the styleText restriction stays, with logger.ts / errors.ts remaining the only exempt files.

Notes

Incidental findings from the migration:

  • cli/commands/function/logs.ts passed string | undefined into a style function. chalk's signature accepted unknown[], so the destructured headerLine never had to be narrowed; the stricter (text: string) => string type surfaces it.
  • sdk-plugin-seed's apply.test.ts replaced the whole logger module with a factory that only provided logger, which broke once apply.ts also imported styles. The mock now spreads importOriginal() and overrides only logger, so a new export cannot break it again.
  • Because styled strings now carry escapes before they reach a stream, tests that assert on formatted output strip control characters (formatLogLine) or compare against the symbols constants rather than literal glyphs (deploy plan formatters). Those assertions previously depended on vitest running outside a TTY, so they would have failed under FORCE_COLOR=1.
  • Table alignment is unaffected: ascii-table and the spinner already measure width with ANSI sequences stripped.

dqn and others added 30 commits July 15, 2026 09:55
…ction

Also describe the refined injection behavior in the cli-plugins
changeset.
The .validate() callback's issues() field parameter used a self-generic
<P extends DottedPaths<...>> signature. Comparing two such generic call
signatures requires TypeScript to check the inner constraint for identity
rather than assignability, which spuriously rejected structurally-equal
TailorDBType instances built from the same custom fields but derived
through different generic instantiation paths (e.g. a table passed across
a module factory boundary), surfacing as TS2719 ("Two different types
with this name exist, but they are unrelated"). Making the parameter a
plain (non-generic) union keeps strict field-path checking while using
ordinary assignability for these comparisons.
Comment restated design rationale already covered by the commit message
and regression test; per the code-comments convention, source comments
should express constraints the code can't otherwise convey, not PR
rationale.
Rename to make clear this documents assignability when a factory's
parameter type is derived via explicit type-argument passthrough,
rather than serving as a direct regression guard for the TS2719
false-positive reported upstream. A standalone repro of that exact
compiler behavior could not be constructed without the consuming
package's full module composition.
Resolve conflicts by taking v2's dependency bumps and generated workflow
types, and drop the pkg.pr.new zinfer override now that zinfer 0.2.5
includes commit 054a3a5.
fix(tailordb): make issues() field param non-generic
… to .start()

Remove the pre-alignment `tailor.workflow` names `triggerWorkflow`,
`triggerJobFunction`, and `resumeWorkflow` (and their `TriggerWorkflowOptions`
/ `TriggerJobFunctionOptions` option types) from `@tailor-platform/sdk/runtime`,
the ambient `@tailor-platform/sdk/runtime/globals` types, and the
`mockWorkflow()` test facade. Use the canonical `startWorkflow`,
`startJobFunction`, and `resumeWorkflowExecution` names instead.

Also rename `Workflow.trigger()` (from `createWorkflow()`) and
`WorkflowJob.trigger()` (from `createWorkflowJob()`) to `.start()`, aligning
the SDK's ergonomic verb with the platform's `start*` RPC vocabulary.
`mockWorkflow()`'s `wf.job()`/`wf.workflow()` mocks, `wf.setTriggerHandler` /
`wf.triggeredJobs`, and the CLI bundler's trigger detection/codegen are
updated to match.

Adds the `v2/workflow-trigger-rename` codemod (automatic) for the low-level
rename, and registers `v2/workflow-start-rename` (manual, guided by prompt)
for the `.trigger()` -> `.start()` call-site rename, since distinguishing a
workflow/job `.trigger()` call from an unrelated object's own method isn't
reliably decidable from a single file's syntax.
…rgs-contract

fix(executor): align workflow argument contracts
`docs/services/workflow.md` links to
`example/resolvers/startWorkflow.ts` on `main`, but that file only exists
on the `v2` branch until it merges. Exclude the URL from lychee's link
check so this known, temporary, non-blocking gap doesn't fail CI; it
self-resolves once `v2` lands on `main`.
# Conflicts:
#	packages/sdk/src/configure/services/executor/executor.ts
- Rename the internal `getTriggerCallInfo`/`TriggerCallInfo` AST helper to
  `getStartCallInfo`/`StartCallInfo`: it now detects `.start(...)` calls, and
  its JSDoc already said so, so the old name was misleading next to it.
- Fix `packages/sdk/src/vitest/globals.ts`'s comment on the default workflow
  runtime: it doesn't run the real job chain locally (that's
  `runWorkflowLocally()`) — job/wait/resolve calls throw a helpful error by
  default, and workflow starts return a placeholder execution id.
- Fix "in separated job" -> "in a separate job" in docs/services/workflow.md.
…ch helper

Share the plugin slug computation between dispatch and the install hint,
and move the hint branch out of runMain so it can be unit tested.
The v2 branch releases under the npm next dist-tag while changesets
pre-release mode is active; a plain install would resolve to the 0.0.0
bootstrap version on latest.
- The bundler's and trigger-transformer's `.start(` pre-checks used a plain
  substring match, which misses whitespace/newline-formatted calls (e.g.
  `.start (` or `.start\n(`) and would silently skip the AST transform,
  leaving an untransformed `.start(...)` call to throw at runtime in the
  bundle. Add a shared `hasStartCall()` regex helper and use it in all three
  call sites (trigger-transformer.ts, bundler.ts x2).
- Fix stale "trigger"/"triggerJobFunction" wording in
  ast-transformer.test.ts comments and titles that no longer match the
  `startJobFunction` assertions they describe.
The v2 merge bumped tsdown to 0.22.5 everywhere else, dropping the
0.22.4 lockfile entry the plugin still referenced and breaking
frozen-lockfile installs.
install-deps now consumes the pending-rebuild state while the plugin's
dist is still missing, so the later rebuild --pending no longer relinks
the skipped bin. Linking at install time against a stub removes the
relink dependency entirely.
…h the public API rename

Rename trigger-context.ts/trigger-transformer.ts and their exported
identifiers (TriggerContext, TriggerTarget, TriggerModuleBindings,
ResolvedTriggerCall, transformFunctionTriggers,
createTriggerTransformPlugin, detectResolvedTriggerCalls, etc.) to
their Start* equivalents, aligning internal AST/bundler naming with
the .start() rename. Unrelated Trigger usages (executor/IdP event
triggers) are untouched.
…kflow-trigger-dispatch

sdk-codemod's CLI applies all applicable codemods in a single --from/--to
pass. v2/workflow-start-rename has no scriptPath, so it never
auto-transforms .trigger() to .start() within that pass, and pre-migration
v1 code cannot contain .start( at all (the method did not exist under that
name). The scenario this pattern guarded against — a file already renamed
to .start( before workflow-trigger-dispatch's suspiciousPatterns are
checked — is unreachable in the tool's actual single-shot usage.
…lugin

feat(sdk)!: extract tailordb erd into a CLI plugin package
@github-actions

This comment has been minimized.

This comment was marked as outdated.

renderFor took a "stdout" | "stderr" target, and the spinner mapped its
configurable stream onto one of them, so every stream other than process.stdout
followed stderr's color support. A spinner pointed at a file stream would have
written escape codes into the file whenever stderr happened to be a terminal.

renderFor now takes the stream and asks styleText about that stream directly. An
instanceof Stream guard keeps the probe from rejecting test doubles, which are
treated as colorless as before. This drops the OutputTarget type and its mapping
helper, so there is one fewer place for a destination and its color decision to
disagree.
@github-actions

This comment has been minimized.

This comment was marked as outdated.

The color factory and the per-stream strip lived in four copies, one per
package that writes styled output. Since they encode one rule — always emit
escapes, drop them for destinations without color support — four copies is
four chances for that rule to drift.

They now live in @tailor-platform/shared, a private package consumed as a
devDependency and bundled into each consumer, following the same wiring as
@tailor-platform/tailor-proto. Nothing about it reaches consumers of the
published packages: the built output inlines it, and only sourcemaps mention
the package name.

Subpaths keep the package from becoming a junk drawer as more moves in;
./color is the only one for now.
@github-actions

This comment has been minimized.

This comment was marked as outdated.

Styling moved from chalk, which ran anywhere, to node:util styleText, which
needs the stream and validateStream options added in Node 20.12. Only the sdk
package declared an engine range, and the codemod was still built for node18,
so installing it on Node 18 or 20 would have produced a missing-function error
at the first colored line instead of a version mismatch at install time.

sdk-codemod, sdk-plugin-seed and sdk-plugin-tailordb-erd now declare the same
range as the sdk package, and the codemod builds for node22 like the rest.

Also corrects the comment above the color-support probe: styleText throws for
values that are not streams, which is why the instanceof check filters test
doubles out before the call rather than letting styleText handle them.
@github-actions

This comment has been minimized.

This comment was marked as outdated.

The shared module exported a curried factory, so every consumer had to name the
result before using it: the codemod kept four module-level aliases whose only
job was to turn color("bold") into bold. Exporting the style functions keyed by
name removes that step — call sites now read color.bold(text), and the codemod
passes color.green to its line writer instead of aliasing it first.

The names are listed explicitly rather than derived from util.inspect.colors.
That list is missing five names styleText nevertheless accepts (grey,
blackBright, bgGrey, bgBlackBright, none), so deriving from it while typing the
result from the styleText parameter union would hand out properties that are
undefined at runtime.

The one compound style, the gray italic placeholder, composes instead:
color.italic(color.gray(text)). styleText reopens the outer style after the
inner one closes, so the rendering is unchanged.
@github-actions

This comment has been minimized.

Importing the shared color module made rolldown-plugin-dts warn on every sdk
build: it resolves declarations lazily and cannot do so for a TypeScript source
file outside this package. The declaration output is identical either way — 258
files before and after, and every types target in package.json#exports is
present in both — so the warning was noise, but noise on every build teaches
people to skip reading it.

Enabling the eager option the warning itself recommends costs about three
seconds of build time. Shipping compiled output from the shared package would
also silence it, at the price of a dist that can go stale against its source.
@github-actions

Copy link
Copy Markdown

Code Metrics Report (packages/sdk)

main (c5e1a27) #1934 (1351c5a) +/-
Coverage 75.4% 77.7% +2.2%
Code to Test Ratio 1:0.4 1:0.4 +0.0
Details
  |                    | main (c5e1a27) | #1934 (1351c5a) |  +/-  |
  |--------------------|----------------|-----------------|-------|
+ | Coverage           |          75.4% |           77.7% | +2.2% |
  |   Files            |            467 |             459 |    -8 |
  |   Lines            |          17741 |           17485 |  -256 |
+ |   Covered          |          13390 |           13589 |  +199 |
+ | Code to Test Ratio |          1:0.4 |           1:0.4 |  +0.0 |
  |   Code             |         120229 |          123407 | +3178 |
+ |   Test             |          56943 |           59749 | +2806 |

Code coverage of files in pull request scope (77.3% → 83.6%)

Skip file coverages because there are too many files (103)

SDK Configure Bundle Size

main (c5e1a27) #1934 (1351c5a) +/-
configure-index-size 40.17KB 39.25KB -0.92KB
dependency-chunks-size 29.87KB 24.96KB -4.91KB
total-bundle-size 70.04KB 64.22KB -5.82KB

Runtime Performance

main (c5e1a27) #1934 (1351c5a) +/-
Generate Median 3,085ms 2,489ms -596ms
Generate Max 3,104ms 2,507ms -597ms
Apply Build Median 3,154ms 2,540ms -614ms
Apply Build Max 3,178ms 2,555ms -623ms

Type Performance (instantiations)

main (c5e1a27) #1934 (1351c5a) +/-
tailordb-basic 44,253 40,450 -3,803
tailordb-optional 4,451 4,535 84
tailordb-relation 6,220 3,854 -2,366
tailordb-validate 753 666 -87
tailordb-hooks 5,279 5,499 220
tailordb-object 12,547 16,345 3,798
tailordb-enum 1,486 1,538 52
resolver-basic 9,265 12,120 2,855
resolver-nested 26,132 35,419 9,287
resolver-array 18,072 24,056 5,984
executor-schedule 4,318 4,489 171
executor-webhook 959 1,130 171
executor-record 6,772 4,542 -2,230
executor-resolver 4,121 5,808 1,687
executor-operation-function 947 1,118 171
executor-operation-gql 955 1,126 171
executor-operation-webhook 966 1,137 171
executor-operation-workflow 1,808 1,931 123

Reported by octocov

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 27 out of 28 changed files in this pull request and generated no new comments.

Files not reviewed (1)
  • pnpm-lock.yaml: Generated file

@toiroakr
toiroakr force-pushed the v2 branch 3 times, most recently from 77bc78c to a3c4978 Compare July 31, 2026 09:51
Base automatically changed from v2 to main July 31, 2026 09:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants