@uniflowed/config 0.0.0-alpha.15 → 0.0.0-alpha.17

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/index.js CHANGED
@@ -5,8 +5,13 @@
5
5
  export type {
6
6
  CapabilityJsHost,
7
7
  CoverageThresholds,
8
+ DeployAdapter,
9
+ PackageManagerPreference,
8
10
  Permissions,
11
+ PluginEntry,
9
12
  RuleLevel,
13
+ RuntimeEngine,
14
+ SizeBudget,
10
15
  TaskDefinition,
11
16
  UniflowedConfig,
12
17
  } from "./internal/schema.js";
@@ -2,6 +2,30 @@
2
2
  //
3
3
  // Owns the Flow shape of `uf.config.js`; `index.js` keeps the public package
4
4
  // entry point thin.
5
+ //
6
+ // # Every key uf reads, and nothing else
7
+ //
8
+ // The guide says a config file "is type-checked as Flow code", which is the
9
+ // whole reason to write one in Flow. That makes an undeclared key worse than a
10
+ // missing feature: `lint.files`, `lint.ignore` and `app.router.enabled` were
11
+ // documented in the configuration reference, accepted by the runtime, and
12
+ // absent from this type — so a project following the reference had to choose
13
+ // between the documented key and a config that checks
14
+ // (ubugeeei-prod/uf#481). They were not the only three.
15
+ //
16
+ // So the key *names* below are no longer a person's job to keep in step with
17
+ // the loader. `crates/uf_config`'s `the_flow_schema_declares_every_key_uf_reads`
18
+ // parses this file with uf's own Flow parser and compares the paths it declares
19
+ // against the paths `uf_config::UniflowedConfig` serializes — in both
20
+ // directions, because a key declared here and read by nothing is an option that
21
+ // silently does nothing, which is the same defect wearing the other face.
22
+ //
23
+ // The value *types* are still this file's own judgement, and deliberately so.
24
+ // A test over names cannot say whether `"biome" | "prettier" | "none"` is the
25
+ // right set, and several keys below are narrower than what the loader will
26
+ // parse — `orm.module` is `"@uniflowed/orm"` because there is one
27
+ // implementation, where the loader takes any string. Where this package means
28
+ // to be more opinionated than the parser, that is what these say.
5
29
 
6
30
  export type RuleLevel = "off" | "warn" | "error" | 0 | 1 | 2 | boolean;
7
31
 
@@ -25,6 +49,68 @@ export type TaskDefinition =
25
49
 
26
50
  export type CapabilityJsHost = "node" | "deno" | "bun";
27
51
 
52
+ /**
53
+ * The package manager uf drives, overriding what it infers from the project.
54
+ *
55
+ * `"auto"` reads the project itself: an explicit `"packageManager"` field, then
56
+ * a lockfile, then the nearest workspace root, then uf's own resolver.
57
+ */
58
+ // The eight below are the *names a project may pin*, not commands this module
59
+ // runs: `@uniflowed/config` declares a type and executes nothing at all.
60
+ // `uniflowed/no-npm-script-invocation` is a line scanner, and a string whose
61
+ // entire contents is `pnpm` reads exactly like the `spawn("pnpm", […])` the
62
+ // rule exists to catch — `crates/uf_lint/src/scan/search.rs` says so in its own
63
+ // documentation and calls what is left "rare and suppressible". This is that
64
+ // residue, and there is no spelling of these values that is not one of them.
65
+ // uf-lint-disable uniflowed/no-npm-script-invocation
66
+ export type PackageManagerPreference =
67
+ | "auto"
68
+ | "uf"
69
+ | "npm"
70
+ | "pnpm"
71
+ | "yarn"
72
+ | "yarn-classic"
73
+ | "yarn-berry"
74
+ | "bun";
75
+ // uf-lint-enable uniflowed/no-npm-script-invocation
76
+
77
+ export type RuntimeEngine = "uf" | "node" | "deno" | "bun" | "edge" | "serverless" | "container";
78
+
79
+ export type DeployAdapter =
80
+ | "node"
81
+ | "bun"
82
+ | "deno"
83
+ | "edge"
84
+ | "serverless"
85
+ | "static"
86
+ | "container";
87
+
88
+ /**
89
+ * One entry of `plugins: [...]`.
90
+ *
91
+ * A bare name takes the default band and applies to every pipeline; the long
92
+ * form says otherwise. Declaration order decides within a band, so the
93
+ * resolved pipeline is a function of this file alone.
94
+ */
95
+ export type PluginEntry =
96
+ | string
97
+ | {
98
+ readonly name: string,
99
+ readonly order?: "pre" | "normal" | "post",
100
+ readonly apply?: "build" | "serve" | "always",
101
+ };
102
+
103
+ /**
104
+ * A ceiling `uf build` fails over, and what it is measured on.
105
+ *
106
+ * `max` accepts a byte count or a size a person would write — `"180kb"` —
107
+ * because a budget is written by hand and read back by a report.
108
+ */
109
+ export type SizeBudget = {
110
+ readonly max: number | string,
111
+ readonly metric?: "raw" | "gzip" | "brotli",
112
+ };
113
+
28
114
  /**
29
115
  * What the project's own code may reach.
30
116
  *
@@ -70,7 +156,52 @@ export type CoverageThresholds = {
70
156
  };
71
157
 
72
158
  export type UniflowedConfig = {
159
+ /**
160
+ * The runtime accessibility audit: `expect(el).toHaveNoAxeViolations()` in a
161
+ * test, and the page `uf dev` is serving.
162
+ *
163
+ * One block for both on purpose. A rule a project has decided cannot be
164
+ * judged here — `color-contrast` against a DOM with no layout is the
165
+ * standing example — has to be the same rule in CI and in the loop somebody
166
+ * is working in, or the audit that finds a violation while the component is
167
+ * being written disagrees with the one that blocks the pull request.
168
+ *
169
+ * Inert without axe-core, which uf does not install: add it and both halves
170
+ * start working.
171
+ *
172
+ * Not the same thing as `lint.rules`' `a11y/*`, which read JSX that was
173
+ * never rendered.
174
+ */
175
+ readonly accessibility?: {
176
+ readonly devAudit?: boolean,
177
+ readonly axe?: {
178
+ // Run only rules carrying one of these axe tags; every rule when absent.
179
+ readonly tags?: $ReadOnlyArray<string>,
180
+ readonly disabledRules?: $ReadOnlyArray<string>,
181
+ readonly minImpact?: "minor" | "moderate" | "serious" | "critical",
182
+ },
183
+ },
73
184
  readonly app?: {
185
+ // Whether a component with no directive is rendered on the server or
186
+ // shipped to the browser.
187
+ readonly componentDefault?: "server" | "client",
188
+ readonly framework?: "uniflowed" | "react" | "react-native",
189
+ readonly react?: {
190
+ // React 19's Strict Mode, on by default in `uf dev`: it double-invokes
191
+ // render and effects so an impurity shows up in development rather
192
+ // than in production. Off has to be a choice a project makes.
193
+ readonly strictMode?: boolean,
194
+ readonly version?: string,
195
+ readonly asyncReact?: boolean,
196
+ readonly suspense?: boolean,
197
+ readonly useHook?: boolean,
198
+ },
199
+ // Whether the project builds React Server Components at all, and whether
200
+ // a `"use server"` export is wired to an endpoint.
201
+ readonly rsc?: boolean,
202
+ readonly serverActions?: boolean,
203
+ // The runtimes the build must satisfy.
204
+ readonly targets?: $ReadOnlyArray<"web" | "react-native" | "server" | "hermes">,
74
205
  readonly orm?: {
75
206
  readonly enabled?: boolean,
76
207
  readonly module?: "@uniflowed/orm",
@@ -79,11 +210,18 @@ export type UniflowedConfig = {
79
210
  readonly preparedByDefault?: true,
80
211
  },
81
212
  readonly builtins?: {
213
+ readonly data?: "uniflowed-query",
214
+ readonly effect?: "uniflowed-effect",
82
215
  readonly fetch?: {
83
216
  readonly module?: "@uniflowed/fetch",
84
217
  readonly overrideGlobalFetch?: false,
85
218
  },
86
219
  readonly cell?: boolean,
220
+ readonly frameworkLints?: boolean,
221
+ readonly nativeTestRunner?: boolean,
222
+ readonly reactTestingLibrary?: boolean,
223
+ readonly relay?: boolean,
224
+ readonly style?: "style-x",
87
225
  readonly reactCompiler?: {
88
226
  readonly enabled?: boolean,
89
227
  readonly implementation?: "official-rust",
@@ -106,6 +244,19 @@ export type UniflowedConfig = {
106
244
  readonly extensions?: $ReadOnlyArray<".mdx">,
107
245
  readonly jsxImportSource?: "@uniflowed/jsx-runtime",
108
246
  readonly pipelinePlugin?: "built-in",
247
+ // Colours are computed during the build and written into the HTML,
248
+ // so nothing ships to the browser to do it. Both themes are emitted
249
+ // together as CSS variables, because a build cannot know which the
250
+ // reader prefers.
251
+ readonly highlight?: {
252
+ readonly enabled?: boolean,
253
+ readonly themes?: {
254
+ readonly light?: string,
255
+ readonly dark?: string,
256
+ },
257
+ // Grammars beyond the ones a uf project uses by default.
258
+ readonly langs?: $ReadOnlyArray<string>,
259
+ },
109
260
  },
110
261
  readonly cache?: "opt-in",
111
262
  },
@@ -179,10 +330,8 @@ export type UniflowedConfig = {
179
330
  },
180
331
  },
181
332
  readonly runtime?: {
182
- readonly default?: "node" | "deno" | "bun" | "uf",
183
- readonly compatibility?: $ReadOnlyArray<
184
- "node" | "bun" | "deno" | "edge" | "serverless" | "container",
185
- >,
333
+ readonly default?: RuntimeEngine,
334
+ readonly compatibility?: $ReadOnlyArray<RuntimeEngine>,
186
335
  readonly capabilityJsHost?: {
187
336
  readonly default?: CapabilityJsHost,
188
337
  readonly hosts?: $ReadOnlyArray<CapabilityJsHost>,
@@ -190,15 +339,20 @@ export type UniflowedConfig = {
190
339
  },
191
340
  readonly deploy?: {
192
341
  readonly enabled?: boolean,
193
- readonly adapters?: $ReadOnlyArray<
194
- "node" | "bun" | "deno" | "edge" | "serverless" | "static" | "container",
195
- >,
342
+ // The target `uf build` writes an artefact for when none is named on
343
+ // the command line; `--adapter` beats it.
344
+ readonly adapter?: DeployAdapter,
345
+ readonly adapters?: $ReadOnlyArray<DeployAdapter>,
196
346
  },
197
347
  },
198
348
  readonly router?: {
349
+ // `false` says this project is not a uf application, and is the only way
350
+ // to say it: a library has no routes to scan for.
351
+ readonly enabled?: boolean,
199
352
  readonly entry?: string,
200
353
  readonly root?: string,
201
354
  readonly manifest?: string,
355
+ readonly convention?: "file-system",
202
356
  // Turning the file-system router off is what makes a project a
203
357
  // **library** rather than an application, and `uf build` reads it: see
204
358
  // `build.lib` below and docs/app/reference/config.
@@ -215,7 +369,17 @@ export type UniflowedConfig = {
215
369
  },
216
370
  },
217
371
  readonly build?: {
372
+ // Size ceilings that fail the build. Unset by default: failing a build
373
+ // nobody asked uf to police is worse than reporting the size.
374
+ readonly budgets?: {
375
+ readonly total?: SizeBudget,
376
+ readonly initialJs?: SizeBudget,
377
+ readonly perRoute?: SizeBudget,
378
+ readonly perAsset?: SizeBudget,
379
+ },
218
380
  readonly entries?: $ReadOnlyArray<string>,
381
+ // Commands to run around the build, in the same shape as `tasks`.
382
+ readonly hooks?: { readonly [string]: TaskDefinition },
219
383
  readonly outDir?: string,
220
384
  // Prerender every route and leave no server bundle behind. Read together
221
385
  // with `app.rendering.modes`; see docs/app/reference/config.
@@ -257,6 +421,17 @@ export type UniflowedConfig = {
257
421
  readonly host?: string,
258
422
  readonly port?: number,
259
423
  readonly strictPort?: boolean,
424
+ // Which files the dev server may serve. `deny` is evaluated on the
425
+ // canonical path and beats `allow`; see docs/security.md.
426
+ readonly fs?: {
427
+ readonly allow?: $ReadOnlyArray<string>,
428
+ readonly deny?: $ReadOnlyArray<string>,
429
+ },
430
+ // `--host` refuses to bind a routable address while this is empty: a dev
431
+ // server reachable from the network with no host allow-list is a file
432
+ // server for your source tree.
433
+ readonly allowedHosts?: $ReadOnlyArray<string>,
434
+ readonly allowedOrigins?: $ReadOnlyArray<string>,
260
435
  },
261
436
  readonly docs?: {
262
437
  readonly enabled?: boolean,
@@ -266,13 +441,19 @@ export type UniflowedConfig = {
266
441
  readonly staticBuild?: boolean,
267
442
  readonly deploy?: "void",
268
443
  },
269
- readonly lint?: {
270
- readonly engine?: "rust",
271
- readonly flow?: {
272
- readonly builtins?: "mixed",
273
- readonly parser?: "official-flow-rust",
274
- },
275
- readonly rules?: { readonly [string]: RuleLevel },
444
+ /**
445
+ * The `.env` cascade, the mode it is read for, and the pinned toolchain.
446
+ *
447
+ * `active` empty means the command decides — `development` for `uf dev`,
448
+ * `production` for a build, `test` for `uf test`. `files` empty selects the
449
+ * conventional cascade rather than no files at all.
450
+ */
451
+ readonly env?: {
452
+ readonly active?: string,
453
+ readonly files?: $ReadOnlyArray<string>,
454
+ // Runtimes and package managers by exact version — `{ node: "24.14.0" }`.
455
+ // Exact, because a range is not an environment.
456
+ readonly toolchain?: { readonly [string]: string },
276
457
  },
277
458
  readonly fmt?: {
278
459
  readonly indentWidth?: number,
@@ -298,6 +479,43 @@ export type UniflowedConfig = {
298
479
  readonly quotes?: "single" | "double",
299
480
  readonly semicolons?: boolean,
300
481
  },
482
+ /**
483
+ * Paths no command walks into.
484
+ *
485
+ * Top level because every command that walks the project reads it: `uf fmt`,
486
+ * `uf lint`, `uf check`, `uf test` and `uf doc`. A bare name — `dist` — names
487
+ * a kind of directory and matches at any depth; a path — `src/generated` —
488
+ * names one place. `.uf` and `.git` are uf's and git's and are not a
489
+ * project's to opt back into.
490
+ *
491
+ * Absent takes uf's own list, `["node_modules", "dist", "target"]`. An empty
492
+ * list is a different instruction: it is a project that has looked at that
493
+ * list and wants none of it.
494
+ */
495
+ readonly ignore?: $ReadOnlyArray<string>,
496
+ readonly lint?: {
497
+ readonly engine?: "rust",
498
+ // Globs to lint.
499
+ readonly files?: $ReadOnlyArray<string>,
500
+ /**
501
+ * The old spelling of the top-level `ignore`.
502
+ *
503
+ * **Deprecated**, and read only when `ignore` is absent. It was never
504
+ * `uf lint`'s alone: `uf fmt`, `uf check`, `uf test` and `uf doc` walk the
505
+ * project through the same code and have always obeyed it, so the key was
506
+ * named after one of the five commands that read it. It keeps working for
507
+ * as long as alpha lasts, and every one of those commands says so once.
508
+ * See ubugeeei-prod/uf#575.
509
+ */
510
+ readonly ignore?: $ReadOnlyArray<string>,
511
+ readonly flow?: {
512
+ readonly builtins?: "mixed",
513
+ readonly parser?: "official-flow-rust",
514
+ },
515
+ // Changes to uf's rule table, not the whole of it: a rule you did not
516
+ // mention keeps the level uf ships. Say `"off"` to switch one off.
517
+ readonly rules?: { readonly [string]: RuleLevel },
518
+ },
301
519
  readonly package?: {
302
520
  readonly generator?: "napi-rs",
303
521
  readonly targets?: $ReadOnlyArray<
@@ -306,12 +524,17 @@ export type UniflowedConfig = {
306
524
  readonly typescriptDeclarationsToFlow?: true,
307
525
  },
308
526
  readonly permissions?: Permissions,
527
+ // Plugins the project adds, appended to uf's own and resolved in the order
528
+ // they are written. A name that names a file is code to run, so `uf_plugin`
529
+ // refuses any that reaches outside the project root.
530
+ readonly plugins?: $ReadOnlyArray<PluginEntry>,
309
531
  readonly pm?: {
310
532
  readonly module?: "@uniflowed/pm",
311
533
  readonly resolver?: "uf-native",
312
534
  readonly lockfile?: "uf.lock",
313
535
  readonly storeDir?: string,
314
536
  readonly allowLifecycleScripts?: false,
537
+ readonly packageManager?: PackageManagerPreference,
315
538
  /**
316
539
  * The registry uf *reads* from: packuments, provenance attestations, and
317
540
  * the versions `uf update` reports against.
@@ -471,10 +694,10 @@ export type UniflowedConfig = {
471
694
  readonly test?: {
472
695
  readonly module?: "@uniflowed/test",
473
696
  readonly runner?: {
474
- readonly runtime?: "capability-js-host" | "uf-self-hosted",
697
+ readonly runtime?: "vite-task" | "capability-js-host" | "uf-self-hosted",
475
698
  readonly jsHosts?: $ReadOnlyArray<CapabilityJsHost>,
476
- readonly scheduler?: "native-work-stealing",
477
- readonly performanceTarget?: "faster-than-bun",
699
+ readonly scheduler?: "vite-task-cache" | "native-work-stealing",
700
+ readonly performanceTarget?: "vite-task" | "faster-than-bun",
478
701
  readonly officialFlowParser?: true,
479
702
  },
480
703
  readonly reactTestingLibraryNative?: true,
@@ -497,6 +720,15 @@ export type UniflowedConfig = {
497
720
  },
498
721
  },
499
722
  readonly tasks?: { readonly [string]: TaskDefinition },
723
+ /**
724
+ * Vite's own configuration, merged over the one uf generates.
725
+ *
726
+ * uf reads none of it, which is the point: an option Vite adds tomorrow
727
+ * works in a uf project tomorrow rather than after a uf release that names
728
+ * it. Deliberately unshaped for the same reason — a Flow type over Vite's
729
+ * options would be the re-declaration this key exists to avoid.
730
+ */
731
+ readonly vite?: { readonly [string]: mixed },
500
732
  readonly vrt?: {
501
733
  readonly enabled?: boolean,
502
734
  readonly module?: "@uniflowed/vrt",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uniflowed/config",
3
- "version": "0.0.0-alpha.15",
3
+ "version": "0.0.0-alpha.17",
4
4
  "description": "Flow declarations for @uniflowed/config, part of the Unified Toolchain for Flow.",
5
5
  "type": "module",
6
6
  "license": "MIT",