@homeflare/config 0.2.1 → 0.4.0

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
@@ -9,18 +9,23 @@ bun add -D @homeflare/config
9
9
 
10
10
  ## What it gives you
11
11
 
12
- | file | how to use it |
13
- | -------------------- | --------------------------------------- |
14
- | `tsconfig.base.json` | `extends` — apps and Workers |
15
- | `tsconfig.lib.json` | `extends` — packages that publish types |
16
- | `oxlintrc.json` | `extends` — 36 rules, 6 plugins |
17
- | `oxfmtrc.json` | copy to `.oxfmtrc.json` |
18
- | `bunfig.toml` | copy to `bunfig.toml` |
12
+ | file | how to use it |
13
+ | -------------------- | ------------------------------------------------ |
14
+ | `tsconfig.base.json` | `extends` — owned code, full strictness |
15
+ | `tsconfig.app.json` | `extends` — Worker apps (source-publishing deps) |
16
+ | `tsconfig.lib.json` | `extends` — packages that publish types |
17
+ | `oxlintrc.json` | `extends` — libraries |
18
+ | `oxlintrc.app.json` | `extends` — Worker / TanStack / Alchemy apps |
19
+ | `oxfmtrc.json` | copy to `.oxfmtrc.json` |
20
+ | `bunfig.toml` | copy to `bunfig.toml` |
19
21
 
20
22
  ## tsconfig
21
23
 
22
24
  ```jsonc
23
- // an app or a Worker
25
+ // a Worker or app that imports packages publishing .ts (not .d.ts)
26
+ { "extends": "@homeflare/config/tsconfig.app.json" }
27
+
28
+ // owned library code — the full baseline
24
29
  { "extends": "@homeflare/config/tsconfig.base.json" }
25
30
 
26
31
  // a package that publishes types
@@ -34,24 +39,40 @@ The base turns on `strict` plus the flags that catch the most runtime bugs:
34
39
  `noUncheckedIndexedAccess` (`arr[0]` is `T | undefined`), `exactOptionalPropertyTypes`,
35
40
  and `noFallthroughCasesInSwitch`.
36
41
 
42
+ ⚠️ **`tsconfig.app.json` turns three of those off.** `skipLibCheck` only skips `.d.ts`.
43
+ Measured 2026-09-16: `@cloudflare/ci@0.2.0` ships `"types": "./src/index.ts"`, and Better
44
+ Auth plugin types do the same. Those files typecheck under _your_ flags, so the strict
45
+ baseline fails the consumer. Use the app preset there; keep `base` / `lib` for code you
46
+ own. Override the flags back on in a project that does not import source-publishing deps.
47
+
37
48
  ## oxlint
38
49
 
39
50
  ```json
40
51
  { "extends": ["./node_modules/@homeflare/config/oxlintrc.json"] }
41
52
  ```
42
53
 
54
+ A Worker, TanStack Start, or Alchemy app:
55
+
56
+ ```json
57
+ { "extends": ["./node_modules/@homeflare/config/oxlintrc.app.json"] }
58
+ ```
59
+
43
60
  ⚠️ oxlint's `extends` takes **file paths**, not package names — there is no
44
61
  `eslint-config-*` style resolution, so the path into `node_modules` is written out.
45
62
 
46
- 36 rules across `typescript`, `unicorn`, `oxc`, `import`, `react` and `jsx-a11y`. The ones
47
- worth knowing:
63
+ The library preset is 36 rules across `typescript`, `unicorn`, `oxc`, `import`, `react`
64
+ and `jsx-a11y`. The app preset extends it and turns off the collisions `--deny-warnings`
65
+ hits on correct Worker/app code (measured 2026-09-16):
48
66
 
49
- - `no-console: error` — ⚠️ except where `console` _is_ the transport, as in a Worker
50
- writing JSON for Workers Logs. Override it per file, not globally.
51
- - `typescript/no-non-null-assertion` — `!` hides exactly what `noUncheckedIndexedAccess`
52
- is trying to surface.
53
- - `react/exhaustive-deps`, `jsx-a11y/click-events-have-key-events` — only bite in React
54
- code; a Worker never sees them.
67
+ - `no-console` off under `src/` — `console` _is_ Workers Logs.
68
+ - `import/no-default-export` off — Alchemy, `Worker`, and TanStack entry files export
69
+ default.
70
+ - `jsx-a11y/control-has-associated-label` off — Kumo initials triggers are labelled by
71
+ the design system, not a `htmlFor`.
72
+ - `typescript/no-non-null-assertion` off in tests.
73
+
74
+ ⛔ Do not copy the library preset into an app and then disable those one by one. That is
75
+ how every app grows a private oxlint.
55
76
 
56
77
  ## oxfmt and bunfig
57
78
 
@@ -63,8 +84,12 @@ cp node_modules/@homeflare/config/bunfig.toml bunfig.toml
63
84
  ⚠️ `.oxfmtrc.json` needs the **leading dot**. Without it oxfmt silently uses its defaults,
64
85
  and the symptom is a formatter that rewrites your quotes.
65
86
 
66
- ⛔ Neither format supports `extends`, so these two are copies. That means they can drift —
67
- which is what `@homeflare/config/check` exists to catch.
87
+ ⛔ oxfmt has no `extends`. Extra `ignorePatterns` are a **merge**: generated OpenAPI,
88
+ `vendor/`, `**/generated/**`, `**/*.gen.ts` stay out of the formatter. ⛔ Identity
89
+ comparison is the wrong gate — it rewrote 154 files to `singleQuote` and formatted
90
+ vendor OpenAPI (measured 2026-09-16). Dropping a house ignore is still drift.
91
+
92
+ `bunfig.toml` is still an exact copy.
68
93
 
69
94
  ## Keeping a project honest
70
95
 
@@ -76,8 +101,8 @@ if (problems.length > 0) throw new Error(problems.join('\n'));
76
101
  ```
77
102
 
78
103
  ★ Run it from a test. It asserts a project still extends the shared tsconfig and oxlint
79
- presets, and that its copied `.oxfmtrc.json` and `bunfig.toml` still match — so a project
80
- that quietly diverges fails its own suite rather than drifting for six months.
104
+ presets, that `.oxfmtrc.json` keeps house style plus at least the house ignores, and that
105
+ `bunfig.toml` still matches.
81
106
 
82
107
  ## License
83
108
 
package/dist/check.d.ts CHANGED
@@ -1,20 +1,3 @@
1
- /**
2
- * Assert that a project still stands on the shared config.
3
- *
4
- * ★ WHY THIS EXISTS. Two of the five presets — `.oxfmtrc.json` and `bunfig.toml` — have no
5
- * `extends` mechanism, so adopting them means COPYING them. A copy drifts silently: the
6
- * project keeps building, the rules quietly diverge, and nobody finds out until two
7
- * repos disagree about how the same code should look.
8
- *
9
- * ⛔ IT REPORTS, IT DOES NOT REPAIR. A checker that rewrites a project's config would
10
- * overwrite a deliberate local exception, and this cannot tell the difference between
11
- * drift and a decision. It names what differs; a human decides which way to move.
12
- *
13
- * ⚠️ Every file it reads is parsed natively — `Bun.file().json()`, `Bun.TOML.parse`. No
14
- * parsing dependency reaches a consumer.
15
- * ⛔ NOT JSONC. Bun's `.json()` rejects comments (measured 2026-09-15), so a project
16
- * whose tsconfig carries them is reported as unreadable rather than silently skipped.
17
- */
18
1
  /** One thing a project should fix, in the imperative. */
19
2
  export type Problem = string;
20
3
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"check.d.ts","sourceRoot":"","sources":["../src/check.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,yDAAyD;AACzD,MAAM,MAAM,OAAO,GAAG,MAAM,CAAC;AAkH7B;;;;;GAKG;AACH,wBAAsB,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,OAAO,EAAE,CAAC,CASlF"}
1
+ {"version":3,"file":"check.d.ts","sourceRoot":"","sources":["../src/check.ts"],"names":[],"mappings":"AAmBA,yDAAyD;AACzD,MAAM,MAAM,OAAO,GAAG,MAAM,CAAC;AAyI7B;;;;;GAKG;AACH,wBAAsB,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,OAAO,EAAE,CAAC,CASlF"}
package/dist/check.js CHANGED
@@ -1,4 +1,22 @@
1
1
  // @bun
2
+ // src/oxfmt.ts
3
+ var STYLE_KEYS = ["printWidth", "useTabs", "singleQuote", "semi"];
4
+ function problemsInOxfmt(preset, theirs) {
5
+ const problems = [];
6
+ for (const key of STYLE_KEYS) {
7
+ if (theirs[key] !== preset[key]) {
8
+ problems.push(`.oxfmtrc.json: ${key} differs from @homeflare/config \u2014 re-copy it, or say why it differs`);
9
+ }
10
+ }
11
+ const local = new Set(theirs.ignorePatterns ?? []);
12
+ for (const pattern of preset.ignorePatterns ?? []) {
13
+ if (!local.has(pattern)) {
14
+ problems.push(`.oxfmtrc.json: missing house ignore ${pattern} \u2014 generated files would be rewritten`);
15
+ }
16
+ }
17
+ return problems;
18
+ }
19
+
2
20
  // src/check.ts
3
21
  function stripComments(text) {
4
22
  let out = "";
@@ -74,11 +92,27 @@ async function checkCopy(projectDir, file, parse, problems) {
74
92
  problems.push(`${file}: differs from @homeflare/config \u2014 re-copy it, or say why it differs`);
75
93
  }
76
94
  }
95
+ async function checkOxfmt(projectDir, problems) {
96
+ const mine = Bun.file(`${projectDir}/.oxfmtrc.json`);
97
+ if (!await mine.exists()) {
98
+ problems.push(".oxfmtrc.json: missing \u2014 copy it from @homeflare/config");
99
+ return;
100
+ }
101
+ let theirs;
102
+ try {
103
+ theirs = JSON.parse(await mine.text());
104
+ } catch {
105
+ problems.push(".oxfmtrc.json: unparseable");
106
+ return;
107
+ }
108
+ const preset = JSON.parse(await Bun.file(`${presetDir()}oxfmtrc.json`).text());
109
+ problems.push(...problemsInOxfmt(preset, theirs));
110
+ }
77
111
  async function checkProject(projectDir) {
78
112
  const problems = [];
79
113
  await checkExtends(projectDir, "tsconfig.json", "@homeflare/config/tsconfig", problems);
80
114
  await checkExtends(projectDir, ".oxlintrc.json", "@homeflare/config/oxlintrc", problems);
81
- await checkCopy(projectDir, ".oxfmtrc.json", (t) => JSON.parse(t), problems);
115
+ await checkOxfmt(projectDir, problems);
82
116
  await checkCopy(projectDir, "bunfig.toml", (t) => Bun.TOML.parse(t), problems);
83
117
  return problems;
84
118
  }
@@ -86,5 +120,5 @@ export {
86
120
  checkProject
87
121
  };
88
122
 
89
- //# debugId=7F6869C495728F5464756E2164756E21
123
+ //# debugId=550B779804E201AD64756E2164756E21
90
124
  //# sourceMappingURL=check.js.map
package/dist/check.js.map CHANGED
@@ -1,10 +1,11 @@
1
1
  {
2
2
  "version": 3,
3
- "sources": ["../src/check.ts"],
3
+ "sources": ["../src/oxfmt.ts", "../src/check.ts"],
4
4
  "sourcesContent": [
5
- "/**\n * Assert that a project still stands on the shared config.\n *\n * ★ WHY THIS EXISTS. Two of the five presets — `.oxfmtrc.json` and `bunfig.toml` — have no\n * `extends` mechanism, so adopting them means COPYING them. A copy drifts silently: the\n * project keeps building, the rules quietly diverge, and nobody finds out until two\n * repos disagree about how the same code should look.\n *\n * ⛔ IT REPORTS, IT DOES NOT REPAIR. A checker that rewrites a project's config would\n * overwrite a deliberate local exception, and this cannot tell the difference between\n * drift and a decision. It names what differs; a human decides which way to move.\n *\n * ⚠️ Every file it reads is parsed natively — `Bun.file().json()`, `Bun.TOML.parse`. No\n * parsing dependency reaches a consumer.\n * ⛔ NOT JSONC. Bun's `.json()` rejects comments (measured 2026-09-15), so a project\n * whose tsconfig carries them is reported as unreadable rather than silently skipped.\n */\n\n/** One thing a project should fix, in the imperative. */\nexport type Problem = string;\n\ntype Json = Record<string, unknown>;\n\n/**\n * ⚠️ A tsconfig LEGITIMATELY carries comments — that is where the reasoning for a strict\n * flag lives, and stripping them to satisfy a parser would be the wrong trade. Bun's\n * `.json()` rejects them (measured 2026-09-15), so comments are removed before parsing\n * rather than treated as a fault.\n * 🔴 A NAIVE REGEX CORRUPTS THE FILE IT IS READING. Measured while writing this: a\n * `(^|[^:\"])//.*$` strip mangled `\"./node_modules/oxlint/...\"` — the `//` inside a\n * STRING — turning valid JSON into a parse error reported as \"JSONC not supported\".\n * So this walks the text and tracks whether it is inside a string, which is the\n * smallest correct thing; a real JSONC parser would be a dependency every consumer\n * inherits for one check.\n */\nfunction stripComments(text: string): string {\n let out = '';\n let inString = false;\n let escaped = false;\n\n for (let i = 0; i < text.length; i += 1) {\n const ch = text[i] ?? '';\n const next = text[i + 1] ?? '';\n\n if (inString) {\n out += ch;\n if (escaped) escaped = false;\n else if (ch === '\\\\') escaped = true;\n else if (ch === '\"') inString = false;\n continue;\n }\n\n if (ch === '\"') {\n inString = true;\n out += ch;\n } else if (ch === '/' && next === '/') {\n while (i < text.length && text[i] !== '\\n') i += 1;\n out += '\\n';\n } else if (ch === '/' && next === '*') {\n i += 2;\n while (i < text.length && !(text[i] === '*' && text[i + 1] === '/')) i += 1;\n i += 1;\n } else {\n out += ch;\n }\n }\n\n return out;\n}\n\nasync function readJson(path: string): Promise<Json | undefined> {\n const file = Bun.file(path);\n if (!(await file.exists())) return undefined;\n try {\n return JSON.parse(stripComments(await file.text())) as Json;\n } catch {\n return undefined;\n }\n}\n\n/** Where this package's own presets live, however it was installed. */\nfunction presetDir(): string {\n return new URL('../', import.meta.url).pathname;\n}\n\nasync function checkExtends(\n projectDir: string,\n file: string,\n needle: string,\n problems: Problem[],\n): Promise<void> {\n const config = await readJson(`${projectDir}/${file}`);\n if (config === undefined) {\n problems.push(`${file}: missing or unparseable (JSONC comments are not supported)`);\n return;\n }\n\n const extended = JSON.stringify(config['extends'] ?? '');\n\n // ⚠️ A WORKSPACE PATH COUNTS. Inside this repo the presets are referenced as\n // `./packages/config/...` rather than by package name, and reporting that as drift\n // would make the check fail in the one repo that defines the config.\n const workspacePath = needle.replace('@homeflare/config/', 'packages/config/');\n if (!extended.includes(needle) && !extended.includes(workspacePath)) {\n problems.push(`${file}: does not extend ${needle} — it currently extends ${extended}`);\n }\n}\n\n/**\n * Compare a copied file against the preset, ignoring formatting.\n * ⚠️ Structural comparison, not text: a project that reformats its copy has not drifted,\n * and reporting that would train people to ignore this check.\n */\nasync function checkCopy(\n projectDir: string,\n file: string,\n parse: (text: string) => unknown,\n problems: Problem[],\n): Promise<void> {\n const mine = Bun.file(`${projectDir}/${file}`);\n if (!(await mine.exists())) {\n problems.push(`${file}: missing — copy it from @homeflare/config`);\n return;\n }\n\n const preset = parse(await Bun.file(`${presetDir()}${file.replace(/^\\./, '')}`).text());\n const theirs = parse(await mine.text());\n\n if (JSON.stringify(theirs) !== JSON.stringify(preset)) {\n problems.push(`${file}: differs from @homeflare/config — re-copy it, or say why it differs`);\n }\n}\n\n/**\n * Check a project directory. Returns an empty array when everything is in step.\n *\n * const problems = await checkProject(process.cwd());\n * expect(problems).toEqual([]);\n */\nexport async function checkProject(projectDir: string): Promise<readonly Problem[]> {\n const problems: Problem[] = [];\n\n await checkExtends(projectDir, 'tsconfig.json', '@homeflare/config/tsconfig', problems);\n await checkExtends(projectDir, '.oxlintrc.json', '@homeflare/config/oxlintrc', problems);\n await checkCopy(projectDir, '.oxfmtrc.json', (t) => JSON.parse(t) as unknown, problems);\n await checkCopy(projectDir, 'bunfig.toml', (t) => Bun.TOML.parse(t), problems);\n\n return problems;\n}\n"
5
+ "/**\n * House oxfmt is a COPY — the format has no `extends`.\n *\n * ⛔ IDENTITY WAS THE WRONG GATE. Measured 2026-09-16: a Worker/TanStack app that\n * copied the preset verbatim would format vendor OpenAPI YAML, generated trees,\n * TanStack routeTree files and openapi.json, and rewrite 154 files to singleQuote.\n * Generated OpenAPI is an artefact; a quote flip there is a false diff, not style.\n *\n * ★ Extra `ignorePatterns` are therefore allowed. House style keys are not. Dropping\n * a house ignore is still drift — that is how generated files get rewritten.\n */\n\nexport type OxfmtConfig = {\n readonly printWidth?: number;\n readonly useTabs?: boolean;\n readonly singleQuote?: boolean;\n readonly semi?: boolean;\n readonly ignorePatterns?: readonly string[];\n readonly [key: string]: unknown;\n};\n\nconst STYLE_KEYS = ['printWidth', 'useTabs', 'singleQuote', 'semi'] as const;\n\n/**\n * Problems in a project's `.oxfmtrc.json` against the house preset.\n * Empty means the copy still carries house style, and at least the house ignores.\n */\nexport function problemsInOxfmt(preset: OxfmtConfig, theirs: OxfmtConfig): readonly string[] {\n const problems: string[] = [];\n\n for (const key of STYLE_KEYS) {\n if (theirs[key] !== preset[key]) {\n problems.push(\n `.oxfmtrc.json: ${key} differs from @homeflare/config — re-copy it, or say why it differs`,\n );\n }\n }\n\n const local = new Set(theirs.ignorePatterns ?? []);\n for (const pattern of preset.ignorePatterns ?? []) {\n if (!local.has(pattern)) {\n problems.push(\n `.oxfmtrc.json: missing house ignore ${pattern} — generated files would be rewritten`,\n );\n }\n }\n\n return problems;\n}\n",
6
+ "/**\n * Assert that a project still stands on the shared config.\n *\n * ★ WHY THIS EXISTS. Two of the five presets — `.oxfmtrc.json` and `bunfig.toml` — have no\n * `extends` mechanism, so adopting them means COPYING them. A copy drifts silently: the\n * project keeps building, the rules quietly diverge, and nobody finds out until two\n * repos disagree about how the same code should look.\n *\n * ⛔ IT REPORTS, IT DOES NOT REPAIR. A checker that rewrites a project's config would\n * overwrite a deliberate local exception, and this cannot tell the difference between\n * drift and a decision. It names what differs; a human decides which way to move.\n *\n * ⚠️ Every file it reads is parsed natively — `Bun.file().json()`, `Bun.TOML.parse`. No\n * parsing dependency reaches a consumer.\n * ⛔ NOT JSONC. Bun's `.json()` rejects comments (measured 2026-09-15), so a project\n * whose tsconfig carries them is reported as unreadable rather than silently skipped.\n */\nimport { type OxfmtConfig, problemsInOxfmt } from './oxfmt.ts';\n\n/** One thing a project should fix, in the imperative. */\nexport type Problem = string;\n\ntype Json = Record<string, unknown>;\n\n/**\n * ⚠️ A tsconfig LEGITIMATELY carries comments — that is where the reasoning for a strict\n * flag lives, and stripping them to satisfy a parser would be the wrong trade. Bun's\n * `.json()` rejects them (measured 2026-09-15), so comments are removed before parsing\n * rather than treated as a fault.\n * 🔴 A NAIVE REGEX CORRUPTS THE FILE IT IS READING. Measured while writing this: a\n * `(^|[^:\"])//.*$` strip mangled `\"./node_modules/oxlint/...\"` — the `//` inside a\n * STRING — turning valid JSON into a parse error reported as \"JSONC not supported\".\n * So this walks the text and tracks whether it is inside a string, which is the\n * smallest correct thing; a real JSONC parser would be a dependency every consumer\n * inherits for one check.\n */\nfunction stripComments(text: string): string {\n let out = '';\n let inString = false;\n let escaped = false;\n\n for (let i = 0; i < text.length; i += 1) {\n const ch = text[i] ?? '';\n const next = text[i + 1] ?? '';\n\n if (inString) {\n out += ch;\n if (escaped) escaped = false;\n else if (ch === '\\\\') escaped = true;\n else if (ch === '\"') inString = false;\n continue;\n }\n\n if (ch === '\"') {\n inString = true;\n out += ch;\n } else if (ch === '/' && next === '/') {\n while (i < text.length && text[i] !== '\\n') i += 1;\n out += '\\n';\n } else if (ch === '/' && next === '*') {\n i += 2;\n while (i < text.length && !(text[i] === '*' && text[i + 1] === '/')) i += 1;\n i += 1;\n } else {\n out += ch;\n }\n }\n\n return out;\n}\n\nasync function readJson(path: string): Promise<Json | undefined> {\n const file = Bun.file(path);\n if (!(await file.exists())) return undefined;\n try {\n return JSON.parse(stripComments(await file.text())) as Json;\n } catch {\n return undefined;\n }\n}\n\n/** Where this package's own presets live, however it was installed. */\nfunction presetDir(): string {\n return new URL('../', import.meta.url).pathname;\n}\n\nasync function checkExtends(\n projectDir: string,\n file: string,\n needle: string,\n problems: Problem[],\n): Promise<void> {\n const config = await readJson(`${projectDir}/${file}`);\n if (config === undefined) {\n problems.push(`${file}: missing or unparseable (JSONC comments are not supported)`);\n return;\n }\n\n const extended = JSON.stringify(config['extends'] ?? '');\n\n // ⚠️ A WORKSPACE PATH COUNTS. Inside this repo the presets are referenced as\n // `./packages/config/...` rather than by package name, and reporting that as drift\n // would make the check fail in the one repo that defines the config.\n const workspacePath = needle.replace('@homeflare/config/', 'packages/config/');\n if (!extended.includes(needle) && !extended.includes(workspacePath)) {\n problems.push(`${file}: does not extend ${needle} — it currently extends ${extended}`);\n }\n}\n\n/**\n * Compare a copied file against the preset, ignoring formatting.\n * ⚠️ Structural comparison, not text: a project that reformats its copy has not drifted,\n * and reporting that would train people to ignore this check.\n */\nasync function checkCopy(\n projectDir: string,\n file: string,\n parse: (text: string) => unknown,\n problems: Problem[],\n): Promise<void> {\n const mine = Bun.file(`${projectDir}/${file}`);\n if (!(await mine.exists())) {\n problems.push(`${file}: missing — copy it from @homeflare/config`);\n return;\n }\n\n const preset = parse(await Bun.file(`${presetDir()}${file.replace(/^\\./, '')}`).text());\n const theirs = parse(await mine.text());\n\n if (JSON.stringify(theirs) !== JSON.stringify(preset)) {\n problems.push(`${file}: differs from @homeflare/config — re-copy it, or say why it differs`);\n }\n}\n\n/**\n * House oxfmt has no `extends`. Extra ignores are a merge; style keys are not.\n * ⛔ Identity comparison rewrote generated OpenAPI (measured 2026-09-16).\n */\nasync function checkOxfmt(projectDir: string, problems: Problem[]): Promise<void> {\n const mine = Bun.file(`${projectDir}/.oxfmtrc.json`);\n if (!(await mine.exists())) {\n problems.push('.oxfmtrc.json: missing — copy it from @homeflare/config');\n return;\n }\n\n let theirs: OxfmtConfig;\n try {\n theirs = JSON.parse(await mine.text()) as OxfmtConfig;\n } catch {\n problems.push('.oxfmtrc.json: unparseable');\n return;\n }\n\n const preset = JSON.parse(await Bun.file(`${presetDir()}oxfmtrc.json`).text()) as OxfmtConfig;\n problems.push(...problemsInOxfmt(preset, theirs));\n}\n\n/**\n * Check a project directory. Returns an empty array when everything is in step.\n *\n * const problems = await checkProject(process.cwd());\n * expect(problems).toEqual([]);\n */\nexport async function checkProject(projectDir: string): Promise<readonly Problem[]> {\n const problems: Problem[] = [];\n\n await checkExtends(projectDir, 'tsconfig.json', '@homeflare/config/tsconfig', problems);\n await checkExtends(projectDir, '.oxlintrc.json', '@homeflare/config/oxlintrc', problems);\n await checkOxfmt(projectDir, problems);\n await checkCopy(projectDir, 'bunfig.toml', (t) => Bun.TOML.parse(t), problems);\n\n return problems;\n}\n"
6
7
  ],
7
- "mappings": ";;AAmCA,SAAS,aAAa,CAAC,MAAsB;AAAA,EAC3C,IAAI,MAAM;AAAA,EACV,IAAI,WAAW;AAAA,EACf,IAAI,UAAU;AAAA,EAEd,SAAS,IAAI,EAAG,IAAI,KAAK,QAAQ,KAAK,GAAG;AAAA,IACvC,MAAM,KAAK,KAAK,MAAM;AAAA,IACtB,MAAM,OAAO,KAAK,IAAI,MAAM;AAAA,IAE5B,IAAI,UAAU;AAAA,MACZ,OAAO;AAAA,MACP,IAAI;AAAA,QAAS,UAAU;AAAA,MAClB,SAAI,OAAO;AAAA,QAAM,UAAU;AAAA,MAC3B,SAAI,OAAO;AAAA,QAAK,WAAW;AAAA,MAChC;AAAA,IACF;AAAA,IAEA,IAAI,OAAO,KAAK;AAAA,MACd,WAAW;AAAA,MACX,OAAO;AAAA,IACT,EAAO,SAAI,OAAO,OAAO,SAAS,KAAK;AAAA,MACrC,OAAO,IAAI,KAAK,UAAU,KAAK,OAAO;AAAA;AAAA,QAAM,KAAK;AAAA,MACjD,OAAO;AAAA;AAAA,IACT,EAAO,SAAI,OAAO,OAAO,SAAS,KAAK;AAAA,MACrC,KAAK;AAAA,MACL,OAAO,IAAI,KAAK,UAAU,EAAE,KAAK,OAAO,OAAO,KAAK,IAAI,OAAO;AAAA,QAAM,KAAK;AAAA,MAC1E,KAAK;AAAA,IACP,EAAO;AAAA,MACL,OAAO;AAAA;AAAA,EAEX;AAAA,EAEA,OAAO;AAAA;AAGT,eAAe,QAAQ,CAAC,MAAyC;AAAA,EAC/D,MAAM,OAAO,IAAI,KAAK,IAAI;AAAA,EAC1B,IAAI,CAAE,MAAM,KAAK,OAAO;AAAA,IAAI;AAAA,EAC5B,IAAI;AAAA,IACF,OAAO,KAAK,MAAM,cAAc,MAAM,KAAK,KAAK,CAAC,CAAC;AAAA,IAClD,MAAM;AAAA,IACN;AAAA;AAAA;AAKJ,SAAS,SAAS,GAAW;AAAA,EAC3B,OAAO,IAAI,IAAI,OAAO,YAAY,GAAG,EAAE;AAAA;AAGzC,eAAe,YAAY,CACzB,YACA,MACA,QACA,UACe;AAAA,EACf,MAAM,SAAS,MAAM,SAAS,GAAG,cAAc,MAAM;AAAA,EACrD,IAAI,WAAW,WAAW;AAAA,IACxB,SAAS,KAAK,GAAG,iEAAiE;AAAA,IAClF;AAAA,EACF;AAAA,EAEA,MAAM,WAAW,KAAK,UAAU,OAAO,cAAc,EAAE;AAAA,EAKvD,MAAM,gBAAgB,OAAO,QAAQ,sBAAsB,kBAAkB;AAAA,EAC7E,IAAI,CAAC,SAAS,SAAS,MAAM,KAAK,CAAC,SAAS,SAAS,aAAa,GAAG;AAAA,IACnE,SAAS,KAAK,GAAG,yBAAyB,sCAAiC,UAAU;AAAA,EACvF;AAAA;AAQF,eAAe,SAAS,CACtB,YACA,MACA,OACA,UACe;AAAA,EACf,MAAM,OAAO,IAAI,KAAK,GAAG,cAAc,MAAM;AAAA,EAC7C,IAAI,CAAE,MAAM,KAAK,OAAO,GAAI;AAAA,IAC1B,SAAS,KAAK,GAAG,qDAAgD;AAAA,IACjE;AAAA,EACF;AAAA,EAEA,MAAM,SAAS,MAAM,MAAM,IAAI,KAAK,GAAG,UAAU,IAAI,KAAK,QAAQ,OAAO,EAAE,GAAG,EAAE,KAAK,CAAC;AAAA,EACtF,MAAM,SAAS,MAAM,MAAM,KAAK,KAAK,CAAC;AAAA,EAEtC,IAAI,KAAK,UAAU,MAAM,MAAM,KAAK,UAAU,MAAM,GAAG;AAAA,IACrD,SAAS,KAAK,GAAG,+EAA0E;AAAA,EAC7F;AAAA;AASF,eAAsB,YAAY,CAAC,YAAiD;AAAA,EAClF,MAAM,WAAsB,CAAC;AAAA,EAE7B,MAAM,aAAa,YAAY,iBAAiB,8BAA8B,QAAQ;AAAA,EACtF,MAAM,aAAa,YAAY,kBAAkB,8BAA8B,QAAQ;AAAA,EACvF,MAAM,UAAU,YAAY,iBAAiB,CAAC,MAAM,KAAK,MAAM,CAAC,GAAc,QAAQ;AAAA,EACtF,MAAM,UAAU,YAAY,eAAe,CAAC,MAAM,IAAI,KAAK,MAAM,CAAC,GAAG,QAAQ;AAAA,EAE7E,OAAO;AAAA;",
8
- "debugId": "7F6869C495728F5464756E2164756E21",
8
+ "mappings": "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",
9
+ "debugId": "550B779804E201AD64756E2164756E21",
9
10
  "names": []
10
11
  }
@@ -0,0 +1,25 @@
1
+ /**
2
+ * House oxfmt is a COPY — the format has no `extends`.
3
+ *
4
+ * ⛔ IDENTITY WAS THE WRONG GATE. Measured 2026-09-16: a Worker/TanStack app that
5
+ * copied the preset verbatim would format vendor OpenAPI YAML, generated trees,
6
+ * TanStack routeTree files and openapi.json, and rewrite 154 files to singleQuote.
7
+ * Generated OpenAPI is an artefact; a quote flip there is a false diff, not style.
8
+ *
9
+ * ★ Extra `ignorePatterns` are therefore allowed. House style keys are not. Dropping
10
+ * a house ignore is still drift — that is how generated files get rewritten.
11
+ */
12
+ export type OxfmtConfig = {
13
+ readonly printWidth?: number;
14
+ readonly useTabs?: boolean;
15
+ readonly singleQuote?: boolean;
16
+ readonly semi?: boolean;
17
+ readonly ignorePatterns?: readonly string[];
18
+ readonly [key: string]: unknown;
19
+ };
20
+ /**
21
+ * Problems in a project's `.oxfmtrc.json` against the house preset.
22
+ * Empty means the copy still carries house style, and at least the house ignores.
23
+ */
24
+ export declare function problemsInOxfmt(preset: OxfmtConfig, theirs: OxfmtConfig): readonly string[];
25
+ //# sourceMappingURL=oxfmt.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oxfmt.d.ts","sourceRoot":"","sources":["../src/oxfmt.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,MAAM,MAAM,WAAW,GAAG;IACxB,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,WAAW,CAAC,EAAE,OAAO,CAAC;IAC/B,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,cAAc,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC5C,QAAQ,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACjC,CAAC;AAIF;;;GAGG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,WAAW,GAAG,SAAS,MAAM,EAAE,CAqB3F"}
package/oxfmtrc.json CHANGED
@@ -3,5 +3,17 @@
3
3
  "useTabs": false,
4
4
  "singleQuote": true,
5
5
  "semi": true,
6
- "ignorePatterns": ["**/node_modules/**", "**/dist/**", "**/coverage/**"]
6
+ "ignorePatterns": [
7
+ "**/node_modules/**",
8
+ "**/dist/**",
9
+ "**/coverage/**",
10
+ "**/generated/**",
11
+ "**/*.gen.ts",
12
+ "**/vendor/**",
13
+ "**/*.openapi.yaml",
14
+ "**/*.openapi.yml",
15
+ "**/openapi.json",
16
+ "**/.wrangler/**",
17
+ "**/worker-configuration.d.ts"
18
+ ]
7
19
  }
@@ -0,0 +1,32 @@
1
+ {
2
+ "extends": ["./oxlintrc.json"],
3
+ "ignorePatterns": [
4
+ "dist/**",
5
+ "coverage/**",
6
+ "**/generated/**",
7
+ "**/*.gen.ts",
8
+ "**/vendor/**",
9
+ "**/*.openapi.yaml",
10
+ "**/*.openapi.yml",
11
+ "**/openapi.json"
12
+ ],
13
+ "rules": {
14
+ "import/no-default-export": "off",
15
+ "jsx-a11y/control-has-associated-label": "off"
16
+ },
17
+ "overrides": [
18
+ {
19
+ "files": ["src/**", "workers/**", "app/**"],
20
+ "rules": {
21
+ "no-console": "off"
22
+ }
23
+ },
24
+ {
25
+ "files": ["**/tests/**", "**/*.test.ts", "**/*.test.tsx", "**/*.spec.ts", "**/scripts/**"],
26
+ "rules": {
27
+ "no-console": "off",
28
+ "typescript/no-non-null-assertion": "off"
29
+ }
30
+ }
31
+ ]
32
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@homeflare/config",
3
- "version": "0.2.1",
3
+ "version": "0.4.0",
4
4
  "description": "Shared tsconfig, oxlint and oxfmt configuration for HomeFlare projects.",
5
5
  "license": "MIT",
6
6
  "author": "Timothy Schneider",
@@ -14,7 +14,9 @@
14
14
  "src",
15
15
  "tsconfig.base.json",
16
16
  "tsconfig.lib.json",
17
+ "tsconfig.app.json",
17
18
  "oxlintrc.json",
19
+ "oxlintrc.app.json",
18
20
  "oxfmtrc.json",
19
21
  "bunfig.toml",
20
22
  "README.md",
@@ -24,7 +26,9 @@
24
26
  "exports": {
25
27
  "./tsconfig.base.json": "./tsconfig.base.json",
26
28
  "./tsconfig.lib.json": "./tsconfig.lib.json",
29
+ "./tsconfig.app.json": "./tsconfig.app.json",
27
30
  "./oxlintrc.json": "./oxlintrc.json",
31
+ "./oxlintrc.app.json": "./oxlintrc.app.json",
28
32
  "./oxfmtrc.json": "./oxfmtrc.json",
29
33
  "./bunfig.toml": "./bunfig.toml",
30
34
  "./check": {
package/src/check.ts CHANGED
@@ -15,6 +15,7 @@
15
15
  * ⛔ NOT JSONC. Bun's `.json()` rejects comments (measured 2026-09-15), so a project
16
16
  * whose tsconfig carries them is reported as unreadable rather than silently skipped.
17
17
  */
18
+ import { type OxfmtConfig, problemsInOxfmt } from './oxfmt.ts';
18
19
 
19
20
  /** One thing a project should fix, in the imperative. */
20
21
  export type Problem = string;
@@ -131,6 +132,29 @@ async function checkCopy(
131
132
  }
132
133
  }
133
134
 
135
+ /**
136
+ * House oxfmt has no `extends`. Extra ignores are a merge; style keys are not.
137
+ * ⛔ Identity comparison rewrote generated OpenAPI (measured 2026-09-16).
138
+ */
139
+ async function checkOxfmt(projectDir: string, problems: Problem[]): Promise<void> {
140
+ const mine = Bun.file(`${projectDir}/.oxfmtrc.json`);
141
+ if (!(await mine.exists())) {
142
+ problems.push('.oxfmtrc.json: missing — copy it from @homeflare/config');
143
+ return;
144
+ }
145
+
146
+ let theirs: OxfmtConfig;
147
+ try {
148
+ theirs = JSON.parse(await mine.text()) as OxfmtConfig;
149
+ } catch {
150
+ problems.push('.oxfmtrc.json: unparseable');
151
+ return;
152
+ }
153
+
154
+ const preset = JSON.parse(await Bun.file(`${presetDir()}oxfmtrc.json`).text()) as OxfmtConfig;
155
+ problems.push(...problemsInOxfmt(preset, theirs));
156
+ }
157
+
134
158
  /**
135
159
  * Check a project directory. Returns an empty array when everything is in step.
136
160
  *
@@ -142,7 +166,7 @@ export async function checkProject(projectDir: string): Promise<readonly Problem
142
166
 
143
167
  await checkExtends(projectDir, 'tsconfig.json', '@homeflare/config/tsconfig', problems);
144
168
  await checkExtends(projectDir, '.oxlintrc.json', '@homeflare/config/oxlintrc', problems);
145
- await checkCopy(projectDir, '.oxfmtrc.json', (t) => JSON.parse(t) as unknown, problems);
169
+ await checkOxfmt(projectDir, problems);
146
170
  await checkCopy(projectDir, 'bunfig.toml', (t) => Bun.TOML.parse(t), problems);
147
171
 
148
172
  return problems;
package/src/oxfmt.ts ADDED
@@ -0,0 +1,49 @@
1
+ /**
2
+ * House oxfmt is a COPY — the format has no `extends`.
3
+ *
4
+ * ⛔ IDENTITY WAS THE WRONG GATE. Measured 2026-09-16: a Worker/TanStack app that
5
+ * copied the preset verbatim would format vendor OpenAPI YAML, generated trees,
6
+ * TanStack routeTree files and openapi.json, and rewrite 154 files to singleQuote.
7
+ * Generated OpenAPI is an artefact; a quote flip there is a false diff, not style.
8
+ *
9
+ * ★ Extra `ignorePatterns` are therefore allowed. House style keys are not. Dropping
10
+ * a house ignore is still drift — that is how generated files get rewritten.
11
+ */
12
+
13
+ export type OxfmtConfig = {
14
+ readonly printWidth?: number;
15
+ readonly useTabs?: boolean;
16
+ readonly singleQuote?: boolean;
17
+ readonly semi?: boolean;
18
+ readonly ignorePatterns?: readonly string[];
19
+ readonly [key: string]: unknown;
20
+ };
21
+
22
+ const STYLE_KEYS = ['printWidth', 'useTabs', 'singleQuote', 'semi'] as const;
23
+
24
+ /**
25
+ * Problems in a project's `.oxfmtrc.json` against the house preset.
26
+ * Empty means the copy still carries house style, and at least the house ignores.
27
+ */
28
+ export function problemsInOxfmt(preset: OxfmtConfig, theirs: OxfmtConfig): readonly string[] {
29
+ const problems: string[] = [];
30
+
31
+ for (const key of STYLE_KEYS) {
32
+ if (theirs[key] !== preset[key]) {
33
+ problems.push(
34
+ `.oxfmtrc.json: ${key} differs from @homeflare/config — re-copy it, or say why it differs`,
35
+ );
36
+ }
37
+ }
38
+
39
+ const local = new Set(theirs.ignorePatterns ?? []);
40
+ for (const pattern of preset.ignorePatterns ?? []) {
41
+ if (!local.has(pattern)) {
42
+ problems.push(
43
+ `.oxfmtrc.json: missing house ignore ${pattern} — generated files would be rewritten`,
44
+ );
45
+ }
46
+ }
47
+
48
+ return problems;
49
+ }
@@ -0,0 +1,22 @@
1
+ {
2
+ // For a Worker or app that IMPORTS packages which publish TypeScript source.
3
+ //
4
+ // { "extends": "@homeflare/config/tsconfig.app.json" }
5
+ //
6
+ // ★ THE STRICT BASELINE STAYS. tsconfig.base.json and tsconfig.lib.json keep
7
+ // exactOptionalPropertyTypes, noImplicitOverride and noUncheckedIndexedAccess —
8
+ // they catch real bugs in code you own.
9
+ //
10
+ // ⛔ skipLibCheck CANNOT HELP HERE. It only skips .d.ts. Measured 2026-09-16:
11
+ // @cloudflare/ci@0.2.0 ships `"types": "./src/index.ts"`, and Better Auth plugin
12
+ // types do the same via a `dev-source` condition. Those files typecheck under
13
+ // THIS project's flags, so the three below fail the consumer, not the library.
14
+ //
15
+ // Override them back on in a project that does not import source-publishing deps.
16
+ "extends": "./tsconfig.base.json",
17
+ "compilerOptions": {
18
+ "exactOptionalPropertyTypes": false,
19
+ "noImplicitOverride": false,
20
+ "noUncheckedIndexedAccess": false
21
+ }
22
+ }