@theholocron/astromech 5.0.0-alpha.11 → 5.0.0-alpha.110

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 CHANGED
@@ -33,7 +33,7 @@ const report = astromech.run("verification.unitTests", { passthrough: ["--watch"
33
33
  `holocron run verification.unitTests` runs your tests — you don't tell it
34
34
  turbo vs pnpm vs npm, or which runner:
35
35
 
36
- ```
36
+ ```text
37
37
  1. turbo.json defines the task → turbo run <task>
38
38
  2. package.json has a <task> script → <detected pm> run <task>
39
39
  (a "holocron run …" thin caller is skipped — no recursion)
@@ -53,28 +53,28 @@ what it actually runs, and why. Tool names never appear in
53
53
  `holocron.config.ts` — they're an implementation detail this table
54
54
  documents, not a naming convention repos need to follow.
55
55
 
56
- | Task | Runs | Notes |
57
- | ---------------------------------------- | -------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- |
58
- | `verification.unitTests` | vitest | carries the `--coverage` org default |
59
- | `verification.typeSafety` | tsc | `tsc --noEmit` |
60
- | `verification.performance` | Lighthouse CI | only runs with a `lighthouse.config.*` present |
61
- | `sourceQuality.staticAnalysis` | eslint, actionlint, git-merge-conflict-markers | a `linterGroup` — see below |
62
- | `sourceQuality.formatting` | prettier, editorconfig-checker, markdownlint-cli2 | a `linterGroup` |
63
- | `sourceQuality.structuredDataValidation` | yamllint | |
64
- | `sourceQuality.deadCodeAnalysis` | knip | |
65
- | `security.secretDetection` | gitleaks | |
66
- | `security.codeScanning` | CodeQL | no local equivalent — CI only |
67
- | `security.dependencyReview` | GitHub's native Dependabot alerts/graph | a capability-model method (`Source.enableVulnerabilityAlerts()`), not a task |
68
- | `delivery.build` | tsdown / vite / rollup / tsc, detected from the repo's own config file | |
69
- | `delivery.publish` | semantic-release | no local equivalent — CI only; carries `preview` (npm dist-tags) |
70
- | `delivery.deploy` | Cloudflare Pages / Vercel | no local equivalent — CI only; carries `preview` |
71
- | `delivery.bundleSize` | bundle-size upload to Codecov | no local equivalent — CI only |
72
- | `platform.repoSync` | `holocron sync` | keeps generated files current |
73
- | `platform.commitStandards` | commitlint | no local equivalent — enforced by the `commit-msg` hook locally, over the PR's commit range in CI |
74
- | `platform.repoValidation` | `scripts/validate-adrs.mjs`, `scripts/validate-registry.mjs`, `scripts/validate-docs-presence.mjs` | a job-bearing task — spec/ADR frontmatter, registry-doc completeness, and docs-presence, not linters |
75
- | `knowledge.wiki` | Fern | publishes `docs/wiki/*.md` — not a sync, a publish; carries `preview` |
76
- | `knowledge.docs` | Astro build | the docs site itself; carries `preview` (defaults on) |
77
- | `knowledge.components` | Storybook build | a browsable component catalog — "Storybook" is the tool, not the intent; carries `preview` (defaults on) |
56
+ | Task | Runs | Notes |
57
+ | ---------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- |
58
+ | `verification.unitTests` | vitest | carries the `--coverage` org default |
59
+ | `verification.typeSafety` | tsc | `tsc --noEmit` |
60
+ | `verification.performance` | Lighthouse CI | only runs with a `lighthouse.config.*` present |
61
+ | `sourceQuality.staticAnalysis` | eslint, actionlint, git-merge-conflict-markers | a `linterGroup` — see below |
62
+ | `sourceQuality.formatting` | prettier, editorconfig-checker, markdownlint-cli2 | a `linterGroup` |
63
+ | `sourceQuality.structuredDataValidation` | yamllint | |
64
+ | `sourceQuality.deadCodeAnalysis` | knip | |
65
+ | `security.secretDetection` | gitleaks | |
66
+ | `security.codeScanning` | CodeQL | no local equivalent — CI only |
67
+ | `security.dependencyReview` | GitHub's native Dependabot alerts/graph | a capability-model method (`Source.enableVulnerabilityAlerts()`), not a task |
68
+ | `delivery.build` | tsdown / vite / rollup / tsc, detected from the repo's own config file, or `package.json`'s own `dependencies`/`devDependencies` if that config file isn't there | |
69
+ | `delivery.publish` | semantic-release | no local equivalent — CI only; carries `preview` (npm dist-tags) |
70
+ | `delivery.deploy` | Cloudflare Pages / Vercel | no local equivalent — CI only; carries `preview` |
71
+ | `delivery.bundleSize` | bundle-size upload to Codecov | no local equivalent — CI only |
72
+ | `platform.repoSync` | `holocron sync` | keeps generated files current |
73
+ | `platform.commitStandards` | commitlint | no local equivalent — enforced by the `commit-msg` hook locally, over the PR's commit range in CI |
74
+ | `platform.repoValidation` | `scripts/validate-adrs.mjs`, `scripts/validate-registry.mjs`, `scripts/validate-docs-presence.mjs` | a job-bearing task — spec/ADR frontmatter, registry-doc completeness, and docs-presence, not linters |
75
+ | `knowledge.wiki` | Fern | publishes `docs/wiki/*.md` — not a sync, a publish; carries `preview` |
76
+ | `knowledge.docs` | Astro build | the docs site itself; carries `preview` (defaults on) |
77
+ | `knowledge.components` | Storybook build | a browsable component catalog — "Storybook" is the tool, not the intent; carries `preview` (defaults on) |
78
78
 
79
79
  **`preview` is a cross-cutting feature, not a namespace.** npm has staging
80
80
  dist-tags, Cloudflare/Vercel do per-PR preview deploys, Fern previews
@@ -83,10 +83,12 @@ publish/deploy-shaped task can carry, toggled via that task's `with:`.
83
83
 
84
84
  **`linterGroup` tasks** (`sourceQuality.staticAnalysis`,
85
85
  `sourceQuality.formatting`) bundle more than one tool under a single
86
- required check. Each tool is still gated by its own detection rule (e.g.
87
- `eslint` only runs if `eslint.config.*` is present) — the
86
+ required check. Each tool is still gated by its own detection rule — e.g.
87
+ `eslint` runs when either a local `eslint.config.*` is present, or the
88
+ package has no local file but `@theholocron/eslint-config` resolves
89
+ (the same shared-config splice the `tool`/`detect` runner path uses) — the
88
90
  `tool name → detection rule → local binary` mapping lives in one place,
89
- `src/linters.ts`. A repo's choice of which of these run is just which
91
+ `src/linters.ts`. A repo's choice of which of these run is based on which
90
92
  tasks it includes in `tasks: [...]`, same as any other task.
91
93
 
92
94
  **No super-linter.** Each task above runs its own tool directly, through
@@ -116,6 +118,9 @@ export default defineConfig({
116
118
  `loadTasksConfig(cwd)` resolves the manifest: the `tasks` key of
117
119
  `holocron.config.*` (a bare item array), then a dedicated
118
120
  `astromech.config.*` merged on top (dedicated wins; arrays concatenate).
121
+ `mergeTasksLayers(parentTasks, dedicated)` is that merge step on its own,
122
+ for callers that load the two sources some other way (Sentinel reads them
123
+ through the GitHub API).
119
124
 
120
125
  | `TaskEntry` field | Effect |
121
126
  | ------------------------ | ----------------------------------------------------------- |
@@ -187,6 +192,27 @@ template. `holocron setup` writes the result via the `source` capability;
187
192
  this method never touches the filesystem beyond reading `packages/*` and
188
193
  `apps/*` under `cwd`.
189
194
 
195
+ ## Release-time deploys — `deployOnRelease`
196
+
197
+ Deploys the workspace packages whose manifest declares it (holocron#930):
198
+
199
+ ```ts
200
+ // packages/<app>/holocron.config.ts
201
+ tasks: [{ name: "delivery.deploy", with: { on: "release", channel: "alpha" } }];
202
+ ```
203
+
204
+ `holocron deploy-on-release --channel=<alpha|main> --from=<previous release commit> --to=<release commit>`
205
+ (or `deployOnRelease()` from `@theholocron/astromech`) deploys each such package
206
+ when the release is on its `channel` (default `main`, the stable release;
207
+ set `alpha` for prereleases) and changes the package or one of its `workspace:*` dependencies
208
+ (`with.paths` overrides that), once npm serves the release's new dependency
209
+ versions, by running `pnpm --filter <package> delivery.deploy`. A package
210
+ that doesn't apply to the release is skipped; a deploy that fails exits
211
+ non-zero. The shared `delivery.publish` workflow runs it as a separate `deploy`
212
+ job after the release job (opt in with `deploy-on-release: true`), so a deploy
213
+ is its own re-runnable check. A `delivery.deploy` task with `on: "release"`
214
+ generates no workflow.
215
+
190
216
  ## Development
191
217
 
192
218
  | Script | Description |
@@ -14,6 +14,13 @@ import { i as normalizeTaskEntry, n as TaskEntry, r as TasksConfig, t as TaskCon
14
14
  declare const defineConfig: <C extends TasksConfig>(config: C) => C;
15
15
  //#endregion
16
16
  //#region src/config/load.d.ts
17
+ /**
18
+ * The `tasks` value in each source. A dedicated `astromech.config.*`
19
+ * default-exports the full {@link TasksConfig} object; the `tasks` key of
20
+ * `holocron.config.*` is the bare item array (it mirrors the old
21
+ * `workflows` key). Either form is accepted from either source.
22
+ */
23
+ type TasksInput = TasksConfig | TaskConfigItem[];
17
24
  /**
18
25
  * Resolve the task manifest for a repo: the `tasks` key of
19
26
  * `holocron.config.*`, then a dedicated `astromech.config.*` merged on
@@ -21,5 +28,13 @@ declare const defineConfig: <C extends TasksConfig>(config: C) => C;
21
28
  * neither source is present.
22
29
  */
23
30
  declare function loadTasksConfig(cwd: string): Promise<TasksConfig>;
31
+ /**
32
+ * The merge step of {@link loadTasksConfig}, for callers that load the two
33
+ * sources some other way (Sentinel reads them from the GitHub API, not
34
+ * from disk, holocron#916): `holocron.config.*`'s `tasks` value first,
35
+ * then a dedicated `astromech.config.*`'s default export merged on top.
36
+ * Either may be `undefined` (source absent); returns `{}` when both are.
37
+ */
38
+ declare function mergeTasksLayers(parentTasks: TasksInput | undefined, dedicated: TasksInput | undefined): TasksConfig;
24
39
  //#endregion
25
- export { type TaskConfigItem, type TaskEntry, type TasksConfig, defineConfig, loadTasksConfig, normalizeTaskEntry };
40
+ export { type TaskConfigItem, type TaskEntry, type TasksConfig, defineConfig, loadTasksConfig, mergeTasksLayers, normalizeTaskEntry };
@@ -1,5 +1,5 @@
1
- import { t as normalizeTaskEntry } from "../schema-Cf5dfaDO.mjs";
2
- import { createDefineConfig, loadConfigFile, mergeConfig } from "@theholocron/datapad";
1
+ import { n as mergeTasksLayers, r as normalizeTaskEntry, t as loadTasksConfig } from "../load-XHHfD9TX.mjs";
2
+ import { createDefineConfig } from "@theholocron/datapad";
3
3
  //#region src/config/define.ts
4
4
  /**
5
5
  * Typed identity helper for `astromech.config.ts`:
@@ -14,26 +14,4 @@ import { createDefineConfig, loadConfigFile, mergeConfig } from "@theholocron/da
14
14
  */
15
15
  const defineConfig = createDefineConfig();
16
16
  //#endregion
17
- //#region src/config/load.ts
18
- /**
19
- * Resolve the task manifest for a repo: the `tasks` key of
20
- * `holocron.config.*`, then a dedicated `astromech.config.*` merged on
21
- * top (dedicated wins; item arrays concatenate). Returns `{}` when
22
- * neither source is present.
23
- */
24
- async function loadTasksConfig(cwd) {
25
- const dedicated = await loadConfigFile({
26
- cwd,
27
- name: "astromech"
28
- });
29
- return [coerce((await loadConfigFile({
30
- cwd,
31
- name: "holocron"
32
- }))?.config.tasks), coerce(dedicated?.config)].filter((layer) => layer !== void 0).reduce((acc, layer) => mergeConfig(acc, layer), {});
33
- }
34
- function coerce(value) {
35
- if (value == null) return void 0;
36
- return Array.isArray(value) ? { tasks: value } : value;
37
- }
38
- //#endregion
39
- export { defineConfig, loadTasksConfig, normalizeTaskEntry };
17
+ export { defineConfig, loadTasksConfig, mergeTasksLayers, normalizeTaskEntry };
package/dist/index.d.mts CHANGED
@@ -226,6 +226,10 @@ interface EnsureRootWorkspaceMemberResult {
226
226
  content: string;
227
227
  changed: boolean;
228
228
  }
229
+ interface EnsureTurboDependencyResult {
230
+ content: string;
231
+ changed: boolean;
232
+ }
229
233
  //#endregion
230
234
  //#region src/astromech.d.ts
231
235
  interface AstromechOptions {
@@ -328,10 +332,24 @@ interface Astromech {
328
332
  * (not just an orchestrator) silently vanishes from `turbo run <task>`.
329
333
  * Pass `pnpm-workspace.yaml`'s current content (or `""` if none exists —
330
334
  * a no-op either way, since no `packages:` key means single-package mode
331
- * already) and root `package.json`'s own `scripts` keys. `changed: false`
332
- * means don't write anything — the file is already correct.
335
+ * already) and root `package.json`'s own `scripts` object — values and
336
+ * all, not just keys, so a task-name-matching script that's already the
337
+ * generated `holocron run <task> --` wrapper (predating this call, not
338
+ * written by it) is correctly excluded rather than treated as proof root
339
+ * is directly buildable (#846). `changed: false` means don't write
340
+ * anything — the file is already correct.
341
+ */
342
+ ensureRootWorkspaceMember(workspaceYaml: string, rootScripts: Readonly<Record<string, string>>): EnsureRootWorkspaceMemberResult;
343
+ /**
344
+ * `turboConfig()` writes a `turbo.json` but never touches `package.json`
345
+ * — a repo with no local `turbo` devDependency falls back to whatever
346
+ * (if anything) is globally on `PATH`, per `run.ts`'s `resolveBin()`.
347
+ * Pass root `package.json`'s raw content and `pnpm-workspace.yaml`'s
348
+ * current content (checked for an existing `turbo` catalog entry, which
349
+ * takes precedence over the hardcoded fallback version). `changed: false`
350
+ * means don't write anything — `devDependencies.turbo` already exists.
333
351
  */
334
- ensureRootWorkspaceMember(workspaceYaml: string, rootScripts: readonly string[]): EnsureRootWorkspaceMemberResult;
352
+ ensureTurboDependency(packageJson: string, workspaceYaml: string): EnsureTurboDependencyResult;
335
353
  }
336
354
  declare function createAstromech(options: AstromechOptions): Astromech;
337
355
  //#endregion
@@ -380,6 +398,53 @@ declare function createCodecovConfig(packages: WorkspacePackage[]): string;
380
398
  */
381
399
  declare function codecovConfig(repoRoot: string, existing: string | null): string;
382
400
  //#endregion
401
+ //#region src/deploy-on-release.d.ts
402
+ interface SpawnResult {
403
+ status: number | null;
404
+ stdout: string;
405
+ stderr: string;
406
+ }
407
+ type Run = (command: string, args: string[], options: Record<string, unknown>) => SpawnResult;
408
+ interface Tree {
409
+ readJson: (path: string) => unknown;
410
+ listDirs: (path: string) => string[];
411
+ }
412
+ interface DeployOnReleaseOptions {
413
+ cwd: string;
414
+ /** The release's channel: its prerelease identifier (`alpha`); empty or `main` for a stable release. */
415
+ channel: string;
416
+ /** The previous release's commit; omitted when there is none, so every deploying channel match deploys. */
417
+ from?: string;
418
+ /** The release commit. Default `HEAD`. */
419
+ to?: string;
420
+ /** Where progress lines go (stdout in the CLI). */
421
+ print: (line: string) => void;
422
+ dryRun?: boolean;
423
+ }
424
+ interface DeployOnReleaseDeps extends Partial<Tree> {
425
+ run?: Run;
426
+ sleep?: (ms: number) => Promise<void>;
427
+ now?: () => number;
428
+ /** Reads a package folder's task manifest; `loadTasksConfig` by default. */
429
+ loadTasks?: (dir: string) => Promise<TasksConfig>;
430
+ }
431
+ interface DeployOnReleaseResult {
432
+ pkg: string;
433
+ status: "ok" | "fail" | "skip" | "dry-run";
434
+ message: string;
435
+ }
436
+ interface DeployOnReleaseReport {
437
+ /** `fail` if any package's deploy failed; `skip` if none applied to this release; else `ok`. */
438
+ status: "ok" | "fail" | "skip";
439
+ results: DeployOnReleaseResult[];
440
+ }
441
+ /**
442
+ * Deploys every package whose manifest declares a release-time deploy that
443
+ * applies to this release. Never throws for a deploy problem: each package's
444
+ * outcome is in the report, and `status: "fail"` is the caller's exit code.
445
+ */
446
+ declare function deployOnRelease(options: DeployOnReleaseOptions, { run, readJson, listDirs, sleep, now, loadTasks }?: DeployOnReleaseDeps): Promise<DeployOnReleaseReport>;
447
+ //#endregion
383
448
  //#region src/linters.d.ts
384
449
  /**
385
450
  * The linter registry — one list of linter names (from `config.tasks`'
@@ -434,11 +499,20 @@ declare const LINTER_NAMES: ReadonlySet<string>;
434
499
  * Resolve the linter set for a repo. An `explicit` list (from `config.tasks`)
435
500
  * picks the candidate set; otherwise the candidates are every `always` linter
436
501
  * plus every `detect`-gated linter whose filenames are present at the repo
437
- * root. Either way, a `detect`-gated linter (eslint, markdownlint) only makes
438
- * the final set when its config file is actually present — a repo can't run
439
- * eslint without an `eslint.config.*`, and super-linter FATALs if you ask it
440
- * to (theholocron/holocron#654). `always` linters are unconditional. Result
441
- * is ordered by {@link LINTERS} declaration order.
502
+ * root, OR whose mapped `@theholocron/*-config` package resolves per
503
+ * {@link sharedConfigAvailable} — a repo can't run eslint with genuinely no
504
+ * config anywhere (super-linter FATALs if you ask it to,
505
+ * theholocron/holocron#654), but a *shared* one it has installed counts
506
+ * (holocron#795: proving `clients`' eslint.config.ts files reduce to zero
507
+ * remaining content is only half the story — the file itself was still load-
508
+ * bearing here as the sole "eslint is even configured" signal until this
509
+ * fallback existed). `always` linters are unconditional. Result is ordered
510
+ * by {@link LINTERS} declaration order.
511
+ *
512
+ * `sharedConfigAvailable` is caller-computed (`run.ts`'s `runLinterGroup`
513
+ * already resolves each linter's `resolveToolConfig()` per member to build
514
+ * its actual command — reusing that instead of duplicating the package-
515
+ * resolution logic here keeps this function itself pure and file-I/O-free).
442
516
  *
443
517
  * @throws when an `explicit` name is not in the registry — a typo is a
444
518
  * config bug, not a linter to silently skip.
@@ -446,6 +520,7 @@ declare const LINTER_NAMES: ReadonlySet<string>;
446
520
  declare function resolveLinters(opts: {
447
521
  explicit?: string[];
448
522
  rootFiles: string[];
523
+ sharedConfigAvailable?: ReadonlySet<string>;
449
524
  }): Array<{
450
525
  name: string;
451
526
  def: LinterDef;
@@ -577,8 +652,18 @@ declare function requiredChecks(config: TasksConfig): string[];
577
652
  * each tool's real `--config`/`--extends` loader, not just the file's shape).
578
653
  * A repo doesn't need this package installed at all — every lookup degrades to
579
654
  * `[]` (no flag added, the tool falls back to its own auto-discovery of a
580
- * local file, unchanged from today) rather than throwing, so this is additive
581
- * and safe to run unconditionally.
655
+ * local file, unchanged from today) rather than throwing.
656
+ *
657
+ * **Local file wins.** A `<tool>.config.*` sitting in `cwd` (whether it's
658
+ * pure boilerplate or has real per-package customization on top of the
659
+ * shared bundle, e.g. `mergeConfig(base, { test: { coverage: { exclude }
660
+ * } })`) always skips the splice, even when the shared package is also
661
+ * installed — an explicit `--config <shared path>` flag beats a tool's own
662
+ * auto-discovery every time, so splicing it unconditionally would silently
663
+ * discard that customization the file is still sitting right there on disk.
664
+ * The shared bundle is only ever the fallback for a package with *no* local
665
+ * file at all — that's still the "delete the file to go fully shared" path
666
+ * this was designed for (#676); it just isn't unconditional (#749).
582
667
  *
583
668
  * `semantic-release`, `devmoji`, `editorconfig-checker`, and `knip` are
584
669
  * deliberately absent — `semantic-release-config`'s `defineConfig()` needs
@@ -596,10 +681,12 @@ interface ResolverDeps {
596
681
  declare const RESOLVABLE_TOOLS: ReadonlySet<string>;
597
682
  /**
598
683
  * `<flag> <absolute path>` for `tool`, resolved against `cwd`'s
599
- * `node_modules` — or `[]` when the tool isn't mapped, the package isn't
600
- * installed, its `package.json` doesn't declare the export subpath this
601
- * needs, or the resolved file doesn't actually exist on disk (a stale
602
- * install, or a package version that predates the export existing).
684
+ * `node_modules` — or `[]` when the tool isn't mapped, `cwd` already has its
685
+ * own local config file for it (#749 — local always wins, customized or
686
+ * not), the shared package isn't installed, its `package.json` doesn't
687
+ * declare the export subpath this needs, or the resolved file doesn't
688
+ * actually exist on disk (a stale install, or a package version that
689
+ * predates the export existing).
603
690
  */
604
691
  declare function resolveToolConfig(tool: string, cwd: string, deps: ResolverDeps): string[];
605
692
  //#endregion
@@ -699,4 +786,4 @@ interface TsconfigOptions {
699
786
  */
700
787
  declare function createTsconfig(options: TsconfigOptions): string;
701
788
  //#endregion
702
- export { type Astromech, type AstromechOptions, CI_ORDER, type CiJobReport, type CiOptions, type CiReport, type ExecFn, type JobDef, KNOWN_TASKS, KNOWN_WORKFLOWS, LINTERS, LINTER_NAMES, type LinterDef, type LocalRunner, type OrgContext, type PreviewConfig, RESOLVABLE_TOOLS, REUSABLE_ACTIONS, REUSABLE_WORKFLOWS, type ResolverDeps, type RunLogger, type RunOptions, type RunTaskInput, type RunTaskReport, TASKS, type TaskDef, type TsconfigOptions, WORKFLOW_CHECK_CONTEXTS, WORKFLOW_TEMPLATES, WORKFLOW_TEMPLATE_PROPERTIES, type WorkspacePackage, codecovComponentBlock, codecovConfig, createAstromech, createCodecovConfig, createTsconfig, deriveDeployPaths, ensureIfNotFound, extractPreviewConfig, generateCombinedDeployContent, generateThinCallerContent, mergeCodecovComponents, normalizeWorkflowWith, readWorkspacePackages, requiredChecks, resolveLinters, resolveToolConfig, reusableTemplates, runCi, runTask };
789
+ export { type Astromech, type AstromechOptions, CI_ORDER, type CiJobReport, type CiOptions, type CiReport, type DeployOnReleaseOptions, type DeployOnReleaseReport, type DeployOnReleaseResult, type ExecFn, type JobDef, KNOWN_TASKS, KNOWN_WORKFLOWS, LINTERS, LINTER_NAMES, type LinterDef, type LocalRunner, type OrgContext, type PreviewConfig, RESOLVABLE_TOOLS, REUSABLE_ACTIONS, REUSABLE_WORKFLOWS, type ResolverDeps, type RunLogger, type RunOptions, type RunTaskInput, type RunTaskReport, TASKS, type TaskDef, type TsconfigOptions, WORKFLOW_CHECK_CONTEXTS, WORKFLOW_TEMPLATES, WORKFLOW_TEMPLATE_PROPERTIES, type WorkspacePackage, codecovComponentBlock, codecovConfig, createAstromech, createCodecovConfig, createTsconfig, deployOnRelease, deriveDeployPaths, ensureIfNotFound, extractPreviewConfig, generateCombinedDeployContent, generateThinCallerContent, mergeCodecovComponents, normalizeWorkflowWith, readWorkspacePackages, requiredChecks, resolveLinters, resolveToolConfig, reusableTemplates, runCi, runTask };