@inixiative/config 0.1.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 +87 -0
- package/biome/base.json +39 -0
- package/biome/react.json +10 -0
- package/dist/cli.js +587 -0
- package/dist/tsup.d.ts +6 -0
- package/dist/tsup.js +23 -0
- package/package.json +69 -0
- package/tsconfig/base.json +20 -0
- package/tsconfig/node.json +6 -0
- package/tsconfig/react.json +7 -0
- package/versions.json +18 -0
package/README.md
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# @inixiative/config
|
|
2
|
+
|
|
3
|
+
Shared toolchain for the inixiative ecosystem: tsconfig/biome/tsup presets, a version manifest (BOM), and a `sync`/`check` CLI that enforces both.
|
|
4
|
+
|
|
5
|
+
## Presets
|
|
6
|
+
|
|
7
|
+
| Export | Use |
|
|
8
|
+
| --- | --- |
|
|
9
|
+
| `@inixiative/config/tsconfig/base.json` | bun library (bundler resolution, verbatimModuleSyntax, `types: ["bun"]`) |
|
|
10
|
+
| `@inixiative/config/tsconfig/node.json` | base + `types: ["node"]` |
|
|
11
|
+
| `@inixiative/config/tsconfig/react.json` | base + DOM lib + `jsx: react-jsx` |
|
|
12
|
+
| `@inixiative/config/biome/base.json` | formatter + linter core (single quotes, lineWidth 100, recommended + strictness block) |
|
|
13
|
+
| `@inixiative/config/biome/react.json` | overlay: hook dependency linting |
|
|
14
|
+
| `@inixiative/config/tsup` | `node(options)` / `react(options)` build presets |
|
|
15
|
+
|
|
16
|
+
Adoption is three stub files plus the devDependency:
|
|
17
|
+
|
|
18
|
+
```jsonc
|
|
19
|
+
// tsconfig.json
|
|
20
|
+
{ "extends": "@inixiative/config/tsconfig/base.json", "exclude": ["dist"] }
|
|
21
|
+
|
|
22
|
+
// biome.json
|
|
23
|
+
{ "extends": ["@inixiative/config/biome/base.json"] }
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
```ts
|
|
27
|
+
// tsup.config.ts
|
|
28
|
+
import { node } from '@inixiative/config/tsup';
|
|
29
|
+
|
|
30
|
+
export default node();
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Stubs hold `extends` plus minimal reviewed overrides only. This package never generates config bodies.
|
|
34
|
+
|
|
35
|
+
## versions.json (the BOM)
|
|
36
|
+
|
|
37
|
+
One version of `@inixiative/config` names one coherent ecosystem state:
|
|
38
|
+
|
|
39
|
+
- `bun` — blessed runtime, written to `.bun-version` and `packageManager`
|
|
40
|
+
- `toolchain` — exact pins for `@biomejs/biome`, `typescript`, `tsup`, `@types/bun`
|
|
41
|
+
- `required` — toolchain packages every repo must carry
|
|
42
|
+
- `ecosystem` — the blessed, mutually-verified `@inixiative/*` set
|
|
43
|
+
|
|
44
|
+
TypeScript 6.0 notes, discovered by this repo's fixture suite:
|
|
45
|
+
|
|
46
|
+
- TS 6.0 no longer auto-includes `node_modules/@types`, so `base.json` sets `types: ["bun"]` and `@types/bun` is required.
|
|
47
|
+
- tsup's dts build trips TS 6.0's `baseUrl` deprecation; the tsup presets scope `ignoreDeprecations: "6.0"` to the dts build only.
|
|
48
|
+
|
|
49
|
+
## CLI
|
|
50
|
+
|
|
51
|
+
```
|
|
52
|
+
bunx @inixiative/config check [dir] [--preset=base|node|react]
|
|
53
|
+
bunx @inixiative/config sync [dir] [--preset=...] [--force] [--no-install]
|
|
54
|
+
bunx @inixiative/config scan [root]
|
|
55
|
+
bunx @inixiative/config train [root]
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
`check` is read-only and exits non-zero on drift — run it in CI after a frozen-lockfile install. It verifies toolchain pins, `"latest"` ranges, `packageManager`/`.bun-version`, legacy `bun.lockb`, a committed (tracked, un-ignored) `bun.lock`, required scripts (`check`/`typecheck`/`lint`/`test`), stub `extends`, presence of this package, and for every ecosystem dependency that the declared range admits the blessed version and the lockfile actually resolves to it (the stale-lockfile class).
|
|
59
|
+
|
|
60
|
+
`sync` applies every fix, re-locks via `bun install`, and runs `bun update` on stale ecosystem entries. It refuses to run on a dirty working tree without `--force`. The react preset is inferred from a `react` dependency; override with `--preset`. Missing scripts are filled with defaults; existing script bodies are never touched.
|
|
61
|
+
|
|
62
|
+
`scan` runs `check` across every ecosystem checkout under a root directory. Targets are derived from the BOM's `ecosystem` keys and matched by package name, so directory names and local layout never need declaring.
|
|
63
|
+
|
|
64
|
+
`train` is the release cascade, run from this repo's checkout. It walks the ecosystem in dependency order; per repo it: skips if dirty or behind origin → bumps ecosystem ranges to the BOM → re-locks → runs the repo's `check` → commits only its own changes (`package.json` + `bun.lock`) → publishes via `npm publish` when the local version is ahead of npm (OTP prompts pass through) → records the published version in the BOM. It never pushes; review its commits, push, then commit and publish this package so the BOM names the new state.
|
|
65
|
+
|
|
66
|
+
Division of labor: this CLI owns the toolchain set, ecosystem coherence, stubs, and lockfile format. Renovate owns everything else plus bumping `@inixiative/config` itself, and must be fenced off the toolchain packages.
|
|
67
|
+
|
|
68
|
+
## CI
|
|
69
|
+
|
|
70
|
+
Every lib repo gets a two-line caller workflow:
|
|
71
|
+
|
|
72
|
+
```yaml
|
|
73
|
+
name: ci
|
|
74
|
+
on:
|
|
75
|
+
push:
|
|
76
|
+
branches: [main]
|
|
77
|
+
pull_request:
|
|
78
|
+
jobs:
|
|
79
|
+
ci:
|
|
80
|
+
uses: inixiative/config/.github/workflows/lib-ci.yml@main
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
`lib-ci.yml` pins bun from `.bun-version`, installs with `--frozen-lockfile` (kills the stale-lockfile class), runs `inixiative-config check .`, then the repo's own `bun run check`.
|
|
84
|
+
|
|
85
|
+
## Release flow
|
|
86
|
+
|
|
87
|
+
Publishing moves in dependency order (json-rules → permissions → transitions → rules-builder). `train` automates the walk; after it completes, commit and publish this package so the BOM names the new state. Downstream repos pick it up via `sync`.
|
package/biome/base.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://biomejs.dev/schemas/2.5.2/schema.json",
|
|
3
|
+
"vcs": { "enabled": true, "clientKind": "git", "useIgnoreFile": true },
|
|
4
|
+
"files": {
|
|
5
|
+
"ignoreUnknown": false,
|
|
6
|
+
"includes": ["**", "!**/dist", "!**/node_modules"]
|
|
7
|
+
},
|
|
8
|
+
"formatter": {
|
|
9
|
+
"enabled": true,
|
|
10
|
+
"indentStyle": "space",
|
|
11
|
+
"indentWidth": 2,
|
|
12
|
+
"lineWidth": 100
|
|
13
|
+
},
|
|
14
|
+
"assist": { "actions": { "source": { "organizeImports": "on" } } },
|
|
15
|
+
"linter": {
|
|
16
|
+
"enabled": true,
|
|
17
|
+
"rules": {
|
|
18
|
+
"recommended": true,
|
|
19
|
+
"correctness": {
|
|
20
|
+
"noUnusedVariables": "error"
|
|
21
|
+
},
|
|
22
|
+
"suspicious": {
|
|
23
|
+
"noExplicitAny": "warn",
|
|
24
|
+
"noImplicitAnyLet": "warn",
|
|
25
|
+
"noThenProperty": "off"
|
|
26
|
+
},
|
|
27
|
+
"style": {
|
|
28
|
+
"noNonNullAssertion": "warn"
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
"javascript": {
|
|
33
|
+
"formatter": {
|
|
34
|
+
"quoteStyle": "single",
|
|
35
|
+
"trailingCommas": "all",
|
|
36
|
+
"semicolons": "always"
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
package/biome/react.json
ADDED
package/dist/cli.js
ADDED
|
@@ -0,0 +1,587 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/cli.ts
|
|
4
|
+
import { spawnSync as spawnSync2 } from "child_process";
|
|
5
|
+
import { existsSync as existsSync2 } from "fs";
|
|
6
|
+
import { join as join2, resolve } from "path";
|
|
7
|
+
|
|
8
|
+
// src/lib.ts
|
|
9
|
+
import { spawnSync } from "child_process";
|
|
10
|
+
import { existsSync, readdirSync, readFileSync, rmSync, writeFileSync } from "fs";
|
|
11
|
+
import { dirname, join } from "path";
|
|
12
|
+
import { fileURLToPath } from "url";
|
|
13
|
+
var packageRoot = join(dirname(fileURLToPath(import.meta.url)), "..");
|
|
14
|
+
var REQUIRED_SCRIPTS = {
|
|
15
|
+
typecheck: "tsc --noEmit",
|
|
16
|
+
lint: "biome check .",
|
|
17
|
+
test: "bun test",
|
|
18
|
+
check: "bun run typecheck && bun run lint && bun run test"
|
|
19
|
+
};
|
|
20
|
+
var DEP_FIELDS = ["dependencies", "devDependencies", "peerDependencies"];
|
|
21
|
+
var loadManifest = () => JSON.parse(readFileSync(join(packageRoot, "versions.json"), "utf8"));
|
|
22
|
+
var ownVersion = () => JSON.parse(readFileSync(join(packageRoot, "package.json"), "utf8")).version;
|
|
23
|
+
var stripJsonc = (text) => {
|
|
24
|
+
let out = "";
|
|
25
|
+
let inString = false;
|
|
26
|
+
let i = 0;
|
|
27
|
+
while (i < text.length) {
|
|
28
|
+
const ch = text[i];
|
|
29
|
+
const next = text[i + 1];
|
|
30
|
+
if (inString) {
|
|
31
|
+
out += ch;
|
|
32
|
+
if (ch === "\\") {
|
|
33
|
+
out += next ?? "";
|
|
34
|
+
i += 2;
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
if (ch === '"') inString = false;
|
|
38
|
+
i++;
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
if (ch === '"') {
|
|
42
|
+
inString = true;
|
|
43
|
+
out += ch;
|
|
44
|
+
i++;
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
if (ch === "/" && next === "/") {
|
|
48
|
+
while (i < text.length && text[i] !== "\n") i++;
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
if (ch === "/" && next === "*") {
|
|
52
|
+
i += 2;
|
|
53
|
+
while (i < text.length && !(text[i] === "*" && text[i + 1] === "/")) i++;
|
|
54
|
+
i += 2;
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
out += ch;
|
|
58
|
+
i++;
|
|
59
|
+
}
|
|
60
|
+
return out.replace(/,(\s*[}\]])/g, "$1");
|
|
61
|
+
};
|
|
62
|
+
var parseJsonc = (text) => JSON.parse(stripJsonc(text));
|
|
63
|
+
var tryParseJsonc = (text) => {
|
|
64
|
+
try {
|
|
65
|
+
const parsed = parseJsonc(text);
|
|
66
|
+
return typeof parsed === "object" && parsed !== null ? parsed : null;
|
|
67
|
+
} catch {
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
var parseRange = (range) => {
|
|
72
|
+
const match = range.match(/^(\^|~|>=)?\s*(\d+)\.(\d+)\.(\d+)(?:-[\w.]+)?$/);
|
|
73
|
+
if (!match) return null;
|
|
74
|
+
return {
|
|
75
|
+
op: match[1] ?? "=",
|
|
76
|
+
version: `${match[2]}.${match[3]}.${match[4]}`
|
|
77
|
+
};
|
|
78
|
+
};
|
|
79
|
+
var compare = (a, b) => {
|
|
80
|
+
const pa = a.split(".").map(Number);
|
|
81
|
+
const pb = b.split(".").map(Number);
|
|
82
|
+
for (let i = 0; i < 3; i++) {
|
|
83
|
+
if (pa[i] !== pb[i]) return pa[i] - pb[i];
|
|
84
|
+
}
|
|
85
|
+
return 0;
|
|
86
|
+
};
|
|
87
|
+
var admits = (range, version) => {
|
|
88
|
+
if (range.includes("||")) {
|
|
89
|
+
const parts = range.split("||").map((part) => admits(part.trim(), version));
|
|
90
|
+
if (parts.some((p) => p === true)) return true;
|
|
91
|
+
return parts.includes(null) ? null : false;
|
|
92
|
+
}
|
|
93
|
+
const parsed = parseRange(range.trim());
|
|
94
|
+
if (!parsed) return null;
|
|
95
|
+
const cmp = compare(version, parsed.version);
|
|
96
|
+
if (parsed.op === "=") return cmp === 0;
|
|
97
|
+
if (parsed.op === ">=") return cmp >= 0;
|
|
98
|
+
if (cmp < 0) return false;
|
|
99
|
+
const [major, minor] = parsed.version.split(".").map(Number);
|
|
100
|
+
const [vMajor, vMinor] = version.split(".").map(Number);
|
|
101
|
+
if (parsed.op === "^") return major === 0 ? vMajor === 0 && vMinor === minor : vMajor === major;
|
|
102
|
+
return vMajor === major && vMinor === minor;
|
|
103
|
+
};
|
|
104
|
+
var lockedVersion = (lockText, name) => {
|
|
105
|
+
const match = lockText.match(new RegExp(`"${name.replace(/\//g, "\\/")}@(\\d[^"]*)"`));
|
|
106
|
+
return match ? match[1] : null;
|
|
107
|
+
};
|
|
108
|
+
var topoOrder = (root, manifest2) => {
|
|
109
|
+
const repos = discoverRepos(root, manifest2).map((repo) => ({
|
|
110
|
+
...repo,
|
|
111
|
+
pkg: JSON.parse(readFileSync(join(repo.dir, "package.json"), "utf8"))
|
|
112
|
+
}));
|
|
113
|
+
const names = new Set(repos.map((repo) => repo.name));
|
|
114
|
+
const dependsOn = /* @__PURE__ */ new Map();
|
|
115
|
+
for (const repo of repos) {
|
|
116
|
+
const edges = /* @__PURE__ */ new Set();
|
|
117
|
+
for (const field of DEP_FIELDS) {
|
|
118
|
+
for (const dep of Object.keys(repo.pkg[field] ?? {})) {
|
|
119
|
+
if (names.has(dep) && dep !== repo.name) edges.add(dep);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
dependsOn.set(repo.name, edges);
|
|
123
|
+
}
|
|
124
|
+
const order = [];
|
|
125
|
+
const done = /* @__PURE__ */ new Set();
|
|
126
|
+
while (done.size < repos.length) {
|
|
127
|
+
const ready = repos.filter(
|
|
128
|
+
(repo) => !done.has(repo.name) && [...dependsOn.get(repo.name)].every((dep) => done.has(dep))
|
|
129
|
+
).sort((a, b) => a.name.localeCompare(b.name));
|
|
130
|
+
if (ready.length === 0) throw new Error("dependency cycle among ecosystem repos");
|
|
131
|
+
for (const repo of ready) {
|
|
132
|
+
order.push(repo);
|
|
133
|
+
done.add(repo.name);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
return order;
|
|
137
|
+
};
|
|
138
|
+
var writeManifest = (manifest2) => writeFileSync(join(packageRoot, "versions.json"), `${JSON.stringify(manifest2, null, 2)}
|
|
139
|
+
`);
|
|
140
|
+
var discoverRepos = (root, manifest2) => {
|
|
141
|
+
const repos = [];
|
|
142
|
+
for (const entry of readdirSync(root)) {
|
|
143
|
+
const pkgPath = join(root, entry, "package.json");
|
|
144
|
+
if (!existsSync(pkgPath)) continue;
|
|
145
|
+
const name = tryParseJsonc(readFileSync(pkgPath, "utf8"))?.name;
|
|
146
|
+
if (typeof name === "string" && name in manifest2.ecosystem)
|
|
147
|
+
repos.push({ dir: join(root, entry), name });
|
|
148
|
+
}
|
|
149
|
+
return repos.sort((a, b) => a.name.localeCompare(b.name));
|
|
150
|
+
};
|
|
151
|
+
var detectPreset = (pkg, override) => {
|
|
152
|
+
if (override) return override;
|
|
153
|
+
const hasReact = DEP_FIELDS.some((field) => pkg[field]?.react !== void 0);
|
|
154
|
+
return hasReact ? "react" : "base";
|
|
155
|
+
};
|
|
156
|
+
function inspect(dir2, manifest2, presetOverride) {
|
|
157
|
+
const findings = [];
|
|
158
|
+
const pkgPath = join(dir2, "package.json");
|
|
159
|
+
if (!existsSync(pkgPath)) {
|
|
160
|
+
return {
|
|
161
|
+
findings: [{ level: "error", message: `no package.json in ${dir2}` }],
|
|
162
|
+
flush: () => false
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
const pkg = JSON.parse(readFileSync(pkgPath, "utf8"));
|
|
166
|
+
let pkgDirty = false;
|
|
167
|
+
const touch = () => {
|
|
168
|
+
pkgDirty = true;
|
|
169
|
+
};
|
|
170
|
+
for (const [name, pin] of Object.entries(manifest2.toolchain)) {
|
|
171
|
+
let present = false;
|
|
172
|
+
for (const field of DEP_FIELDS) {
|
|
173
|
+
const deps = pkg[field];
|
|
174
|
+
if (!deps?.[name]) continue;
|
|
175
|
+
present = true;
|
|
176
|
+
if (field === "peerDependencies") continue;
|
|
177
|
+
if (deps[name] !== pin) {
|
|
178
|
+
findings.push({
|
|
179
|
+
level: "error",
|
|
180
|
+
message: `${name} ${deps[name]} \u2192 ${pin} (${field})`,
|
|
181
|
+
fix: () => {
|
|
182
|
+
deps[name] = pin;
|
|
183
|
+
touch();
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
if (!present && manifest2.required.includes(name)) {
|
|
189
|
+
findings.push({
|
|
190
|
+
level: "error",
|
|
191
|
+
message: `${name} missing \u2192 devDependency ${pin}`,
|
|
192
|
+
fix: () => {
|
|
193
|
+
pkg.devDependencies ??= {};
|
|
194
|
+
pkg.devDependencies[name] = pin;
|
|
195
|
+
touch();
|
|
196
|
+
}
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
for (const field of DEP_FIELDS) {
|
|
201
|
+
const deps = pkg[field];
|
|
202
|
+
if (!deps) continue;
|
|
203
|
+
for (const [name, range] of Object.entries(deps)) {
|
|
204
|
+
if (range !== "latest") continue;
|
|
205
|
+
const pin = manifest2.toolchain[name];
|
|
206
|
+
findings.push({
|
|
207
|
+
level: "error",
|
|
208
|
+
message: `${name} pinned to "latest" in ${field}${pin ? ` \u2192 ${pin}` : " \u2192 pin a version"}`,
|
|
209
|
+
fix: pin ? () => {
|
|
210
|
+
deps[name] = pin;
|
|
211
|
+
touch();
|
|
212
|
+
} : void 0
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
for (const [script, fallback] of Object.entries(REQUIRED_SCRIPTS)) {
|
|
217
|
+
if (pkg.scripts?.[script]) continue;
|
|
218
|
+
findings.push({
|
|
219
|
+
level: "error",
|
|
220
|
+
message: `scripts.${script} missing \u2192 "${fallback}"`,
|
|
221
|
+
fix: () => {
|
|
222
|
+
pkg.scripts ??= {};
|
|
223
|
+
pkg.scripts[script] = fallback;
|
|
224
|
+
touch();
|
|
225
|
+
}
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
if (pkg.name !== "@inixiative/config" && !DEP_FIELDS.some((field) => pkg[field]?.["@inixiative/config"])) {
|
|
229
|
+
findings.push({
|
|
230
|
+
level: "error",
|
|
231
|
+
message: `@inixiative/config missing \u2192 devDependency ^${ownVersion()} (extends cannot resolve without it)`,
|
|
232
|
+
fix: () => {
|
|
233
|
+
pkg.devDependencies ??= {};
|
|
234
|
+
pkg.devDependencies["@inixiative/config"] = `^${ownVersion()}`;
|
|
235
|
+
touch();
|
|
236
|
+
}
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
const expectedPm = `bun@${manifest2.bun}`;
|
|
240
|
+
if (pkg.packageManager !== expectedPm) {
|
|
241
|
+
findings.push({
|
|
242
|
+
level: "error",
|
|
243
|
+
message: `packageManager ${pkg.packageManager ?? "(unset)"} \u2192 ${expectedPm}`,
|
|
244
|
+
fix: () => {
|
|
245
|
+
pkg.packageManager = expectedPm;
|
|
246
|
+
touch();
|
|
247
|
+
}
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
const bunVersionPath = join(dir2, ".bun-version");
|
|
251
|
+
const bunVersion = existsSync(bunVersionPath) ? readFileSync(bunVersionPath, "utf8").trim() : null;
|
|
252
|
+
if (bunVersion !== manifest2.bun) {
|
|
253
|
+
findings.push({
|
|
254
|
+
level: "error",
|
|
255
|
+
message: `.bun-version ${bunVersion ?? "(missing)"} \u2192 ${manifest2.bun}`,
|
|
256
|
+
fix: () => writeFileSync(bunVersionPath, `${manifest2.bun}
|
|
257
|
+
`)
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
const lockbPath = join(dir2, "bun.lockb");
|
|
261
|
+
if (existsSync(lockbPath)) {
|
|
262
|
+
findings.push({
|
|
263
|
+
level: "error",
|
|
264
|
+
message: "legacy binary bun.lockb \u2192 delete and re-lock as bun.lock",
|
|
265
|
+
fix: () => rmSync(lockbPath)
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
const lockPath = join(dir2, "bun.lock");
|
|
269
|
+
const lockText = existsSync(lockPath) ? readFileSync(lockPath, "utf8") : null;
|
|
270
|
+
if (existsSync(join(dir2, ".git"))) {
|
|
271
|
+
if (!lockText) {
|
|
272
|
+
findings.push({
|
|
273
|
+
level: "error",
|
|
274
|
+
message: "bun.lock missing \u2192 run bun install and commit the lockfile"
|
|
275
|
+
});
|
|
276
|
+
} else if (spawnSync("git", ["check-ignore", "-q", "bun.lock"], { cwd: dir2 }).status === 0) {
|
|
277
|
+
findings.push({
|
|
278
|
+
level: "error",
|
|
279
|
+
message: "bun.lock is gitignored \u2192 stop ignoring it and commit the lockfile"
|
|
280
|
+
});
|
|
281
|
+
} else if (spawnSync("git", ["ls-files", "--error-unmatch", "bun.lock"], { cwd: dir2 }).status !== 0) {
|
|
282
|
+
findings.push({
|
|
283
|
+
level: "error",
|
|
284
|
+
message: "bun.lock untracked \u2192 commit the lockfile"
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
for (const [name, blessed] of Object.entries(manifest2.ecosystem)) {
|
|
289
|
+
if (name === pkg.name) continue;
|
|
290
|
+
for (const field of DEP_FIELDS) {
|
|
291
|
+
const deps = pkg[field];
|
|
292
|
+
const range = deps?.[name];
|
|
293
|
+
if (!range) continue;
|
|
294
|
+
const ok = admits(range, blessed);
|
|
295
|
+
if (ok === false) {
|
|
296
|
+
findings.push({
|
|
297
|
+
level: "error",
|
|
298
|
+
kind: "ecosystem-range",
|
|
299
|
+
name,
|
|
300
|
+
message: `${name} ${field} range ${range} does not admit blessed ${blessed} \u2192 ^${blessed}`,
|
|
301
|
+
fix: () => {
|
|
302
|
+
deps[name] = `^${blessed}`;
|
|
303
|
+
touch();
|
|
304
|
+
}
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
if (ok === null) {
|
|
308
|
+
findings.push({
|
|
309
|
+
level: "warn",
|
|
310
|
+
message: `${name} range ${range} not understood; verify manually against ${blessed}`
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
const installed = pkg.dependencies?.[name] ?? pkg.devDependencies?.[name];
|
|
315
|
+
if (lockText && installed) {
|
|
316
|
+
const locked = lockedVersion(lockText, name);
|
|
317
|
+
if (locked && admits(installed, locked) === false) {
|
|
318
|
+
findings.push({
|
|
319
|
+
level: "error",
|
|
320
|
+
kind: "stale-lock",
|
|
321
|
+
name,
|
|
322
|
+
message: `${name} locked at ${locked}, violating declared ${installed} (stale lockfile) \u2192 re-lock`
|
|
323
|
+
});
|
|
324
|
+
} else if (locked && locked !== blessed) {
|
|
325
|
+
findings.push({
|
|
326
|
+
level: "error",
|
|
327
|
+
kind: "stale-lock",
|
|
328
|
+
name,
|
|
329
|
+
message: `${name} locked at ${locked}, blessed is ${blessed} \u2192 re-lock`
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
const preset = detectPreset(pkg, presetOverride);
|
|
335
|
+
const tsconfigTarget = `@inixiative/config/tsconfig/${preset}.json`;
|
|
336
|
+
const tsconfigPath = join(dir2, "tsconfig.json");
|
|
337
|
+
if (!existsSync(tsconfigPath)) {
|
|
338
|
+
findings.push({
|
|
339
|
+
level: "error",
|
|
340
|
+
message: `tsconfig.json missing \u2192 stub extending ${tsconfigTarget}`,
|
|
341
|
+
fix: () => writeFileSync(
|
|
342
|
+
tsconfigPath,
|
|
343
|
+
`${JSON.stringify({ extends: tsconfigTarget, exclude: ["dist"] }, null, 2)}
|
|
344
|
+
`
|
|
345
|
+
)
|
|
346
|
+
});
|
|
347
|
+
} else {
|
|
348
|
+
const tsconfig = tryParseJsonc(readFileSync(tsconfigPath, "utf8")) ?? {};
|
|
349
|
+
const extendsValue = tsconfig.extends;
|
|
350
|
+
if (typeof extendsValue !== "string" || !extendsValue.startsWith("@inixiative/config/tsconfig/")) {
|
|
351
|
+
findings.push({
|
|
352
|
+
level: "error",
|
|
353
|
+
message: `tsconfig.json extends ${typeof extendsValue === "string" ? extendsValue : "(none)"} \u2192 ${tsconfigTarget}; local compilerOptions retained as overrides, review them`,
|
|
354
|
+
fix: () => {
|
|
355
|
+
const { extends: _replaced, ...rest } = tsconfig;
|
|
356
|
+
const next = { extends: tsconfigTarget, ...rest };
|
|
357
|
+
writeFileSync(tsconfigPath, `${JSON.stringify(next, null, 2)}
|
|
358
|
+
`);
|
|
359
|
+
}
|
|
360
|
+
});
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
const biomeTargets = preset === "react" ? ["@inixiative/config/biome/base.json", "@inixiative/config/biome/react.json"] : ["@inixiative/config/biome/base.json"];
|
|
364
|
+
const biomePath = join(dir2, "biome.json");
|
|
365
|
+
if (!existsSync(biomePath)) {
|
|
366
|
+
findings.push({
|
|
367
|
+
level: "error",
|
|
368
|
+
message: `biome.json missing \u2192 stub extending ${biomeTargets.join(" + ")}`,
|
|
369
|
+
fix: () => writeFileSync(biomePath, `${JSON.stringify({ extends: biomeTargets }, null, 2)}
|
|
370
|
+
`)
|
|
371
|
+
});
|
|
372
|
+
} else {
|
|
373
|
+
const biome = tryParseJsonc(readFileSync(biomePath, "utf8")) ?? {};
|
|
374
|
+
const extendsValue = Array.isArray(biome.extends) ? biome.extends : [];
|
|
375
|
+
const missing = biomeTargets.filter((target) => !extendsValue.includes(target));
|
|
376
|
+
if (missing.length > 0) {
|
|
377
|
+
findings.push({
|
|
378
|
+
level: "error",
|
|
379
|
+
message: `biome.json missing extends ${missing.join(", ")}; local rules retained as overrides, review them`,
|
|
380
|
+
fix: () => {
|
|
381
|
+
const next = { ...biome, extends: biomeTargets };
|
|
382
|
+
writeFileSync(biomePath, `${JSON.stringify(next, null, 2)}
|
|
383
|
+
`);
|
|
384
|
+
}
|
|
385
|
+
});
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
return {
|
|
389
|
+
findings,
|
|
390
|
+
flush: () => {
|
|
391
|
+
if (!pkgDirty) return false;
|
|
392
|
+
writeFileSync(pkgPath, `${JSON.stringify(pkg, null, 2)}
|
|
393
|
+
`);
|
|
394
|
+
return true;
|
|
395
|
+
}
|
|
396
|
+
};
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
// src/cli.ts
|
|
400
|
+
var args = process.argv.slice(2);
|
|
401
|
+
var command = args[0];
|
|
402
|
+
var flags = new Set(args.filter((arg) => arg.startsWith("--")).map((arg) => arg.split("=")[0]));
|
|
403
|
+
var presetFlag = args.find((arg) => arg.startsWith("--preset="))?.split("=")[1];
|
|
404
|
+
var positional = args.slice(1).filter((arg) => !arg.startsWith("--"));
|
|
405
|
+
var dir = resolve(positional[0] ?? ".");
|
|
406
|
+
var usage = () => {
|
|
407
|
+
console.log(
|
|
408
|
+
"Usage: inixiative-config <check|sync|scan|train> [dir] [--preset=base|node|react] [--force] [--no-install]"
|
|
409
|
+
);
|
|
410
|
+
process.exit(2);
|
|
411
|
+
};
|
|
412
|
+
if (!["check", "sync", "scan", "train"].includes(command)) usage();
|
|
413
|
+
if (presetFlag && !["base", "node", "react"].includes(presetFlag)) usage();
|
|
414
|
+
var manifest = loadManifest();
|
|
415
|
+
var report = (findings) => {
|
|
416
|
+
for (const finding of findings) {
|
|
417
|
+
console.log(`${finding.level === "error" ? "\u2717" : "\u26A0"} ${finding.message}`);
|
|
418
|
+
}
|
|
419
|
+
const errors = findings.filter((finding) => finding.level === "error").length;
|
|
420
|
+
const warnings = findings.length - errors;
|
|
421
|
+
console.log(
|
|
422
|
+
findings.length === 0 ? "\u2713 in sync" : `${errors} error${errors === 1 ? "" : "s"}, ${warnings} warning${warnings === 1 ? "" : "s"}`
|
|
423
|
+
);
|
|
424
|
+
return errors;
|
|
425
|
+
};
|
|
426
|
+
if (process.versions.bun && process.versions.bun !== manifest.bun) {
|
|
427
|
+
console.log(`\u26A0 running bun ${process.versions.bun}, blessed is ${manifest.bun} (bun upgrade)`);
|
|
428
|
+
}
|
|
429
|
+
if (command === "check") {
|
|
430
|
+
const { findings } = inspect(dir, manifest, presetFlag);
|
|
431
|
+
process.exit(report(findings) > 0 ? 1 : 0);
|
|
432
|
+
}
|
|
433
|
+
if (command === "scan") {
|
|
434
|
+
const repos = discoverRepos(dir, manifest);
|
|
435
|
+
if (repos.length === 0) {
|
|
436
|
+
console.error(`\u2717 no ecosystem repos found under ${dir}`);
|
|
437
|
+
process.exit(1);
|
|
438
|
+
}
|
|
439
|
+
const drifted = [];
|
|
440
|
+
for (const repo of repos) {
|
|
441
|
+
console.log(`
|
|
442
|
+
${repo.name} \u2014 ${repo.dir}`);
|
|
443
|
+
if (report(inspect(repo.dir, manifest).findings) > 0) drifted.push(repo.name);
|
|
444
|
+
}
|
|
445
|
+
console.log(
|
|
446
|
+
`
|
|
447
|
+
scanned ${repos.length} repos: ${drifted.length === 0 ? "all in sync" : `${drifted.length} drifted (${drifted.join(", ")})`}`
|
|
448
|
+
);
|
|
449
|
+
process.exit(drifted.length > 0 ? 1 : 0);
|
|
450
|
+
}
|
|
451
|
+
if (command === "train") {
|
|
452
|
+
const order = topoOrder(dir, manifest);
|
|
453
|
+
if (order.length === 0) {
|
|
454
|
+
console.error(`\u2717 no ecosystem repos found under ${dir}`);
|
|
455
|
+
process.exit(1);
|
|
456
|
+
}
|
|
457
|
+
console.log(`train order: ${order.map((repo) => repo.name).join(" \u2192 ")}`);
|
|
458
|
+
const published = [];
|
|
459
|
+
const skipped = [];
|
|
460
|
+
let bomDirty = false;
|
|
461
|
+
for (const repo of order) {
|
|
462
|
+
console.log(`
|
|
463
|
+
\u25B8 ${repo.name} \u2014 ${repo.dir}`);
|
|
464
|
+
const porcelain = spawnSync2("git", ["status", "--porcelain"], {
|
|
465
|
+
cwd: repo.dir,
|
|
466
|
+
encoding: "utf8"
|
|
467
|
+
});
|
|
468
|
+
if (porcelain.status !== 0) {
|
|
469
|
+
console.log("\u26A0 not a git checkout \u2014 skipping");
|
|
470
|
+
skipped.push(repo.name);
|
|
471
|
+
continue;
|
|
472
|
+
}
|
|
473
|
+
if (porcelain.stdout.trim().length > 0) {
|
|
474
|
+
console.log("\u26A0 dirty working tree \u2014 commit or stash your work first, skipping");
|
|
475
|
+
skipped.push(repo.name);
|
|
476
|
+
continue;
|
|
477
|
+
}
|
|
478
|
+
spawnSync2("git", ["fetch", "--quiet"], { cwd: repo.dir });
|
|
479
|
+
const behind = spawnSync2("git", ["rev-list", "--count", "HEAD..@{upstream}"], {
|
|
480
|
+
cwd: repo.dir,
|
|
481
|
+
encoding: "utf8"
|
|
482
|
+
});
|
|
483
|
+
if (behind.status === 0 && Number(behind.stdout.trim()) > 0) {
|
|
484
|
+
console.log("\u26A0 behind origin \u2014 pull first, skipping");
|
|
485
|
+
skipped.push(repo.name);
|
|
486
|
+
continue;
|
|
487
|
+
}
|
|
488
|
+
const inspection = inspect(repo.dir, manifest);
|
|
489
|
+
const bumps = inspection.findings.filter((finding) => finding.kind === "ecosystem-range");
|
|
490
|
+
for (const bump of bumps) {
|
|
491
|
+
console.log(` ${bump.message}`);
|
|
492
|
+
bump.fix?.();
|
|
493
|
+
}
|
|
494
|
+
inspection.flush();
|
|
495
|
+
let touched = bumps.length > 0;
|
|
496
|
+
const remote = spawnSync2("npm", ["view", `${repo.name}@latest`, "version"], {
|
|
497
|
+
encoding: "utf8"
|
|
498
|
+
});
|
|
499
|
+
const remoteVersion = remote.status === 0 ? remote.stdout.trim() : null;
|
|
500
|
+
const localVersion = repo.pkg.version ?? "0.0.0";
|
|
501
|
+
const ahead = remoteVersion === null || compare(localVersion, remoteVersion) > 0;
|
|
502
|
+
if (remoteVersion && compare(localVersion, remoteVersion) < 0) {
|
|
503
|
+
console.log(`\u26A0 local ${localVersion} is behind npm ${remoteVersion} \u2014 pull first, skipping`);
|
|
504
|
+
skipped.push(repo.name);
|
|
505
|
+
continue;
|
|
506
|
+
}
|
|
507
|
+
if (touched || ahead) {
|
|
508
|
+
const install = spawnSync2("bun", ["install"], { cwd: repo.dir, stdio: "inherit" });
|
|
509
|
+
if (install.status !== 0) {
|
|
510
|
+
console.error("\u2717 bun install failed \u2014 aborting train");
|
|
511
|
+
process.exit(1);
|
|
512
|
+
}
|
|
513
|
+
const stale = inspect(repo.dir, manifest).findings.filter((finding) => finding.kind === "stale-lock" && finding.name).map((finding) => finding.name);
|
|
514
|
+
if (stale.length > 0) {
|
|
515
|
+
spawnSync2("bun", ["update", ...new Set(stale)], { cwd: repo.dir, stdio: "inherit" });
|
|
516
|
+
touched = true;
|
|
517
|
+
}
|
|
518
|
+
const check = spawnSync2("bun", ["run", "check"], { cwd: repo.dir, stdio: "inherit" });
|
|
519
|
+
if (check.status !== 0) {
|
|
520
|
+
console.error(`\u2717 check failed in ${repo.name} \u2014 aborting train`);
|
|
521
|
+
process.exit(1);
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
if (touched) {
|
|
525
|
+
spawnSync2("git", ["add", "package.json", "bun.lock"], { cwd: repo.dir });
|
|
526
|
+
const commit = spawnSync2(
|
|
527
|
+
"git",
|
|
528
|
+
["commit", "-m", "chore: sync ecosystem deps to blessed set"],
|
|
529
|
+
{ cwd: repo.dir, stdio: "inherit" }
|
|
530
|
+
);
|
|
531
|
+
if (commit.status !== 0) {
|
|
532
|
+
console.error(`\u2717 commit failed in ${repo.name} \u2014 aborting train`);
|
|
533
|
+
process.exit(1);
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
if (ahead) {
|
|
537
|
+
console.log(`publishing ${repo.name}@${localVersion} (npm has ${remoteVersion ?? "none"})`);
|
|
538
|
+
const publish = spawnSync2("npm", ["publish"], { cwd: repo.dir, stdio: "inherit" });
|
|
539
|
+
if (publish.status !== 0) {
|
|
540
|
+
console.error(`\u2717 publish failed for ${repo.name} \u2014 aborting train`);
|
|
541
|
+
process.exit(1);
|
|
542
|
+
}
|
|
543
|
+
manifest.ecosystem[repo.name] = localVersion;
|
|
544
|
+
writeManifest(manifest);
|
|
545
|
+
bomDirty = true;
|
|
546
|
+
published.push(`${repo.name}@${localVersion}`);
|
|
547
|
+
} else {
|
|
548
|
+
console.log(`\u2713 ${repo.name}@${localVersion} already on npm`);
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
console.log(`
|
|
552
|
+
published: ${published.length === 0 ? "nothing" : published.join(", ")}`);
|
|
553
|
+
if (skipped.length > 0) console.log(`skipped: ${skipped.join(", ")}`);
|
|
554
|
+
if (bomDirty)
|
|
555
|
+
console.log("BOM updated in versions.json \u2014 commit @inixiative/config and publish it");
|
|
556
|
+
console.log("train does not push \u2014 review the commits it made, then push each repo");
|
|
557
|
+
process.exit(0);
|
|
558
|
+
}
|
|
559
|
+
if (existsSync2(join2(dir, ".git")) && !flags.has("--force")) {
|
|
560
|
+
const status = spawnSync2("git", ["status", "--porcelain"], { cwd: dir, encoding: "utf8" });
|
|
561
|
+
if (status.status === 0 && status.stdout.trim().length > 0) {
|
|
562
|
+
console.error("\u2717 working tree dirty \u2014 commit first or pass --force");
|
|
563
|
+
process.exit(1);
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
var first = inspect(dir, manifest, presetFlag);
|
|
567
|
+
for (const finding of first.findings) finding.fix?.();
|
|
568
|
+
first.flush();
|
|
569
|
+
var fixed = first.findings.filter((finding) => finding.fix).length;
|
|
570
|
+
if (fixed > 0) console.log(`applied ${fixed} fix${fixed === 1 ? "" : "es"}`);
|
|
571
|
+
if (!flags.has("--no-install")) {
|
|
572
|
+
const install = spawnSync2("bun", ["install"], { cwd: dir, stdio: "inherit" });
|
|
573
|
+
if (install.status !== 0) {
|
|
574
|
+
console.error("\u2717 bun install failed");
|
|
575
|
+
process.exit(1);
|
|
576
|
+
}
|
|
577
|
+
const stale = inspect(dir, manifest, presetFlag).findings.filter((finding) => finding.kind === "stale-lock" && finding.name).map((finding) => finding.name);
|
|
578
|
+
if (stale.length > 0) {
|
|
579
|
+
const update = spawnSync2("bun", ["update", ...new Set(stale)], { cwd: dir, stdio: "inherit" });
|
|
580
|
+
if (update.status !== 0) {
|
|
581
|
+
console.error("\u2717 bun update failed");
|
|
582
|
+
process.exit(1);
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
var final = inspect(dir, manifest, presetFlag);
|
|
587
|
+
process.exit(report(final.findings) > 0 ? 1 : 0);
|
package/dist/tsup.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { Options } from 'tsup';
|
|
2
|
+
|
|
3
|
+
declare const node: (options?: Options) => Options | Options[] | ((overrideOptions: Options) => Options | Options[] | Promise<Options | Options[]>);
|
|
4
|
+
declare const react: (options?: Options) => Options | Options[] | ((overrideOptions: Options) => Options | Options[] | Promise<Options | Options[]>);
|
|
5
|
+
|
|
6
|
+
export { node, react };
|
package/dist/tsup.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// src/tsup.ts
|
|
2
|
+
import { defineConfig } from "tsup";
|
|
3
|
+
var base = {
|
|
4
|
+
entry: ["src/index.ts"],
|
|
5
|
+
format: ["cjs", "esm"],
|
|
6
|
+
dts: { compilerOptions: { ignoreDeprecations: "6.0" } },
|
|
7
|
+
splitting: false,
|
|
8
|
+
sourcemap: true,
|
|
9
|
+
clean: true
|
|
10
|
+
};
|
|
11
|
+
var node = (options = {}) => defineConfig({ ...base, ...options });
|
|
12
|
+
var react = (options = {}) => {
|
|
13
|
+
const external = Array.isArray(options.external) ? options.external : [];
|
|
14
|
+
return defineConfig({
|
|
15
|
+
...base,
|
|
16
|
+
...options,
|
|
17
|
+
external: [.../* @__PURE__ */ new Set(["react", "react-dom", "react/jsx-runtime", ...external])]
|
|
18
|
+
});
|
|
19
|
+
};
|
|
20
|
+
export {
|
|
21
|
+
node,
|
|
22
|
+
react
|
|
23
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@inixiative/config",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Shared toolchain for the inixiative ecosystem: tsconfig/biome/tsup presets, a version manifest (BOM), and a sync/check CLI",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"author": "Aron Greenspan",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "https://github.com/inixiative/config"
|
|
11
|
+
},
|
|
12
|
+
"publishConfig": {
|
|
13
|
+
"access": "public"
|
|
14
|
+
},
|
|
15
|
+
"bin": {
|
|
16
|
+
"inixiative-config": "./dist/cli.js"
|
|
17
|
+
},
|
|
18
|
+
"files": [
|
|
19
|
+
"dist",
|
|
20
|
+
"tsconfig",
|
|
21
|
+
"biome",
|
|
22
|
+
"versions.json",
|
|
23
|
+
"README.md"
|
|
24
|
+
],
|
|
25
|
+
"exports": {
|
|
26
|
+
"./versions.json": "./versions.json",
|
|
27
|
+
"./tsconfig/base.json": "./tsconfig/base.json",
|
|
28
|
+
"./tsconfig/node.json": "./tsconfig/node.json",
|
|
29
|
+
"./tsconfig/react.json": "./tsconfig/react.json",
|
|
30
|
+
"./biome/base.json": "./biome/base.json",
|
|
31
|
+
"./biome/react.json": "./biome/react.json",
|
|
32
|
+
"./tsup": {
|
|
33
|
+
"types": "./dist/tsup.d.ts",
|
|
34
|
+
"import": "./dist/tsup.js"
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
"scripts": {
|
|
38
|
+
"build": "tsup",
|
|
39
|
+
"test": "bun test",
|
|
40
|
+
"typecheck": "tsc --noEmit",
|
|
41
|
+
"lint": "biome check .",
|
|
42
|
+
"format": "biome format --write .",
|
|
43
|
+
"check": "bun run typecheck && bun run lint && bun run test",
|
|
44
|
+
"prepublishOnly": "bun run check && bun run build"
|
|
45
|
+
},
|
|
46
|
+
"packageManager": "bun@1.3.14",
|
|
47
|
+
"devDependencies": {
|
|
48
|
+
"@biomejs/biome": "2.5.2",
|
|
49
|
+
"@types/bun": "1.3.14",
|
|
50
|
+
"tsup": "8.5.1",
|
|
51
|
+
"typescript": "6.0.3"
|
|
52
|
+
},
|
|
53
|
+
"peerDependencies": {
|
|
54
|
+
"@biomejs/biome": ">=2.5.0",
|
|
55
|
+
"tsup": ">=8.0.0",
|
|
56
|
+
"typescript": ">=5.9.0"
|
|
57
|
+
},
|
|
58
|
+
"peerDependenciesMeta": {
|
|
59
|
+
"@biomejs/biome": {
|
|
60
|
+
"optional": true
|
|
61
|
+
},
|
|
62
|
+
"tsup": {
|
|
63
|
+
"optional": true
|
|
64
|
+
},
|
|
65
|
+
"typescript": {
|
|
66
|
+
"optional": true
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"lib": ["ESNext"],
|
|
4
|
+
"types": ["bun"],
|
|
5
|
+
"target": "ESNext",
|
|
6
|
+
"module": "ESNext",
|
|
7
|
+
"moduleDetection": "force",
|
|
8
|
+
"allowJs": true,
|
|
9
|
+
"moduleResolution": "bundler",
|
|
10
|
+
"allowImportingTsExtensions": true,
|
|
11
|
+
"verbatimModuleSyntax": true,
|
|
12
|
+
"noEmit": true,
|
|
13
|
+
"strict": true,
|
|
14
|
+
"skipLibCheck": true,
|
|
15
|
+
"noFallthroughCasesInSwitch": true,
|
|
16
|
+
"noUnusedLocals": false,
|
|
17
|
+
"noUnusedParameters": false,
|
|
18
|
+
"noPropertyAccessFromIndexSignature": false
|
|
19
|
+
}
|
|
20
|
+
}
|
package/versions.json
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"bun": "1.3.14",
|
|
3
|
+
"toolchain": {
|
|
4
|
+
"@biomejs/biome": "2.5.2",
|
|
5
|
+
"typescript": "6.0.3",
|
|
6
|
+
"tsup": "8.5.1",
|
|
7
|
+
"@types/bun": "1.3.14"
|
|
8
|
+
},
|
|
9
|
+
"required": ["@biomejs/biome", "typescript", "@types/bun"],
|
|
10
|
+
"ecosystem": {
|
|
11
|
+
"@inixiative/json-rules": "2.12.1",
|
|
12
|
+
"@inixiative/permissions": "0.3.1",
|
|
13
|
+
"@inixiative/transitions": "0.0.3",
|
|
14
|
+
"@inixiative/rules-builder": "0.13.0",
|
|
15
|
+
"@inixiative/prisma-map": "0.1.0",
|
|
16
|
+
"@inixiative/atlas": "0.1.0"
|
|
17
|
+
}
|
|
18
|
+
}
|