@visulima/vis 1.0.0-alpha.11 → 1.0.0-alpha.13

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 (116) hide show
  1. package/CHANGELOG.md +101 -0
  2. package/LICENSE.md +559 -186
  3. package/README.md +18 -0
  4. package/dist/bin.js +1 -9
  5. package/dist/config/index.d.ts +477 -556
  6. package/dist/config/index.js +1 -2
  7. package/dist/generate/index.js +1 -3
  8. package/dist/packem_chunks/applyDefaults.js +2 -336
  9. package/dist/packem_chunks/bin.js +234 -9552
  10. package/dist/packem_chunks/doctor-probe.js +2 -112
  11. package/dist/packem_chunks/fix.js +11 -234
  12. package/dist/packem_chunks/handler.js +1 -99
  13. package/dist/packem_chunks/handler10.js +2 -53
  14. package/dist/packem_chunks/handler11.js +1 -32
  15. package/dist/packem_chunks/handler12.js +5 -100
  16. package/dist/packem_chunks/handler13.js +1 -25
  17. package/dist/packem_chunks/handler14.js +18 -916
  18. package/dist/packem_chunks/handler15.js +15 -201
  19. package/dist/packem_chunks/handler16.js +1 -124
  20. package/dist/packem_chunks/handler17.js +1 -13
  21. package/dist/packem_chunks/handler18.js +1 -106
  22. package/dist/packem_chunks/handler19.js +1 -19
  23. package/dist/packem_chunks/handler2.js +2 -75
  24. package/dist/packem_chunks/handler20.js +5 -29
  25. package/dist/packem_chunks/handler21.js +1 -222
  26. package/dist/packem_chunks/handler22.js +1 -237
  27. package/dist/packem_chunks/handler23.js +5 -101
  28. package/dist/packem_chunks/handler24.js +1 -110
  29. package/dist/packem_chunks/handler25.js +3 -402
  30. package/dist/packem_chunks/handler26.js +1 -13
  31. package/dist/packem_chunks/handler27.js +1 -63
  32. package/dist/packem_chunks/handler28.js +7 -34
  33. package/dist/packem_chunks/handler29.js +21 -456
  34. package/dist/packem_chunks/handler3.js +4 -95
  35. package/dist/packem_chunks/handler30.js +3 -170
  36. package/dist/packem_chunks/handler31.js +1 -530
  37. package/dist/packem_chunks/handler32.js +2 -214
  38. package/dist/packem_chunks/handler33.js +25 -119
  39. package/dist/packem_chunks/handler34.js +2 -630
  40. package/dist/packem_chunks/handler35.js +3 -283
  41. package/dist/packem_chunks/handler36.js +22 -542
  42. package/dist/packem_chunks/handler37.js +410 -744
  43. package/dist/packem_chunks/handler38.js +22 -989
  44. package/dist/packem_chunks/handler39.js +22 -574
  45. package/dist/packem_chunks/handler4.js +2 -90
  46. package/dist/packem_chunks/handler40.js +22 -1685
  47. package/dist/packem_chunks/handler41.js +6 -1088
  48. package/dist/packem_chunks/handler42.js +5 -797
  49. package/dist/packem_chunks/handler43.js +10 -2658
  50. package/dist/packem_chunks/handler44.js +51 -3784
  51. package/dist/packem_chunks/handler45.js +25 -2574
  52. package/dist/packem_chunks/handler46.js +3 -3769
  53. package/dist/packem_chunks/handler47.js +21 -1485
  54. package/dist/packem_chunks/handler48.js +42 -0
  55. package/dist/packem_chunks/handler5.js +8 -174
  56. package/dist/packem_chunks/handler6.js +1 -95
  57. package/dist/packem_chunks/handler7.js +1 -115
  58. package/dist/packem_chunks/handler8.js +1 -12
  59. package/dist/packem_chunks/handler9.js +1 -29
  60. package/dist/packem_chunks/heal-accept.js +10 -522
  61. package/dist/packem_chunks/heal.js +14 -673
  62. package/dist/packem_chunks/index.js +7 -873
  63. package/dist/packem_chunks/loader.js +1 -23
  64. package/dist/packem_chunks/tar.js +3 -0
  65. package/dist/packem_shared/ai-analysis-hm8d2W7z.js +67 -0
  66. package/dist/packem_shared/ai-cache-DoiF80AR.js +1 -0
  67. package/dist/packem_shared/ai-fix-nn4zOE95.js +43 -0
  68. package/dist/packem_shared/cache-directory-CwHlJhgx.js +1 -0
  69. package/dist/packem_shared/dependency-scan-COr5n63B.js +2 -0
  70. package/dist/packem_shared/docker-D6OGr5_S.js +2 -0
  71. package/dist/packem_shared/failure-log-iUVLf6ts.js +2 -0
  72. package/dist/packem_shared/flakiness-D9wf0t56.js +1 -0
  73. package/dist/packem_shared/giget-CcEy_Elm.js +2 -0
  74. package/dist/packem_shared/index-DH-5hsrC.js +1 -0
  75. package/dist/packem_shared/otel-DxDUPJJH.js +6 -0
  76. package/dist/packem_shared/otelPlugin-CQq6poq8.js +1 -0
  77. package/dist/packem_shared/registry-CkubDdiY.js +2 -0
  78. package/dist/packem_shared/run-summary-utils-BfBvjzhY.js +1 -0
  79. package/dist/packem_shared/runtime-check-BXZ43CBW.js +1 -0
  80. package/dist/packem_shared/selectors-BylODRiM.js +3 -0
  81. package/dist/packem_shared/symbols-CQmER5MT.js +1 -0
  82. package/dist/packem_shared/toolchain-BgBOUHII.js +5 -0
  83. package/dist/packem_shared/typosquats-CcZl99B1.js +1 -0
  84. package/dist/packem_shared/use-measured-height-DjYgUOKk.js +1 -0
  85. package/dist/packem_shared/utils-DrNg0XTR.js +1 -0
  86. package/dist/packem_shared/verify-Baj5mFJ7.js +1 -0
  87. package/dist/packem_shared/vis-update-app-D1jl0UZZ.js +1 -0
  88. package/dist/packem_shared/xxh3-DrAUNq4n.js +1 -0
  89. package/index.js +556 -727
  90. package/package.json +19 -29
  91. package/schemas/project.schema.json +739 -297
  92. package/schemas/vis-config.schema.json +3365 -384
  93. package/templates/buildkite-ci/template.yml +20 -20
  94. package/dist/packem_shared/VisUpdateApp-D-Yz_wvg.js +0 -1316
  95. package/dist/packem_shared/_commonjsHelpers-BqLXS_qQ.js +0 -5
  96. package/dist/packem_shared/ai-analysis-CHeB1joD.js +0 -367
  97. package/dist/packem_shared/ai-cache-Be_jexe4.js +0 -142
  98. package/dist/packem_shared/ai-fix-B9iQVcD2.js +0 -379
  99. package/dist/packem_shared/cache-directory-2qvs4goY.js +0 -98
  100. package/dist/packem_shared/catalog-BJTtyi-O.js +0 -1371
  101. package/dist/packem_shared/dependency-scan-A0KSklpG.js +0 -188
  102. package/dist/packem_shared/docker-2iZzc280.js +0 -181
  103. package/dist/packem_shared/failure-log-Cz3Z4SKL.js +0 -100
  104. package/dist/packem_shared/flakiness-goTxXuCX.js +0 -180
  105. package/dist/packem_shared/otel-DCvqCTz_.js +0 -158
  106. package/dist/packem_shared/otelPlugin-DFaLDvJf.js +0 -3
  107. package/dist/packem_shared/registry-CbqXI0rc.js +0 -272
  108. package/dist/packem_shared/run-summary-utils-PVMl4aIh.js +0 -130
  109. package/dist/packem_shared/runtime-check-Cobi3p6l.js +0 -127
  110. package/dist/packem_shared/selectors-SM69TfqC.js +0 -194
  111. package/dist/packem_shared/symbols-Ta7g2nU-.js +0 -14
  112. package/dist/packem_shared/toolchain-BdZd9eBi.js +0 -975
  113. package/dist/packem_shared/typosquats-C-bCh3PX.js +0 -1210
  114. package/dist/packem_shared/use-measured-height-CNP0vT4M.js +0 -20
  115. package/dist/packem_shared/utils-CthVdBPS.js +0 -40
  116. package/dist/packem_shared/xxh3-Ck8mXNg1.js +0 -239
@@ -4,417 +4,859 @@
4
4
  "title": "vis project.json",
5
5
  "description": "Per-project configuration for @visulima/vis. Place a project.json in each workspace package root.",
6
6
  "type": "object",
7
- "additionalProperties": false,
8
7
  "properties": {
9
- "$schema": {
10
- "type": "string",
11
- "description": "JSON Schema reference for editor autocomplete."
12
- },
13
- "projectType": {
14
- "type": "string",
15
- "enum": ["library", "application"],
16
- "description": "Whether this project is a library (consumed by others) or an application (deployment target)."
17
- },
18
- "sourceRoot": {
19
- "type": "string",
20
- "description": "Source root directory, relative to this project's root. Used for display and language inference.",
21
- "default": "src"
22
- },
23
- "tags": {
8
+ "implicitDependencies": {
24
9
  "type": "array",
25
- "items": { "type": "string" },
26
- "description": "Filterable tags. Used by query language (--query \"tag=frontend\"), scoped task defaults, and tag-relationship constraints."
27
- },
28
- "layer": {
29
- "type": "string",
30
- "enum": ["configuration", "library", "scaffolding", "tool", "automation", "application"],
31
- "description": "Project layer in the dependency hierarchy. When enforceLayerRelationships is enabled, projects may only depend on the same or lower layer: configuration < library < scaffolding < tool < automation < application."
32
- },
33
- "stack": {
34
- "type": "string",
35
- "enum": ["backend", "frontend", "data", "infrastructure", "systems"],
36
- "description": "Technology stack classification. Used by query language and scoped task defaults."
10
+ "items": {
11
+ "type": "string"
12
+ },
13
+ "description": "Implicit dependencies on other projects."
37
14
  },
38
15
  "language": {
39
16
  "type": "string",
40
- "description": "Primary programming language (e.g. \"typescript\", \"rust\", \"go\"). Used by query language and scoped task defaults.",
41
- "examples": ["typescript", "javascript", "rust", "go", "python"]
17
+ "description": "Primary language informational and query-able."
42
18
  },
43
- "implicitDependencies": {
44
- "type": "array",
45
- "items": { "type": "string" },
46
- "description": "Workspace project names this project implicitly depends on (not expressed in package.json)."
19
+ "layer": {
20
+ "type": "string",
21
+ "enum": [
22
+ "application",
23
+ "automation",
24
+ "configuration",
25
+ "library",
26
+ "scaffolding",
27
+ "tool"
28
+ ],
29
+ "description": "Project layer, used for constraint inheritance and query filtering."
47
30
  },
48
31
  "owners": {
49
32
  "type": "array",
50
- "description": "Code ownership declarations. Aggregated by `vis sync codeowners` to generate a CODEOWNERS file.",
51
33
  "items": {
52
- "type": "object",
53
- "required": ["path", "owners"],
54
- "additionalProperties": false,
55
- "properties": {
56
- "path": {
57
- "type": "string",
58
- "description": "File or glob pattern relative to this project root (e.g. \"src/**\", \".\")."
59
- },
60
- "owners": {
61
- "type": "array",
62
- "items": { "type": "string" },
63
- "description": "Owner handles (e.g. \"@org/team\", \"user@example.com\")."
64
- },
65
- "channel": {
66
- "type": "string",
67
- "description": "Optional notification channel (e.g. Slack channel). Emitted as a trailing comment in CODEOWNERS."
68
- }
69
- }
70
- }
34
+ "$ref": "#/$defs/OwnersEntry"
35
+ },
36
+ "description": "Code owners for paths inside this project."
71
37
  },
72
38
  "project": {
73
39
  "type": "object",
74
- "description": "Human-readable project metadata.",
75
- "additionalProperties": false,
76
40
  "properties": {
77
- "title": {
78
- "type": "string",
79
- "description": "Display name for the project."
41
+ "channel": {
42
+ "type": "string"
80
43
  },
81
44
  "description": {
82
- "type": "string",
83
- "description": "Short description of the project's purpose."
45
+ "type": "string"
84
46
  },
85
- "channel": {
86
- "type": "string",
87
- "description": "Primary support channel (e.g. Slack, Teams)."
47
+ "maintainers": {
48
+ "type": "array",
49
+ "items": {
50
+ "type": "string"
51
+ }
88
52
  },
89
53
  "owner": {
90
- "type": "string",
91
- "description": "Primary owner or team handle."
54
+ "type": "string"
92
55
  },
93
- "maintainers": {
94
- "type": "array",
95
- "items": { "type": "string" },
96
- "description": "List of maintainer handles."
56
+ "title": {
57
+ "type": "string"
97
58
  }
98
- }
59
+ },
60
+ "additionalProperties": false,
61
+ "description": "Project-level metadata."
62
+ },
63
+ "projectType": {
64
+ "type": "string",
65
+ "enum": [
66
+ "application",
67
+ "library"
68
+ ],
69
+ "description": "Project type — library or application."
70
+ },
71
+ "sourceRoot": {
72
+ "type": "string",
73
+ "description": "Source root, used for display and language inference."
74
+ },
75
+ "stack": {
76
+ "type": "string",
77
+ "enum": [
78
+ "backend",
79
+ "data",
80
+ "frontend",
81
+ "infrastructure",
82
+ "systems"
83
+ ],
84
+ "description": "Tech stack."
85
+ },
86
+ "tags": {
87
+ "type": "array",
88
+ "items": {
89
+ "type": "string"
90
+ },
91
+ "description": "Filterable tags."
99
92
  },
100
93
  "targets": {
101
94
  "type": "object",
102
- "description": "Named build/test/run targets. Merged on top of package.json scripts — the command from scripts is used as the default if no command is set here.",
103
95
  "additionalProperties": {
104
- "$ref": "#/$defs/targetConfiguration"
105
- }
96
+ "$ref": "#/$defs/VisTargetConfiguration"
97
+ },
98
+ "description": "Vis-style target definitions (merged on top of package.json scripts)."
99
+ },
100
+ "$schema": {
101
+ "type": "string",
102
+ "description": "JSON Schema reference for editor autocomplete."
106
103
  }
107
104
  },
105
+ "additionalProperties": false,
108
106
  "$defs": {
109
- "targetConfiguration": {
107
+ "OwnersEntry": {
110
108
  "type": "object",
111
- "description": "Configuration for a single target.",
112
- "additionalProperties": false,
113
109
  "properties": {
114
- "command": {
110
+ "channel": {
115
111
  "type": "string",
116
- "description": "The shell command to execute. Overrides the package.json script of the same name."
112
+ "description": "Optional notification channel (e.g. Slack, Teams)."
117
113
  },
118
- "executor": {
114
+ "owners": {
115
+ "type": "array",
116
+ "items": {
117
+ "type": "string"
118
+ },
119
+ "description": "Owner handles (e.g. `@visulima/core-team`)."
120
+ },
121
+ "path": {
119
122
  "type": "string",
120
- "description": "Alternative to command an executor identifier."
123
+ "description": "File/glob pattern relative to the project root."
124
+ }
125
+ },
126
+ "required": [
127
+ "owners",
128
+ "path"
129
+ ],
130
+ "additionalProperties": false,
131
+ "description": "Declared code-owner assignment for a path glob within a project. Mirrors moon's `owners` shape so migrations can round-trip cleanly."
132
+ },
133
+ "VisTargetConfiguration": {
134
+ "type": "object",
135
+ "properties": {
136
+ "always": {
137
+ "type": "boolean",
138
+ "description": "When `true`, this target runs after the main task graph completes — even if upstream tasks failed. Useful for cleanup, teardown, notifications, or anything that should fire regardless of build outcome. Always-tasks are not part of the normal dependency graph and never block other tasks."
121
139
  },
122
140
  "cache": {
123
141
  "type": "boolean",
124
- "description": "Whether this target's results are cacheable. Defaults based on type: build=true, test=true, run=false."
142
+ "description": "Whether this target is cacheable"
125
143
  },
126
- "inputs": {
144
+ "cacheOnWarning": {
145
+ "type": "boolean",
146
+ "description": "When `false`, exit-0 runs whose terminal output matched any {@link TargetConfiguration.warningPattern } are not seeded into the cache. Defaults to `true` — warnings are surfaced on the result (`hadWarnings: true`) but caching still happens, matching the \"succeeded with warnings is incremental\" behaviour rush, lage and wireit users have repeatedly asked for."
147
+ },
148
+ "cacheRestore": {
149
+ "type": "object",
150
+ "properties": {
151
+ "preserveMtime": {
152
+ "type": "boolean",
153
+ "description": "Restore each file's modification time from the captured tar header (second precision). When `false`, restored files take the current wall-clock time, matching the legacy behaviour."
154
+ },
155
+ "preservePerms": {
156
+ "type": "boolean",
157
+ "description": "Restore each file's POSIX mode bits (rwx triplets, low 12 bits). When `false`, restored files take the process umask. Only meaningful on POSIX hosts; Windows ignores tar mode."
158
+ }
159
+ },
160
+ "additionalProperties": false,
161
+ "description": "Fine-grained controls over how cached outputs are rehydrated. See {@link CacheRestoreOptions } . Both fields default to `true` (faithful restore); flip individually when downstream tooling needs the legacy \"now\"-stamped behaviour."
162
+ },
163
+ "command": {
164
+ "type": "string",
165
+ "description": "The command to run (alternative to executor)"
166
+ },
167
+ "configurations": {
168
+ "type": "object",
169
+ "additionalProperties": {
170
+ "type": "object",
171
+ "additionalProperties": {}
172
+ },
173
+ "description": "Named configurations (e.g., \"production\", \"development\")"
174
+ },
175
+ "dependsOn": {
127
176
  "type": "array",
128
- "description": "Input patterns for cache invalidation. Strings may be bare globs (\"src/**/*.ts\"), @filegroup:<name> references, named-input refs, or URI form: file://path, glob://pattern, env://NAME, func://command, dep://pkg-a,pkg-b. Object form is also accepted for explicit fileset/env/runtime/externalDependencies.",
129
177
  "items": {
130
- "oneOf": [
131
- { "$ref": "#/$defs/inputString" },
132
- { "$ref": "#/$defs/fileSetInput" },
133
- { "$ref": "#/$defs/environmentInput" },
134
- { "$ref": "#/$defs/runtimeInput" },
135
- { "$ref": "#/$defs/externalDependencyInput" }
178
+ "anyOf": [
179
+ {
180
+ "type": "string"
181
+ },
182
+ {
183
+ "type": "object",
184
+ "properties": {
185
+ "dependencies": {
186
+ "type": "boolean",
187
+ "description": "Whether this is a dependency on the same project"
188
+ },
189
+ "params": {
190
+ "type": "string",
191
+ "enum": [
192
+ "forward",
193
+ "ignore"
194
+ ],
195
+ "description": "Params to pass through"
196
+ },
197
+ "projects": {
198
+ "anyOf": [
199
+ {
200
+ "type": "string"
201
+ },
202
+ {
203
+ "type": "array",
204
+ "items": {
205
+ "type": "string"
206
+ }
207
+ }
208
+ ],
209
+ "description": "The project name (if different from the current project)"
210
+ },
211
+ "target": {
212
+ "type": "string",
213
+ "description": "The target name"
214
+ }
215
+ },
216
+ "required": [
217
+ "target"
218
+ ],
219
+ "additionalProperties": false,
220
+ "description": "Defines a dependency on another target."
221
+ }
136
222
  ]
137
- }
223
+ },
224
+ "description": "Other targets this target depends on"
138
225
  },
139
- "outputs": {
140
- "type": "array",
141
- "items": { "type": "string" },
142
- "description": "Output file/directory patterns produced by this target (e.g. \"dist/**\")."
226
+ "executor": {
227
+ "type": "string",
228
+ "description": "The executor/command to run"
143
229
  },
144
- "dependsOn": {
230
+ "inputs": {
145
231
  "type": "array",
146
- "description": "Other targets this target depends on. Can be plain target names (same project) or dependency config objects.",
147
232
  "items": {
148
- "oneOf": [{ "type": "string" }, { "$ref": "#/$defs/targetDependencyConfig" }]
149
- }
233
+ "anyOf": [
234
+ {
235
+ "type": "string"
236
+ },
237
+ {
238
+ "anyOf": [
239
+ {
240
+ "type": "object",
241
+ "properties": {
242
+ "fileset": {
243
+ "anyOf": [
244
+ {
245
+ "type": "object",
246
+ "properties": {
247
+ "base": {
248
+ "type": "string",
249
+ "enum": [
250
+ "package",
251
+ "workspace"
252
+ ],
253
+ "description": "Anchor for the pattern."
254
+ },
255
+ "pattern": {
256
+ "type": "string",
257
+ "description": "Glob pattern (may start with `!` for negation)."
258
+ }
259
+ },
260
+ "required": [
261
+ "base",
262
+ "pattern"
263
+ ],
264
+ "additionalProperties": false,
265
+ "description": "Object form of a fileset pattern, for anchoring to the workspace root."
266
+ },
267
+ {
268
+ "type": "string"
269
+ }
270
+ ]
271
+ }
272
+ },
273
+ "required": [
274
+ "fileset"
275
+ ],
276
+ "additionalProperties": false,
277
+ "description": "An input based on file patterns.\n\n`fileset` may be a bare glob string (package-root relative) or an object form `{ pattern, base }` to anchor explicitly to the workspace root. Negation (`!` prefix) works in both forms."
278
+ },
279
+ {
280
+ "type": "object",
281
+ "properties": {
282
+ "runtime": {
283
+ "type": "string"
284
+ }
285
+ },
286
+ "required": [
287
+ "runtime"
288
+ ],
289
+ "additionalProperties": false,
290
+ "description": "An input based on a runtime command."
291
+ },
292
+ {
293
+ "type": "object",
294
+ "properties": {
295
+ "env": {
296
+ "type": "string"
297
+ }
298
+ },
299
+ "required": [
300
+ "env"
301
+ ],
302
+ "additionalProperties": false,
303
+ "description": "An input based on environment variables."
304
+ },
305
+ {
306
+ "type": "object",
307
+ "properties": {
308
+ "externalDependencies": {
309
+ "type": "array",
310
+ "items": {
311
+ "type": "string"
312
+ }
313
+ }
314
+ },
315
+ "required": [
316
+ "externalDependencies"
317
+ ],
318
+ "additionalProperties": false,
319
+ "description": "An input based on external dependency versions."
320
+ }
321
+ ],
322
+ "description": "Defines an input for cache invalidation."
323
+ }
324
+ ]
325
+ },
326
+ "description": "Input patterns for cache invalidation"
150
327
  },
151
- "configurations": {
152
- "type": "object",
153
- "description": "Named configuration variants (e.g. \"production\", \"development\").",
154
- "additionalProperties": {
155
- "type": "object",
156
- "additionalProperties": true
157
- }
328
+ "outputs": {
329
+ "type": "array",
330
+ "items": {
331
+ "type": "string"
332
+ },
333
+ "description": "Output patterns produced by this target"
158
334
  },
159
335
  "parallelism": {
160
336
  "type": "boolean",
161
- "description": "Whether this target supports parallel execution.",
162
- "default": true
337
+ "description": "Whether this target supports parallel execution"
338
+ },
339
+ "warningPattern": {
340
+ "anyOf": [
341
+ {
342
+ "type": "string"
343
+ },
344
+ {
345
+ "type": "array",
346
+ "items": {
347
+ "type": "string"
348
+ }
349
+ }
350
+ ],
351
+ "description": "Regex source string(s) that mark a successful task as having emitted warnings. The orchestrator scans the task's combined terminal output after a 0-exit and, on first match, sets `hadWarnings` on the result. Combine with `cacheOnWarning: false` to skip caching for warning-tainted runs. Both bare strings and arrays are accepted; arrays are tested in order.\n\n ```ts warningPattern: [\"\\\\bwarning\\\\b\", \"TS\\\\d{4}\"] ```"
163
352
  },
164
- "type": {
165
- "type": "string",
166
- "enum": ["build", "test", "run"],
167
- "description": "Semantic task type. Affects caching defaults and CI filtering. build/test are cached by default; run is not.",
168
- "default": "test"
353
+ "when": {
354
+ "type": "object",
355
+ "properties": {
356
+ "branch": {
357
+ "anyOf": [
358
+ {
359
+ "type": "string"
360
+ },
361
+ {
362
+ "type": "array",
363
+ "items": {
364
+ "type": "string"
365
+ }
366
+ }
367
+ ],
368
+ "description": "Match against the current git branch (HEAD)."
369
+ },
370
+ "ci": {
371
+ "type": "boolean",
372
+ "description": "Run only when invoked inside CI when `true`, only outside CI when `false`. Detects CI via the `CI` env var (the convention GitHub Actions, GitLab, CircleCI, Jenkins, etc. all share)."
373
+ },
374
+ "env": {
375
+ "anyOf": [
376
+ {
377
+ "anyOf": [
378
+ {
379
+ "type": "string"
380
+ },
381
+ {
382
+ "type": "object",
383
+ "properties": {
384
+ "equals": {
385
+ "type": "string",
386
+ "description": "Match this exact value. Mutually exclusive with `exists`."
387
+ },
388
+ "exists": {
389
+ "type": "boolean",
390
+ "description": "Assert the variable is set & non-empty (`true`) or unset/empty (`false`)."
391
+ },
392
+ "name": {
393
+ "type": "string",
394
+ "description": "Variable name."
395
+ }
396
+ },
397
+ "required": [
398
+ "name"
399
+ ],
400
+ "additionalProperties": false
401
+ }
402
+ ],
403
+ "description": "An environment-variable match. The string form is shorthand for `{ name, exists: true }` (set + non-empty). The object form supports either presence assertions or exact-value matching."
404
+ },
405
+ {
406
+ "type": "array",
407
+ "items": {
408
+ "anyOf": [
409
+ {
410
+ "type": "string"
411
+ },
412
+ {
413
+ "type": "object",
414
+ "properties": {
415
+ "equals": {
416
+ "type": "string",
417
+ "description": "Match this exact value. Mutually exclusive with `exists`."
418
+ },
419
+ "exists": {
420
+ "type": "boolean",
421
+ "description": "Assert the variable is set & non-empty (`true`) or unset/empty (`false`)."
422
+ },
423
+ "name": {
424
+ "type": "string",
425
+ "description": "Variable name."
426
+ }
427
+ },
428
+ "required": [
429
+ "name"
430
+ ],
431
+ "additionalProperties": false
432
+ }
433
+ ],
434
+ "description": "An environment-variable match. The string form is shorthand for `{ name, exists: true }` (set + non-empty). The object form supports either presence assertions or exact-value matching."
435
+ }
436
+ }
437
+ ],
438
+ "description": "Match against environment variables. A bare string asserts the variable is set and non-empty; the object form lets you match an exact value or just assert presence/absence."
439
+ },
440
+ "not": {
441
+ "type": "object",
442
+ "properties": {
443
+ "branch": {
444
+ "anyOf": [
445
+ {
446
+ "type": "string"
447
+ },
448
+ {
449
+ "type": "array",
450
+ "items": {
451
+ "type": "string"
452
+ }
453
+ }
454
+ ]
455
+ },
456
+ "ci": {
457
+ "type": "boolean"
458
+ },
459
+ "env": {
460
+ "anyOf": [
461
+ {
462
+ "anyOf": [
463
+ {
464
+ "type": "string"
465
+ },
466
+ {
467
+ "type": "object",
468
+ "properties": {
469
+ "equals": {
470
+ "type": "string",
471
+ "description": "Match this exact value. Mutually exclusive with `exists`."
472
+ },
473
+ "exists": {
474
+ "type": "boolean",
475
+ "description": "Assert the variable is set & non-empty (`true`) or unset/empty (`false`)."
476
+ },
477
+ "name": {
478
+ "type": "string",
479
+ "description": "Variable name."
480
+ }
481
+ },
482
+ "required": [
483
+ "name"
484
+ ],
485
+ "additionalProperties": false
486
+ }
487
+ ],
488
+ "description": "An environment-variable match. The string form is shorthand for `{ name, exists: true }` (set + non-empty). The object form supports either presence assertions or exact-value matching."
489
+ },
490
+ {
491
+ "type": "array",
492
+ "items": {
493
+ "anyOf": [
494
+ {
495
+ "type": "string"
496
+ },
497
+ {
498
+ "type": "object",
499
+ "properties": {
500
+ "equals": {
501
+ "type": "string",
502
+ "description": "Match this exact value. Mutually exclusive with `exists`."
503
+ },
504
+ "exists": {
505
+ "type": "boolean",
506
+ "description": "Assert the variable is set & non-empty (`true`) or unset/empty (`false`)."
507
+ },
508
+ "name": {
509
+ "type": "string",
510
+ "description": "Variable name."
511
+ }
512
+ },
513
+ "required": [
514
+ "name"
515
+ ],
516
+ "additionalProperties": false
517
+ }
518
+ ],
519
+ "description": "An environment-variable match. The string form is shorthand for `{ name, exists: true }` (set + non-empty). The object form supports either presence assertions or exact-value matching."
520
+ }
521
+ }
522
+ ]
523
+ },
524
+ "os": {
525
+ "anyOf": [
526
+ {
527
+ "type": "string",
528
+ "enum": [
529
+ "aix",
530
+ "darwin",
531
+ "freebsd",
532
+ "linux",
533
+ "openbsd",
534
+ "sunos",
535
+ "windows",
536
+ "win32"
537
+ ],
538
+ "description": "Aliased platform list — `\"windows\"` is sugar for Node's `\"win32\"`."
539
+ },
540
+ {
541
+ "type": "array",
542
+ "items": {
543
+ "type": "string",
544
+ "enum": [
545
+ "aix",
546
+ "darwin",
547
+ "freebsd",
548
+ "linux",
549
+ "openbsd",
550
+ "sunos",
551
+ "windows",
552
+ "win32"
553
+ ],
554
+ "description": "Aliased platform list — `\"windows\"` is sugar for Node's `\"win32\"`."
555
+ }
556
+ }
557
+ ]
558
+ }
559
+ },
560
+ "additionalProperties": false,
561
+ "description": "Negative mirrors. A task runs only when *all* `not.*` clauses fail."
562
+ },
563
+ "os": {
564
+ "anyOf": [
565
+ {
566
+ "type": "string",
567
+ "enum": [
568
+ "aix",
569
+ "darwin",
570
+ "freebsd",
571
+ "linux",
572
+ "openbsd",
573
+ "sunos",
574
+ "windows",
575
+ "win32"
576
+ ],
577
+ "description": "Aliased platform list — `\"windows\"` is sugar for Node's `\"win32\"`."
578
+ },
579
+ {
580
+ "type": "array",
581
+ "items": {
582
+ "type": "string",
583
+ "enum": [
584
+ "aix",
585
+ "darwin",
586
+ "freebsd",
587
+ "linux",
588
+ "openbsd",
589
+ "sunos",
590
+ "windows",
591
+ "win32"
592
+ ],
593
+ "description": "Aliased platform list — `\"windows\"` is sugar for Node's `\"win32\"`."
594
+ }
595
+ }
596
+ ],
597
+ "description": "Match `process.platform` (`\"linux\" | \"darwin\" | \"win32\" | \"freebsd\" | \"openbsd\" | \"sunos\" | \"aix\"`). Pass `\"windows\"` as an alias for `\"win32\"` — easier to remember and matches what people type."
598
+ }
599
+ },
600
+ "additionalProperties": false,
601
+ "description": "Predicate that gates execution. When the condition evaluates to `false` for the current environment, the task is skipped (marked `\"skipped\"`, not failed). Combine with `os`, `env`, `branch`, and `ci` clauses for granular control:\n\n ```ts when: { os: \"linux\", ci: true, env: \"DEPLOY_TOKEN\" } ```"
169
602
  },
170
- "preset": {
603
+ "aliases": {
604
+ "type": "array",
605
+ "items": {
606
+ "type": "string"
607
+ },
608
+ "description": "Alternate names that resolve to this target on the CLI. Useful for shortening long canonical names (`test` ↔ `t`) or for offering migration-friendly aliases when renaming targets. Aliases must be globally unique within the workspace."
609
+ },
610
+ "description": {
171
611
  "type": "string",
172
- "enum": ["server", "utility"],
173
- "description": "Preset that pre-fills a bundle of options. 'server': persistent, no cache, no CI. 'utility': no cache, no CI."
612
+ "description": "One-line description surfaced by `vis list` and (in future) per-task `--help`. Kept short — longer docs belong in project READMEs or vis.config.ts comments."
613
+ },
614
+ "inferred": {
615
+ "type": "boolean",
616
+ "description": "True when the target was synthesized by a Project Crystal-style detector (see {@link ../inference } ) rather than declared by a package.json script, project.json, or vis.task.ts file. Surfaced by `vis list --inferred` and used by tooling to distinguish implicit defaults from explicit user intent."
174
617
  },
175
618
  "options": {
176
- "$ref": "#/$defs/targetOptions"
619
+ "$ref": "#/$defs/VisTargetOptions",
620
+ "description": "Vis-specific target options."
177
621
  },
178
- "when": {
179
- "$ref": "#/$defs/whenCondition",
180
- "description": "Predicate gating execution. Tasks whose `when` does not match the current environment are marked skipped (not failed) by the orchestrator."
622
+ "preset": {
623
+ "$ref": "#/$defs/TargetPreset",
624
+ "description": "Preset applied before user-specified options."
181
625
  },
182
- "always": {
183
- "type": "boolean",
184
- "description": "When true, this task runs after the main task graph completes — even if upstream tasks failed or the run was interrupted (except SIGINT). Used for cleanup / teardown / notifications. Always-tasks are pulled out of the dependency graph and never cached.",
185
- "default": false
626
+ "type": {
627
+ "$ref": "#/$defs/TargetType",
628
+ "description": "Semantic task type. Affects caching defaults and CI filtering.",
629
+ "default": "test"
186
630
  }
187
- }
631
+ },
632
+ "additionalProperties": false,
633
+ "description": "An extended target configuration that adds the vis-specific options on top of task-runner's `TargetConfiguration`."
188
634
  },
189
- "targetOptions": {
635
+ "VisTargetOptions": {
190
636
  "type": "object",
191
- "description": "Vis-specific target options controlling execution behavior.",
192
- "additionalProperties": false,
193
637
  "properties": {
194
- "persistent": {
195
- "type": "boolean",
196
- "description": "Long-running/never-ending process. Scheduled last, never cached.",
638
+ "affectedFiles": {
639
+ "$ref": "#/$defs/AffectedFilesMode",
640
+ "description": "How to forward affected files to the task process. Only used when invoked via `vis affected &lt;target>`.",
197
641
  "default": false
198
642
  },
643
+ "envFile": {
644
+ "anyOf": [
645
+ {
646
+ "type": "boolean"
647
+ },
648
+ {
649
+ "type": "string"
650
+ },
651
+ {
652
+ "type": "array",
653
+ "items": {
654
+ "type": "string"
655
+ }
656
+ }
657
+ ],
658
+ "description": "Load environment variables from dotenv file(s) before running.\n- `string`: a single file path (relative to project root).\n- `string[]`: multiple files — later entries override earlier ones, so put more-specific files last (e.g. `[\".env\", \".env.local\"]`).\n- `true`: auto-cascade in the Next/Vite order: `.env` → `.env.{NODE_ENV}` → `.env.local` → `.env.{NODE_ENV}.local`. Skips `.env.local` when NODE_ENV is `test`, matching Next.js."
659
+ },
199
660
  "interactive": {
200
661
  "type": "boolean",
201
- "description": "Serialized with respect to parallel execution; claims stdin for terminal input.",
662
+ "description": "When true, the task is serialized with respect to parallel execution and must be run on the main process (claims stdin). Used for commands that read from the terminal.",
202
663
  "default": false
203
664
  },
204
665
  "internal": {
205
666
  "type": "boolean",
206
- "description": "Hidden from CLI listings. Can only be invoked as a dependency of another task.",
667
+ "description": "When true, the task is hidden from CLI listings and can only be invoked as a dependency of another task.",
207
668
  "default": false
208
669
  },
209
- "runInCI": {
210
- "oneOf": [{ "type": "boolean" }, { "type": "string", "enum": ["affected", "always"] }],
211
- "description": "Controls whether this task runs in CI. true=always, false=never, 'affected'=only when project is affected, 'always'=even if unaffected.",
212
- "default": true
670
+ "killGracePeriodMs": {
671
+ "type": "number",
672
+ "description": "Milliseconds the timeout watchdog waits between sending SIGTERM and SIGKILL when the `timeout` budget fires. Tasks that ignore SIGTERM (e.g. test runners holding open child processes) get force-killed after this grace window so a stuck task can't outlive its budget.\n\nSet to `0` to skip escalation and rely on SIGTERM only.",
673
+ "default": 5000
213
674
  },
214
- "retryCount": {
215
- "type": "integer",
216
- "minimum": 0,
217
- "description": "Number of times to retry on failure.",
218
- "default": 0
675
+ "mutex": {
676
+ "type": "string",
677
+ "description": "Serializes all tasks that share the same mutex name. Useful for tasks that contend on a shared resource (e.g., a database migration)."
219
678
  },
220
- "retryDelay": {
221
- "oneOf": [
222
- { "type": "integer", "minimum": 0 },
223
- { "type": "string", "const": "exponential" }
679
+ "outputStyle": {
680
+ "type": "string",
681
+ "enum": [
682
+ "normal",
683
+ "quiet"
224
684
  ],
225
- "description": "Delay between retries in ms, or \"exponential\" for 2^attempt * 1000ms.",
226
- "default": "exponential"
685
+ "description": "Per-target output verbosity. Overrides the global `--output-style` flag for this specific target.\n\n- `\"normal\"` (default): print every task's terminal output\n- `\"quiet\"`: only print output when the task fails. Successful and cached tasks contribute their status line and timing, but their captured stdout/stderr is suppressed.\n\nUseful when a routinely-noisy task (a linter or test runner with verbose progress output) should stay quiet during green builds but reveal everything when it fails."
227
686
  },
228
- "affectedFiles": {
229
- "oneOf": [
230
- { "type": "boolean", "const": false },
231
- { "type": "string", "enum": ["args", "env", "both"] }
232
- ],
233
- "description": "How to forward affected files to the task. false=don't, 'args'=as command arguments, 'env'=via VIS_AFFECTED_FILES, 'both'=both.",
687
+ "persistent": {
688
+ "type": "boolean",
689
+ "description": "When true, the task is a long-running / never-ending process. Persistent tasks are scheduled last, execute after all cacheable tasks complete, and are never cached.",
234
690
  "default": false
235
691
  },
236
- "envFile": {
237
- "type": "string",
238
- "description": "Dotenv file to load before running. Path relative to the project root.",
239
- "examples": [".env", ".env.local", ".env.production"]
692
+ "preset": {
693
+ "$ref": "#/$defs/TargetPreset",
694
+ "description": "A preset that pre-fills a common bundle of options. User-provided fields always take precedence over the preset."
240
695
  },
241
- "mutex": {
242
- "type": "string",
243
- "description": "Named mutex. All tasks sharing the same mutex name are serialized.",
244
- "examples": ["db-migration"]
696
+ "pty": {
697
+ "type": "boolean",
698
+ "description": "Run the task through a pseudo-terminal so color-aware tools (vitest, eslint, biome, …) render as if attached to a real TTY instead of a pipe. Output is captured via task-runner's `TerminalBuffer` so ANSI escapes are normalized into the final rendered state before reaching the reporter.\n\nForces cache to off — PTY output can include timing-dependent frames (spinners) that aren't safe to replay from a cache.",
699
+ "default": false
700
+ },
701
+ "retryCount": {
702
+ "type": "number",
703
+ "description": "Number of times to retry the task on failure. Uses an exponential backoff by default (1s, 2s, 4s, ...).",
704
+ "default": 0
705
+ },
706
+ "retryDelay": {
707
+ "anyOf": [
708
+ {
709
+ "type": "number"
710
+ },
711
+ {
712
+ "type": "string",
713
+ "const": "exponential"
714
+ }
715
+ ],
716
+ "description": "Delay between retry attempts in milliseconds, or `\"exponential\"` for 2^attempt * 1000 ms.",
717
+ "default": "exponential"
245
718
  },
246
719
  "runFromWorkspaceRoot": {
247
720
  "type": "boolean",
248
- "description": "Execute from the workspace root instead of the project root.",
721
+ "description": "When true, the command executes with the workspace root as CWD instead of the project root.",
249
722
  "default": false
250
723
  },
251
- "shell": {
252
- "type": "string",
253
- "description": "Per-target shell override (cross-platform).",
254
- "examples": ["/bin/bash", "/bin/zsh"]
724
+ "runInCI": {
725
+ "$ref": "#/$defs/RunInCI",
726
+ "description": "Controls whether the task runs in CI environments.",
727
+ "default": true
255
728
  },
256
- "unixShell": {
257
- "type": "string",
258
- "description": "Shell override for Linux and macOS. Takes precedence over 'shell'."
729
+ "runnerTags": {
730
+ "type": "array",
731
+ "items": {
732
+ "type": "string"
733
+ },
734
+ "description": "Capability tags that gate this task to runners advertising the same tag. The CLI's `--runner-tags=gpu,slow` flag (or `VIS_RUNNER_TAGS` env var) tells vis what the current runner supports; tasks whose `runnerTags` share at least one tag with the runner set are eligible. Untagged tasks (no `runnerTags` or an empty array) are general-purpose and always run.\n\nUse this for special-purpose CI lanes — e.g. a GPU runner that should only pick up visual-regression suites, or a nightly job that runs `slow` integration tests. When neither flag nor env is set, the filter is inactive and every task runs."
259
735
  },
260
- "windowsShell": {
261
- "type": "string",
262
- "description": "Shell override for Windows. Takes precedence over 'shell'.",
263
- "examples": ["powershell.exe", "cmd.exe"]
736
+ "service": {
737
+ "$ref": "#/$defs/ServiceConfig",
738
+ "description": "Marks this target as a long-lived service that can be started via `vis service start &lt;id>` and auto-attached when other tasks declare it in `dependsOn`. Implies persistent + non-cacheable behaviour (set `preset: \"server\"` to inherit the rest of the bundle).\n\nThe presence of this block — not `preset: \"server\"` alone — is what makes a target eligible for the cross-invocation registry. `preset: \"server\"` without `service` keeps today's in-run-only behaviour."
264
739
  },
265
- "preset": {
266
- "type": "string",
267
- "enum": ["server", "utility"],
268
- "description": "Preset applied before user options."
269
- }
270
- }
271
- },
272
- "targetDependencyConfig": {
273
- "type": "object",
274
- "description": "Structured dependency on another target.",
275
- "required": ["target"],
276
- "additionalProperties": false,
277
- "properties": {
278
- "target": {
740
+ "shell": {
279
741
  "type": "string",
280
- "description": "The target name to depend on."
742
+ "description": "Per-target shell override. When set, the command runs through this shell instead of the platform default."
281
743
  },
282
- "dependencies": {
744
+ "strictEnv": {
283
745
  "type": "boolean",
284
- "description": "When true, run this target on all dependency projects first (equivalent to turbo's ^target)."
746
+ "description": "Override the workspace `strictEnv` setting for this target. When truthy, the target fails if its command references an env var that resolves to neither the task's effective env nor `process.env`. When `false`, the target opts out of a workspace `strictEnv: true` (e.g. for a one-off command that legitimately tolerates an unset variable)."
285
747
  },
286
- "projects": {
287
- "oneOf": [{ "type": "string" }, { "type": "array", "items": { "type": "string" } }],
288
- "description": "Specific project name(s) to run this target on."
748
+ "timeout": {
749
+ "type": "number",
750
+ "description": "Maximum wall-clock milliseconds a single task run is allowed to take before being killed. `0` / `undefined` means no timeout.\n\nWhen the timeout fires the task is sent SIGTERM and, if it has not exited within `killGracePeriodMs`, SIGKILL. The task exits with a failure status carrying the `[timeout]` marker in `terminalOutput`. Retries count per-attempt, not cumulatively.\n\nUse this to prevent runaway tasks from eating CI wall-clock time up to the job-level cutoff."
289
751
  },
290
- "params": {
752
+ "unixShell": {
291
753
  "type": "string",
292
- "enum": ["forward", "ignore"],
293
- "description": "Whether to forward or ignore parameters from the parent task."
294
- }
295
- }
296
- },
297
- "inputString": {
298
- "type": "string",
299
- "description": "Input pattern. Bare globs (\"src/**/*.ts\"), @filegroup:<name>/named-input refs, or URI form: file://path | glob://pattern | env://NAME | func://command | dep://pkg-a,pkg-b. Negation (\"!\") works for fileset forms (file://, glob://, bare globs)."
300
- },
301
- "fileSetInput": {
302
- "type": "object",
303
- "required": ["fileset"],
304
- "additionalProperties": false,
305
- "properties": {
306
- "fileset": {
754
+ "description": "Per-target unix shell override, used on Linux and macOS. Takes precedence over `shell` on unix-like systems."
755
+ },
756
+ "windowsShell": {
307
757
  "type": "string",
308
- "description": "Glob pattern for input files (e.g. \"src/**/*.ts\")."
758
+ "description": "Per-target windows shell override, used on Windows. Takes precedence over `shell` on Windows."
309
759
  }
310
- }
311
- },
312
- "environmentInput": {
313
- "type": "object",
314
- "required": ["env"],
760
+ },
315
761
  "additionalProperties": false,
316
- "properties": {
317
- "env": {
318
- "type": "string",
319
- "description": "Environment variable name to include in cache key."
320
- }
321
- }
762
+ "description": "Vis-specific target options that extend the task-runner's base `TargetConfiguration`. These live under `target.options` and are interpreted by vis before handing the task off to task-runner.\n\nConditional execution (`when:`) and finally tasks (`always:`) live at the target top level, not under `options` — they're handled by the task-runner orchestrator. See `@visulima/task-runner`'s `WhenCondition`."
322
763
  },
323
- "runtimeInput": {
324
- "type": "object",
325
- "required": ["runtime"],
326
- "additionalProperties": false,
327
- "properties": {
328
- "runtime": {
329
- "type": "string",
330
- "description": "Shell command whose output is included in cache key (e.g. \"node --version\")."
331
- }
332
- }
764
+ "AffectedFilesMode": {
765
+ "type": [
766
+ "string",
767
+ "boolean"
768
+ ],
769
+ "enum": [
770
+ "args",
771
+ "both",
772
+ "env",
773
+ false
774
+ ],
775
+ "description": "Controls how affected files are forwarded to a task.\n- `false` (default): Do not forward.\n- `\"args\"`: Append affected paths as additional command arguments.\n- `\"env\"`: Expose them via `VIS_AFFECTED_FILES` environment variable.\n- `\"both\"`: Both of the above."
333
776
  },
334
- "externalDependencyInput": {
335
- "type": "object",
336
- "required": ["externalDependencies"],
337
- "additionalProperties": false,
338
- "properties": {
339
- "externalDependencies": {
340
- "type": "array",
341
- "items": { "type": "string" },
342
- "description": "npm package names whose resolved versions are included in cache key."
343
- }
344
- }
777
+ "TargetPreset": {
778
+ "type": "string",
779
+ "enum": [
780
+ "server",
781
+ "utility"
782
+ ],
783
+ "description": "Preset bundles of target options.\n- `server`: Long-running local dev server — caching off, not in CI, interactive, persistent.\n- `utility`: Short-lived helper — caching off, not in CI."
345
784
  },
346
- "envMatcher": {
347
- "oneOf": [
785
+ "RunInCI": {
786
+ "anyOf": [
348
787
  {
349
788
  "type": "string",
350
- "description": "Bare env-var name; task runs when set and non-empty."
789
+ "const": "affected"
351
790
  },
352
- {
353
- "type": "object",
354
- "required": ["name"],
355
- "additionalProperties": false,
356
- "properties": {
357
- "name": { "type": "string", "description": "Env var name." },
358
- "equals": { "type": "string", "description": "Match this exact value. Mutually exclusive with `exists`." },
359
- "exists": { "type": "boolean", "description": "true → must be set+non-empty; false → must be unset/empty." }
360
- }
361
- }
362
- ]
363
- },
364
- "envMatcherList": {
365
- "oneOf": [{ "$ref": "#/$defs/envMatcher" }, { "type": "array", "items": { "$ref": "#/$defs/envMatcher" } }]
366
- },
367
- "platformList": {
368
- "oneOf": [
369
791
  {
370
792
  "type": "string",
371
- "enum": ["aix", "darwin", "freebsd", "linux", "openbsd", "sunos", "windows", "win32"]
793
+ "const": "always"
372
794
  },
373
795
  {
374
- "type": "array",
375
- "items": {
376
- "type": "string",
377
- "enum": ["aix", "darwin", "freebsd", "linux", "openbsd", "sunos", "windows", "win32"]
378
- }
796
+ "type": "boolean"
379
797
  }
380
- ]
798
+ ],
799
+ "description": "Controls whether a target runs in CI.\n- `true` (default): Always run.\n- `false`: Never run in CI (local-only).\n- `\"affected\"`: Only when the project is affected by the current change set.\n- `\"always\"`: Always run, even if unaffected."
381
800
  },
382
- "branchList": {
383
- "oneOf": [{ "type": "string" }, { "type": "array", "items": { "type": "string" } }]
384
- },
385
- "whenCondition": {
801
+ "ServiceConfig": {
386
802
  "type": "object",
387
- "additionalProperties": false,
388
- "description": "Predicate that gates whether a task runs. All positive clauses must match (AND). Within a clause, an array means any-of (OR). `not.*` mirrors invert each positive clause.",
389
803
  "properties": {
390
- "os": {
391
- "$ref": "#/$defs/platformList",
392
- "description": "Match `process.platform`. Use \"windows\" as a friendly alias for \"win32\"."
393
- },
394
804
  "env": {
395
- "$ref": "#/$defs/envMatcherList",
396
- "description": "Match against environment variables. String form asserts set+non-empty; object form supports `equals` or `exists`."
805
+ "type": "object",
806
+ "additionalProperties": {
807
+ "type": "string"
808
+ },
809
+ "description": "Env vars to expose to dependent tasks when this service is registered. Merged into the dependent task's env after the task's own envFile and before the task's explicit `env` overrides — the dependent task wins on key collisions.\n\nNote: only this `env` map propagates to dependents. The service target's own `envFile` is loaded into the **service process** at start time but is *not* forwarded — dependents must declare any shared values they need either here or in their own envFile. This boundary is intentional: envFiles often contain operator-only secrets (deploy keys, admin tokens) that should not leak into downstream test commands."
397
810
  },
398
- "branch": {
399
- "$ref": "#/$defs/branchList",
400
- "description": "Match against the current git branch (HEAD)."
811
+ "killGracePeriodMs": {
812
+ "type": "number",
813
+ "description": "Grace period in milliseconds between SIGTERM and SIGKILL when the service is stopped.",
814
+ "default": 5000
401
815
  },
402
- "ci": {
403
- "type": "boolean",
404
- "description": "true run only inside CI; false run only outside CI. CI is detected via the CI env var."
816
+ "port": {
817
+ "type": "number",
818
+ "description": "Optional port the service listens on. Used as the default for `readiness.tcp.port` when no explicit probe is configured, and surfaced by `vis service list`."
405
819
  },
406
- "not": {
820
+ "readiness": {
407
821
  "type": "object",
408
- "additionalProperties": false,
409
- "description": "Negative mirrors. Task runs only when *all* `not.*` clauses fail.",
410
822
  "properties": {
411
- "os": { "$ref": "#/$defs/platformList" },
412
- "env": { "$ref": "#/$defs/envMatcherList" },
413
- "branch": { "$ref": "#/$defs/branchList" },
414
- "ci": { "type": "boolean" }
415
- }
823
+ "tcp": {
824
+ "type": "object",
825
+ "properties": {
826
+ "host": {
827
+ "type": "string"
828
+ },
829
+ "port": {
830
+ "type": "number"
831
+ },
832
+ "timeoutMs": {
833
+ "type": "number"
834
+ }
835
+ },
836
+ "required": [
837
+ "port"
838
+ ],
839
+ "additionalProperties": false
840
+ }
841
+ },
842
+ "required": [
843
+ "tcp"
844
+ ],
845
+ "additionalProperties": false,
846
+ "description": "Readiness probe configuration. v1 supports TCP only."
416
847
  }
417
- }
848
+ },
849
+ "additionalProperties": false,
850
+ "description": "Configuration block declared on a target to mark it as a long-lived \"service\" — eligible to be started/stopped via `vis service` and auto-attached when other tasks depend on it.\n\nTargets must also carry `preset: \"server\"` (or the equivalent `persistent: true`) for the service-mode lifecycle to apply."
851
+ },
852
+ "TargetType": {
853
+ "type": "string",
854
+ "enum": [
855
+ "build",
856
+ "run",
857
+ "test"
858
+ ],
859
+ "description": "Semantic classification for a target.\n- `build`: Generates one or more artifacts; cached by default.\n- `test`: Validation task (lint, typecheck, unit test). Default type.\n- `run`: One-off or long-running process. Not cached by default."
418
860
  }
419
861
  }
420
862
  }