create-geonosis 2.10.0 → 2.11.1
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/CHANGELOG.md +63 -0
- package/README.md +17 -13
- package/bin/create-geonosis.mjs +164 -17
- package/dist/{chunk-5AQ755XJ.js → chunk-YF4AX76D.js} +55 -38
- package/dist/create-geonosis-cli.js +5 -4
- package/dist/index.d.ts +4 -4
- package/dist/index.js +1 -1
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,68 @@
|
|
|
1
1
|
# create-geonosis
|
|
2
2
|
|
|
3
|
+
## 2.11.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Updated dependencies [08611c4]
|
|
8
|
+
- @geonosis/cli@2.11.1
|
|
9
|
+
|
|
10
|
+
## 2.11.0
|
|
11
|
+
|
|
12
|
+
### Patch Changes
|
|
13
|
+
|
|
14
|
+
- 0f99e26: Every bin's stale-build guard now sees the workspace dependencies its build read. A `dist`
|
|
15
|
+
current against its own `src` could still have been built against an older dependency's `dist` —
|
|
16
|
+
literally, where tsup inlined a workspace devDependency into it — and a dependency whose own `src`
|
|
17
|
+
had moved past its `dist` was read stale by every bin above it; the guard answered "current" for
|
|
18
|
+
both. Now each shim holds every `workspace:` dependency, transitively, to the same reading as its
|
|
19
|
+
own package (never built, stale against its src, current), and refuses when this `dist` is older
|
|
20
|
+
than a dependency's — by content, so a dependency rebuilt to the same bytes with
|
|
21
|
+
`--force --filter` is the same build and does not poison the tree. Unknown is still stale: the
|
|
22
|
+
first run over a tree where a dependency was rebuilt alone, before any record, refuses once and
|
|
23
|
+
names `--force --filter=<this package>`. Only a checkout with `src/` beside `dist/` sees any of
|
|
24
|
+
this; the published package ships no `src/` and the guard is skipped. Measured: within noise for a
|
|
25
|
+
bin with one dependency, +8 ms for `geonosis`, which walks fourteen.
|
|
26
|
+
- 0f99e26: Every bin's stale-build guard decides, and never throws. The digest guard's refusal read
|
|
27
|
+
`package.json` to name `pnpm build --force --filter=<name>`; over a checkout with no manifest
|
|
28
|
+
beside the bin it ended with exit 1 and a `node:fs` frame instead of exit 2 and a sentence. The
|
|
29
|
+
name is now a courtesy the refusal does without. The contract, stated: a source newer by mtime whose
|
|
30
|
+
content the bin saw beside a current dist is not stale; one whose content differs is, and the
|
|
31
|
+
refusal names the fix; one the bin has no record for is stale — unknown is stale; a missing dist is
|
|
32
|
+
"never built". Each is exit 2 with a sentence. Only a checkout with `src/` beside `dist/` sees any
|
|
33
|
+
of this; the published package ships no `src/` and the guard is skipped.
|
|
34
|
+
- 0f99e26: The kit names no consumer. Docs, doc comments and messages describe the repos a pattern was measured in by their stack (a Medusa storefront, a Workers + D1 app, a Mercur v1 marketplace). Example configs are named the same way: `examples/medusa-storefront.*.json`, `examples/workers-app.*.json`. No behaviour changes.
|
|
35
|
+
- 0f99e26: `create-geonosis` refuses a flag it would have ignored. A misspelled flag written with `=`
|
|
36
|
+
(`--runtim=pnpm`) was taken and dropped, where the same flag written with a space was refused; and
|
|
37
|
+
`--presets` outside `--plan` was accepted and ignored, because a scaffold's presets follow from
|
|
38
|
+
`--backend` and `--ui`.
|
|
39
|
+
- 0f99e26: The `backend-sagaflow-cf` deprecation line says the old preset name resolves "until the next major", which is what the 2.3.0 changelog promised. It used to say "for one release", seven minor releases on. The retired-rule docs and RULES.md now use the same wording.
|
|
40
|
+
- 0f99e26: `--backend sagaflow-cf` now enables the `backend-workflows-cf` preset instead of the deprecated
|
|
41
|
+
`backend-sagaflow-cf` alias (#197 in the plugin) — a scaffolded repo's `.oxlintrc.json` no longer
|
|
42
|
+
goes stale the release the alias is dropped. Same rules today; no change for other backends.
|
|
43
|
+
- 0f99e26: A bun repo's scaffolded `LAW.md` says `bun run format` and `bun run format:check`; it said `pnpm`
|
|
44
|
+
whatever the runtime.
|
|
45
|
+
|
|
46
|
+
The README says the scaffold writes `LAW.md` with `CLAUDE.md` importing it, which it has since
|
|
47
|
+
D-054, and lists `--domains`, `--medusa`, `--check` and `--plan` in the flag table.
|
|
48
|
+
- 0f99e26: A scaffolded repo pins vitest `^4.1.11`, the major the kit itself runs; a test holds every catalog tool to the kit's own major.
|
|
49
|
+
- 0f99e26: Every order a tool writes, compares or applies — generated files, migration order, listings, cursors — sorts with the `en-US` collation, named rather than inherited from the machine. On an `en-US` machine nothing moves; on one set to another language (Estonian sorts `z` before `t`) the output is now the same one.
|
|
50
|
+
- Updated dependencies [0f99e26]
|
|
51
|
+
- Updated dependencies [0f99e26]
|
|
52
|
+
- Updated dependencies [0f99e26]
|
|
53
|
+
- Updated dependencies [0f99e26]
|
|
54
|
+
- Updated dependencies [0f99e26]
|
|
55
|
+
- Updated dependencies [0f99e26]
|
|
56
|
+
- Updated dependencies [0f99e26]
|
|
57
|
+
- Updated dependencies [0f99e26]
|
|
58
|
+
- Updated dependencies [0f99e26]
|
|
59
|
+
- Updated dependencies [0f99e26]
|
|
60
|
+
- Updated dependencies [0f99e26]
|
|
61
|
+
- Updated dependencies [0f99e26]
|
|
62
|
+
- Updated dependencies [0f99e26]
|
|
63
|
+
- Updated dependencies [0f99e26]
|
|
64
|
+
- @geonosis/cli@2.11.0
|
|
65
|
+
|
|
3
66
|
## 2.10.0
|
|
4
67
|
|
|
5
68
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -15,11 +15,15 @@ and a default borrowed from another repo would be that repo's stack hardcoded in
|
|
|
15
15
|
| `--runtime` | `pnpm` · `bun` |
|
|
16
16
|
| `--backend` | `medusa` · `sagaflow-cf` · `none` |
|
|
17
17
|
| `--ui` | `tiered-shadcn` · `atoms-only` · `none` |
|
|
18
|
+
| `--domains <a,b>` | the domains the app composes, written into its `src/platform.ts` |
|
|
19
|
+
| `--medusa <range>` | the Medusa a `--backend medusa` tree declares |
|
|
18
20
|
| `--brand <name>` | turns on `no-brand-names` for that word |
|
|
19
21
|
| `--themekit <name>` | consumes a themekit **by version**, never by copying it |
|
|
20
22
|
| `--marketplace <path>` | install the plugin from a local marketplace instead of GitHub |
|
|
21
23
|
| `--kit <path>` | read the kit from a workspace instead of the registry — a proof aid |
|
|
22
24
|
| `--no-install` | write the files and stop |
|
|
25
|
+
| `--check` | say what an existing repo lacks of what the scaffold writes today; write nothing |
|
|
26
|
+
| `--plan` | what adopting an existing repo would take, computed off its tree; write nothing |
|
|
23
27
|
|
|
24
28
|
## What it writes
|
|
25
29
|
|
|
@@ -34,7 +38,8 @@ and a default borrowed from another repo would be that repo's stack hardcoded in
|
|
|
34
38
|
`geonosis-ledger sync` generates turbo's `boundaries.tags` AND the linter's `layer-walls` layers
|
|
35
39
|
from it, so a tag added there reaches both gates and neither can drift from the other. The
|
|
36
40
|
scaffold runs both splices for you.
|
|
37
|
-
- **`
|
|
41
|
+
- **`LAW.md` under 60 lines**, with `CLAUDE.md` the one line that imports it, plus a
|
|
42
|
+
`docs/pipelines/` page template.
|
|
38
43
|
- **The five config files**: `geonosis.json` (verify tiers, ledger, walk, testbed, doctor),
|
|
39
44
|
`.oxlintrc.json` with the presets the answers imply, `geonosis.ratchet.json`, `gate-baseline.json`,
|
|
40
45
|
`.oxfmtrc.json`.
|
|
@@ -45,16 +50,16 @@ and a default borrowed from another repo would be that repo's stack hardcoded in
|
|
|
45
50
|
## The baseline, and the one number that is not zero
|
|
46
51
|
|
|
47
52
|
Every counter starts at **0**: a new repo has earned no debt. `lawLineCount` starts at the measured
|
|
48
|
-
length of the `
|
|
49
|
-
|
|
53
|
+
length of the `LAW.md` that was just written, because it counts something that already exists and a
|
|
54
|
+
0 there would fail the first `ratchet` run over a clean tree.
|
|
50
55
|
|
|
51
56
|
## `lint-corpus/` — the reach corpus a shipped one cannot be
|
|
52
57
|
|
|
53
58
|
`geonosis-doctor` asks whether every enabled rule can fire at all, against the corpus the **plugin**
|
|
54
|
-
ships. That
|
|
55
|
-
|
|
56
|
-
and
|
|
57
|
-
|
|
59
|
+
ships. That cannot answer for a rule whose reach is the repo's own vocabulary — its layer names, its
|
|
60
|
+
brand, its plugin package root, the `within` its sagas live in — and the plugin's corpus says `acme`
|
|
61
|
+
and `layers/core`. So the scaffold writes a small corpus in **this** repo's vocabulary, with a
|
|
62
|
+
`manifest.json` naming exactly the rules it is evidence about.
|
|
58
63
|
|
|
59
64
|
It is deliberately broken code, so it is excluded from the repo's own lint, format and typecheck —
|
|
60
65
|
the same treatment the plugin gives its own fixtures. The corpus run copies it to a temp root and
|
|
@@ -90,13 +95,12 @@ Two things are compared and two are deliberately not:
|
|
|
90
95
|
new repo compile, not a shape a repo must hold. Measured over a consumer: including them turned 24 real
|
|
91
96
|
findings into 52, thirty of them about a sample workspace that repo does not have.
|
|
92
97
|
|
|
93
|
-
Over
|
|
94
|
-
|
|
95
|
-
|
|
98
|
+
Over a repo scaffolded before D-054 moved the law it reads `LAW.md`, `geonosis.json → law.file`
|
|
99
|
+
and the rest of what the scaffold has learned since; `proofs/024-W31a-create-check/` has a whole run
|
|
100
|
+
and the before/after hash.
|
|
96
101
|
|
|
97
|
-
A JSON file the repo has that is not plain JSON —
|
|
98
|
-
|
|
99
|
-
over it.
|
|
102
|
+
A JSON file the repo has that is not plain JSON — a `tsconfig.json` with `//` comments, legal
|
|
103
|
+
JSONC — is reported rather than skipped. A check that could not read a file has not passed over it.
|
|
100
104
|
|
|
101
105
|
## Proven, not claimed
|
|
102
106
|
|
package/bin/create-geonosis.mjs
CHANGED
|
@@ -1,14 +1,24 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
import {
|
|
2
|
+
// Every packages/*/bin shim is packages/cli/bin/geonosis.mjs with its own name and entry: edit that
|
|
3
|
+
// one, then `pnpm generate:shims`. Committed so `pnpm install` can link the bin on a fresh clone,
|
|
4
|
+
// before `pnpm build` has made dist/. In the repo `src/` sits beside `dist/`, and a dist older
|
|
5
|
+
// than src answered for a fix it did not carry once (#62); the published package ships no src, so
|
|
6
|
+
// there the check is skipped.
|
|
7
|
+
import { createHash } from 'node:crypto'
|
|
8
|
+
import {
|
|
9
|
+
existsSync,
|
|
10
|
+
mkdirSync,
|
|
11
|
+
readdirSync,
|
|
12
|
+
readFileSync,
|
|
13
|
+
realpathSync,
|
|
14
|
+
statSync,
|
|
15
|
+
writeFileSync
|
|
16
|
+
} from 'node:fs'
|
|
8
17
|
import { dirname, join } from 'node:path'
|
|
9
18
|
import { fileURLToPath } from 'node:url'
|
|
10
19
|
|
|
11
20
|
const here = dirname(fileURLToPath(import.meta.url))
|
|
21
|
+
const own = join(here, '..')
|
|
12
22
|
// tsup never reads a test file, so one counting as source made every bin refuse a build that had
|
|
13
23
|
// not gone stale (#399).
|
|
14
24
|
const NOT_SOURCE = (name) =>
|
|
@@ -24,25 +34,162 @@ const newest = (dir, skip) =>
|
|
|
24
34
|
return Math.max(most, entry.isDirectory() ? newest(at, skip) : statSync(at).mtimeMs)
|
|
25
35
|
}, 0)
|
|
26
36
|
: 0
|
|
27
|
-
const
|
|
28
|
-
const
|
|
37
|
+
const digestOf = (dir, hash) => {
|
|
38
|
+
for (const name of readdirSync(dir).toSorted()) {
|
|
39
|
+
if (NOT_SOURCE(name)) continue
|
|
40
|
+
const at = join(dir, name)
|
|
41
|
+
if (statSync(at).isDirectory()) {
|
|
42
|
+
digestOf(at, hash)
|
|
43
|
+
continue
|
|
44
|
+
}
|
|
45
|
+
hash.update(name).update('\0').update(readFileSync(at)).update('\0')
|
|
46
|
+
}
|
|
47
|
+
return hash
|
|
48
|
+
}
|
|
49
|
+
const digest = (...dirs) => {
|
|
50
|
+
const hash = createHash('sha256')
|
|
51
|
+
for (const dir of dirs) if (existsSync(dir)) digestOf(dir, hash)
|
|
52
|
+
return hash.digest('hex')
|
|
53
|
+
}
|
|
54
|
+
// mtime says a source file was WRITTEN, not that it CHANGED. A checkout rewrites it with the same
|
|
55
|
+
// content, and the next `pnpm build` is a cache hit that leaves the current dist exactly as it
|
|
56
|
+
// was — so every bin refused with "run pnpm build", the action that had just changed nothing and
|
|
57
|
+
// would change nothing again. So a digest of the sources is kept from the last run that found
|
|
58
|
+
// dist current: the same digest later is the same sources, whatever their timestamps say, and a
|
|
59
|
+
// different one is the staleness this guard exists for. The newest mtime is kept beside it so a
|
|
60
|
+
// run over untouched sources need not hash them again. The second line is the same record for
|
|
61
|
+
// the dists of the workspace dependencies this build read (row 584, below).
|
|
62
|
+
const recordOf = (pkg) => join(pkg, '.turbo', 'geonosis-src-digest')
|
|
63
|
+
const recorded = (pkg) => {
|
|
64
|
+
try {
|
|
65
|
+
const [first = '', second = ''] = readFileSync(recordOf(pkg), 'utf8').split('\n')
|
|
66
|
+
const [seen = '', sum = ''] = first.trim().split(' ')
|
|
67
|
+
const [depsSeen = '', depsSum = ''] = second.trim().split(' ')
|
|
68
|
+
return { depsSeen: Number(depsSeen), depsSum, seen: Number(seen), sum }
|
|
69
|
+
} catch {
|
|
70
|
+
return { depsSeen: 0, depsSum: '', seen: 0, sum: '' }
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
const remember = (pkg, known) => {
|
|
74
|
+
try {
|
|
75
|
+
mkdirSync(dirname(recordOf(pkg)), { recursive: true })
|
|
76
|
+
writeFileSync(recordOf(pkg), `${known.seen} ${known.sum}\n${known.depsSeen} ${known.depsSum}\n`)
|
|
77
|
+
} catch {
|
|
78
|
+
// A record nothing could write costs one more hash next time, never a wrong answer.
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
// The refusal names a package for turbo's --filter. A manifest that cannot be read costs the
|
|
82
|
+
// name, never the decision: the first digest shim threw here — exit 1 and a node:fs frame — over a
|
|
83
|
+
// tree with no package.json, and a guard that throws neither answers nor refuses.
|
|
84
|
+
const manifestOf = (pkg) => {
|
|
85
|
+
try {
|
|
86
|
+
return JSON.parse(readFileSync(join(pkg, 'package.json'), 'utf8'))
|
|
87
|
+
} catch {
|
|
88
|
+
return {}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
const refuse = (sentence) => {
|
|
92
|
+
process.stderr.write(`create-geonosis: ${sentence}\n`)
|
|
93
|
+
process.exit(2)
|
|
94
|
+
}
|
|
95
|
+
// Whether one package's dist reflects its src: 'unbuilt', 'stale' or 'current'. The same reading
|
|
96
|
+
// for this package and for each workspace dependency, off that package's own record.
|
|
97
|
+
const standing = (pkg) => {
|
|
98
|
+
const src = join(pkg, 'src')
|
|
99
|
+
const dist = join(pkg, 'dist')
|
|
100
|
+
if (!existsSync(src)) return 'current'
|
|
101
|
+
const built = newest(dist)
|
|
102
|
+
if (built === 0) return 'unbuilt'
|
|
103
|
+
const written = newest(src, NOT_SOURCE)
|
|
104
|
+
const known = recorded(pkg)
|
|
105
|
+
if (written > built) {
|
|
106
|
+
const sum = known.seen === written ? known.sum : digest(src)
|
|
107
|
+
if (sum !== known.sum) return 'stale'
|
|
108
|
+
if (known.seen !== written) remember(pkg, { ...known, seen: written, sum })
|
|
109
|
+
} else if (known.seen !== written) {
|
|
110
|
+
remember(pkg, { ...known, seen: written, sum: digest(src) })
|
|
111
|
+
}
|
|
112
|
+
return 'current'
|
|
113
|
+
}
|
|
114
|
+
// Row 584: a dist current against its own src can still have been built against an OLDER
|
|
115
|
+
// dependency's dist — literally, when tsup inlined a workspace devDependency into it — and a
|
|
116
|
+
// dependency whose src has moved past its dist is read by this dist stale, inlined or imported.
|
|
117
|
+
// So every workspace dependency this manifest names (`workspace:`, resolved through the link pnpm
|
|
118
|
+
// made, transitively) is held to the same reading as this package, and this dist must not be
|
|
119
|
+
// older than any of theirs — by content: a dependency rebuilt to the same bytes is the same build,
|
|
120
|
+
// so the `--force --filter` escape below cannot poison the tree it was run in.
|
|
121
|
+
const workspaceDependencies = (pkg, found = new Map()) => {
|
|
122
|
+
const manifest = manifestOf(pkg)
|
|
123
|
+
for (const block of [
|
|
124
|
+
manifest.dependencies,
|
|
125
|
+
manifest.devDependencies,
|
|
126
|
+
manifest.optionalDependencies,
|
|
127
|
+
manifest.peerDependencies
|
|
128
|
+
]) {
|
|
129
|
+
for (const [name, range] of Object.entries(block ?? {})) {
|
|
130
|
+
if (typeof range !== 'string' || !range.startsWith('workspace:')) continue
|
|
131
|
+
let dir
|
|
132
|
+
try {
|
|
133
|
+
dir = realpathSync(join(pkg, 'node_modules', name))
|
|
134
|
+
} catch {
|
|
135
|
+
continue // Not linked: the import will say so far more loudly than this guard can.
|
|
136
|
+
}
|
|
137
|
+
if (found.has(dir)) continue
|
|
138
|
+
found.set(dir, name)
|
|
139
|
+
workspaceDependencies(dir, found)
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return found
|
|
143
|
+
}
|
|
29
144
|
// `newest` answers 0 for a directory that is NOT THERE and for one that is merely old, so the
|
|
30
145
|
// two conditions were one comparison and one sentence — and the staleness sentence was printed
|
|
31
146
|
// over trees that had never been built at all, blocking a turn in a fresh git worktree, which
|
|
32
147
|
// has no dist because dist is not committed (#357). Only the second is evidence of a mistake.
|
|
33
|
-
if (existsSync(src)) {
|
|
34
|
-
const built = newest(dist)
|
|
148
|
+
if (existsSync(join(own, 'src'))) {
|
|
149
|
+
const built = newest(join(own, 'dist'))
|
|
35
150
|
if (built === 0) {
|
|
36
|
-
|
|
37
|
-
'
|
|
151
|
+
refuse(
|
|
152
|
+
'this tree has never been built — run pnpm build. No dist here at all, which is what a fresh clone or a linked git worktree starts with; nothing is stale.'
|
|
38
153
|
)
|
|
39
|
-
process.exit(2)
|
|
40
154
|
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
155
|
+
const name = manifestOf(own).name ?? '<this package>'
|
|
156
|
+
if (standing(own) === 'stale') {
|
|
157
|
+
refuse(
|
|
158
|
+
`dist is older than src — run pnpm build before trusting this bin. If that build just said FULL TURBO, the cache hit left a current dist with its old timestamp and this bin had not yet recorded the sources it was current for: pnpm build --force --filter=${name} rebuilds this one package and clears it.`
|
|
44
159
|
)
|
|
45
|
-
|
|
160
|
+
}
|
|
161
|
+
const dependencies = workspaceDependencies(own)
|
|
162
|
+
const dists = new Map()
|
|
163
|
+
for (const [dir, dependency] of dependencies) {
|
|
164
|
+
const state = standing(dir)
|
|
165
|
+
if (state === 'unbuilt') {
|
|
166
|
+
refuse(
|
|
167
|
+
`${dependency}, which this dist reads, has never been built — run pnpm build. No dist there at all; nothing is stale.`
|
|
168
|
+
)
|
|
169
|
+
}
|
|
170
|
+
if (state === 'stale') {
|
|
171
|
+
refuse(
|
|
172
|
+
`${dependency}'s dist is older than its src, and this dist reads that dist — run pnpm build before trusting this bin. If that build just said FULL TURBO, the cache hit left that dist current with its old timestamp and nothing had yet recorded the sources it was current for: pnpm build --force --filter=${dependency} rebuilds that one package and clears it.`
|
|
173
|
+
)
|
|
174
|
+
}
|
|
175
|
+
dists.set(join(dir, 'dist'), dependency)
|
|
176
|
+
}
|
|
177
|
+
if (dists.size > 0) {
|
|
178
|
+
const known = recorded(own)
|
|
179
|
+
const theirs = [...dists.keys()].toSorted()
|
|
180
|
+
const depsNewest = theirs.reduce((most, dist) => Math.max(most, newest(dist)), 0)
|
|
181
|
+
if (depsNewest > built) {
|
|
182
|
+
const depsSum = known.depsSeen === depsNewest ? known.depsSum : digest(...theirs)
|
|
183
|
+
if (depsSum !== known.depsSum) {
|
|
184
|
+
const newer = theirs.filter((dist) => newest(dist) > built).map((dist) => dists.get(dist))
|
|
185
|
+
refuse(
|
|
186
|
+
`dist was built before the current dist of ${newer.join(', ')}, which it reads — run pnpm build before trusting this bin. If that build just said FULL TURBO, the dependency was rebuilt alone and this bin had not yet recorded the build it was current against: pnpm build --force --filter=${name} rebuilds this one package against it.`
|
|
187
|
+
)
|
|
188
|
+
}
|
|
189
|
+
if (known.depsSeen !== depsNewest) remember(own, { ...known, depsSeen: depsNewest, depsSum })
|
|
190
|
+
} else if (known.depsSeen !== depsNewest) {
|
|
191
|
+
remember(own, { ...known, depsSeen: depsNewest, depsSum: digest(...theirs) })
|
|
192
|
+
}
|
|
46
193
|
}
|
|
47
194
|
}
|
|
48
195
|
|
|
@@ -24,7 +24,7 @@ var SEARCH = {
|
|
|
24
24
|
"import { pgFtsSearchIndex, type SearchIndex, type SearchProjections } from '@geonosis/search'"
|
|
25
25
|
],
|
|
26
26
|
name: "@geonosis/search",
|
|
27
|
-
version: "0.
|
|
27
|
+
version: "0.4.1"
|
|
28
28
|
};
|
|
29
29
|
var DOCUMENT = {
|
|
30
30
|
at: "domains/document",
|
|
@@ -37,13 +37,13 @@ var DOCUMENT = {
|
|
|
37
37
|
fieldType: "typeof DOCUMENT_ENTITIES",
|
|
38
38
|
imports: ["import { documentEntities } from '@geonosis/document'"],
|
|
39
39
|
name: "@geonosis/document",
|
|
40
|
-
version: "0.
|
|
40
|
+
version: "0.9.0"
|
|
41
41
|
};
|
|
42
42
|
var DOMAINS = [SEARCH, DOCUMENT];
|
|
43
43
|
var DOMAIN_NAMES = DOMAINS.map((one) => one.field);
|
|
44
44
|
var FOUNDATIONS = [
|
|
45
|
-
{ at: "foundations/db", name: "@geonosis/db", version: "0.
|
|
46
|
-
{ at: "foundations/events", name: "@geonosis/events", version: "2.
|
|
45
|
+
{ at: "foundations/db", name: "@geonosis/db", version: "0.14.0" },
|
|
46
|
+
{ at: "foundations/events", name: "@geonosis/events", version: "2.2.0" }
|
|
47
47
|
];
|
|
48
48
|
var domainsNamed = (named) => named.map((one) => {
|
|
49
49
|
const found = DOMAINS.find((domain) => domain.field === one);
|
|
@@ -55,7 +55,7 @@ var domainsNamed = (named) => named.map((one) => {
|
|
|
55
55
|
return found;
|
|
56
56
|
});
|
|
57
57
|
var floorDependencies = (named) => named.length === 0 ? {} : Object.fromEntries(
|
|
58
|
-
[...FOUNDATIONS, ...domainsNamed(named)].map((one) => [one.name, `^${one.version}`]).toSorted(([a], [b]) => String(a).localeCompare(String(b)))
|
|
58
|
+
[...FOUNDATIONS, ...domainsNamed(named)].map((one) => [one.name, `^${one.version}`]).toSorted(([a], [b]) => String(a).localeCompare(String(b), "en-US"))
|
|
59
59
|
);
|
|
60
60
|
var floorDirectories = (named) => named.length === 0 ? [] : [...FOUNDATIONS, ...domainsNamed(named)].map((one) => one.at);
|
|
61
61
|
var platformSource = (named) => {
|
|
@@ -144,8 +144,7 @@ var VALUED = /* @__PURE__ */ new Set([
|
|
|
144
144
|
"--ui"
|
|
145
145
|
]);
|
|
146
146
|
var SWITCHES = /* @__PURE__ */ new Set(["--check", "--help", "-h", "--no-install", "--plan"]);
|
|
147
|
-
var parseArgs = (argv) =>
|
|
148
|
-
var read = (argv) => {
|
|
147
|
+
var parseArgs = (argv) => {
|
|
149
148
|
const flags = {};
|
|
150
149
|
let dir;
|
|
151
150
|
for (let at = 0; at < argv.length; at += 1) {
|
|
@@ -155,16 +154,17 @@ var read = (argv) => {
|
|
|
155
154
|
continue;
|
|
156
155
|
}
|
|
157
156
|
const equals = token.indexOf("=");
|
|
157
|
+
const flag = equals === -1 ? token : token.slice(0, equals);
|
|
158
|
+
if (!VALUED.has(flag) && !SWITCHES.has(flag)) {
|
|
159
|
+
throw new Error(
|
|
160
|
+
`${flag} is not an option create-geonosis takes. Run create-geonosis --help for the ones it does.`
|
|
161
|
+
);
|
|
162
|
+
}
|
|
158
163
|
if (equals !== -1) {
|
|
159
|
-
flags[
|
|
164
|
+
flags[flag] = token.slice(equals + 1);
|
|
160
165
|
continue;
|
|
161
166
|
}
|
|
162
|
-
if (
|
|
163
|
-
if (!SWITCHES.has(token)) {
|
|
164
|
-
throw new Error(
|
|
165
|
-
`${token} is not an option create-geonosis takes. Run create-geonosis --help for the ones it does.`
|
|
166
|
-
);
|
|
167
|
-
}
|
|
167
|
+
if (SWITCHES.has(token)) {
|
|
168
168
|
flags[token] = "true";
|
|
169
169
|
continue;
|
|
170
170
|
}
|
|
@@ -191,10 +191,15 @@ var domainsFrom = (given) => {
|
|
|
191
191
|
return named;
|
|
192
192
|
};
|
|
193
193
|
var parseAnswers = (argv, cwd) => {
|
|
194
|
-
const { dir, flags } =
|
|
194
|
+
const { dir, flags } = parseArgs(argv);
|
|
195
195
|
if (dir === void 0) {
|
|
196
196
|
throw new Error("create-geonosis needs a directory to write the repo into");
|
|
197
197
|
}
|
|
198
|
+
if (flags["--presets"] !== void 0) {
|
|
199
|
+
throw new Error(
|
|
200
|
+
"--presets is a --plan option: a scaffold takes its presets from --backend and --ui, and would ignore the ones named here"
|
|
201
|
+
);
|
|
202
|
+
}
|
|
198
203
|
const name = basename(resolve(cwd, dir));
|
|
199
204
|
const domains = domainsFrom(flags["--domains"]);
|
|
200
205
|
return {
|
|
@@ -420,27 +425,37 @@ var workspacesOf = (answers) => {
|
|
|
420
425
|
};
|
|
421
426
|
|
|
422
427
|
// src/files.ts
|
|
423
|
-
import {
|
|
424
|
-
|
|
425
|
-
|
|
428
|
+
import {
|
|
429
|
+
countersFor,
|
|
430
|
+
LAW_SOURCE,
|
|
431
|
+
LOADED_VERSION,
|
|
432
|
+
oxlintConfigFor,
|
|
433
|
+
rulesFor,
|
|
434
|
+
tiersFor
|
|
435
|
+
} from "@geonosis/cli";
|
|
436
|
+
var VERSION = LOADED_VERSION;
|
|
426
437
|
var CATALOG = {
|
|
427
438
|
"@types/node": "^24.3.0",
|
|
428
439
|
oxfmt: "^0.62.0",
|
|
429
440
|
oxlint: "^1.80.0",
|
|
430
441
|
typescript: "^5.9.2",
|
|
431
|
-
vitest: "^
|
|
442
|
+
vitest: "^4.1.11"
|
|
432
443
|
};
|
|
433
444
|
var REACT = { "@types/react": "^19.0.0", react: "^19.0.0" };
|
|
434
445
|
var json = (value) => `${JSON.stringify(value, void 0, 2)}
|
|
435
446
|
`;
|
|
436
447
|
var exec = (runtime) => runtime === "bun" ? "bunx" : "npx";
|
|
448
|
+
var run = (runtime) => runtime === "bun" ? "bun run" : "pnpm";
|
|
437
449
|
var everyWorkspace = (runtime, script) => runtime === "bun" ? `bun run --filter '*' ${script}` : `pnpm -r ${script}`;
|
|
438
450
|
var presetsFor = (answers) => [
|
|
439
451
|
"tooling",
|
|
440
452
|
...answers.ui === "none" ? [] : ["frontend"],
|
|
441
453
|
...answers.backend === "none" ? [] : ["tenant-scoping"],
|
|
442
454
|
...answers.backend === "medusa" ? ["backend-medusa"] : [],
|
|
443
|
-
|
|
455
|
+
// #197 (plugin): `backend-sagaflow-cf` is a deprecated alias for `backend-workflows-cf` that
|
|
456
|
+
// resolves to the same rules until the next major, then goes — ask for the current name, never
|
|
457
|
+
// the alias, so a scaffolded repo's config does not go stale the day the alias is dropped.
|
|
458
|
+
...answers.backend === "sagaflow-cf" ? ["backend-workflows-cf"] : [],
|
|
444
459
|
...answers.themekit === void 0 ? [] : ["themekit"]
|
|
445
460
|
];
|
|
446
461
|
var tokenPrefix = (name) => `--${name.toLowerCase().replaceAll(/[^a-z]/g, "").slice(0, 3)}-`;
|
|
@@ -645,7 +660,7 @@ var workspaceYaml = (answers) => [
|
|
|
645
660
|
"# One version of a shared dependency, named once. A repo whose workspaces each pin their own",
|
|
646
661
|
"# typescript is a repo where one of them typechecks against a compiler the others do not run.",
|
|
647
662
|
"catalog:",
|
|
648
|
-
...Object.entries({ ...CATALOG, ...answers.ui === "none" ? {} : REACT }).toSorted(([a], [b]) => a.localeCompare(b)).map(([name, spec]) => ` '${name}': '${spec}'`),
|
|
663
|
+
...Object.entries({ ...CATALOG, ...answers.ui === "none" ? {} : REACT }).toSorted(([a], [b]) => a.localeCompare(b, "en-US")).map(([name, spec]) => ` '${name}': '${spec}'`),
|
|
649
664
|
"",
|
|
650
665
|
"# pnpm 11 runs no dependency's install script unless it is named here. esbuild fetches its",
|
|
651
666
|
"# platform binary in one, and without it vitest cannot start \u2014 a test suite that cannot run",
|
|
@@ -713,8 +728,8 @@ var law = (answers) => [
|
|
|
713
728
|
"",
|
|
714
729
|
"**3. No suppression.** No `@ts-ignore`, no `@ts-expect-error`. Fix the type, or ask.",
|
|
715
730
|
"",
|
|
716
|
-
|
|
717
|
-
"not a dependency here, so it downloads itself and reformats with its own defaults.",
|
|
731
|
+
`**4. oxfmt only.** \`${run(answers.runtime)} format\` writes, \`${run(answers.runtime)} format:check\` verifies. Never`,
|
|
732
|
+
"prettier: it is not a dependency here, so it downloads itself and reformats with its own defaults.",
|
|
718
733
|
"",
|
|
719
734
|
"**5. Nothing is hardcoded to a repo.** Every list a rule needs is an option.",
|
|
720
735
|
"",
|
|
@@ -845,14 +860,7 @@ var filesFor = (answers) => {
|
|
|
845
860
|
CORPUS_DIR
|
|
846
861
|
]
|
|
847
862
|
}),
|
|
848
|
-
".oxlintrc.json": json(
|
|
849
|
-
$schema: SCHEMA,
|
|
850
|
-
plugins: ["typescript", "unicorn"],
|
|
851
|
-
jsPlugins: ["@geonosis/oxlint-plugin-biological-architecture"],
|
|
852
|
-
categories: { correctness: "error", suspicious: "error" },
|
|
853
|
-
rules,
|
|
854
|
-
ignorePatterns: ["node_modules", "dist", "coverage", CORPUS_DIR]
|
|
855
|
-
}),
|
|
863
|
+
".oxlintrc.json": json(oxlintConfigFor(rules, [CORPUS_DIR])),
|
|
856
864
|
"geonosis.json": json({
|
|
857
865
|
verify: tiersFor({
|
|
858
866
|
rules,
|
|
@@ -996,7 +1004,15 @@ var describeDrift = (drift) => drift.map((one) => SAID[one.kind](one));
|
|
|
996
1004
|
|
|
997
1005
|
// src/scaffold.ts
|
|
998
1006
|
import { spawnSync } from "child_process";
|
|
999
|
-
import {
|
|
1007
|
+
import {
|
|
1008
|
+
chmodSync,
|
|
1009
|
+
existsSync as existsSync2,
|
|
1010
|
+
mkdirSync,
|
|
1011
|
+
readdirSync,
|
|
1012
|
+
readFileSync as readFileSync2,
|
|
1013
|
+
symlinkSync,
|
|
1014
|
+
writeFileSync
|
|
1015
|
+
} from "fs";
|
|
1000
1016
|
import { dirname, join as join2 } from "path";
|
|
1001
1017
|
var KIT_PACKAGES = [
|
|
1002
1018
|
"cli",
|
|
@@ -1023,7 +1039,7 @@ var writeScaffold = (answers) => {
|
|
|
1023
1039
|
for (const [file, body] of Object.entries(files)) write(answers.dir, file, body);
|
|
1024
1040
|
return Object.keys(files).toSorted();
|
|
1025
1041
|
};
|
|
1026
|
-
var
|
|
1042
|
+
var run2 = (what, dir, command, args) => {
|
|
1027
1043
|
const done = spawnSync(command, args, { cwd: dir, encoding: "utf8", maxBuffer: 32 * 1024 * 1024 });
|
|
1028
1044
|
return {
|
|
1029
1045
|
code: done.error === void 0 ? done.status ?? 2 : 2,
|
|
@@ -1081,7 +1097,7 @@ var linkKit = (answers, kit) => {
|
|
|
1081
1097
|
const declared = readJson(join2(from, "package.json"));
|
|
1082
1098
|
if (declared.name === void 0) continue;
|
|
1083
1099
|
const to = join2(scope, declared.name.replace("@geonosis/", ""));
|
|
1084
|
-
if (!existsSync2(to))
|
|
1100
|
+
if (!existsSync2(to)) symlinkSync(from, to);
|
|
1085
1101
|
for (const [called, entry] of binsOf(from)) {
|
|
1086
1102
|
const shim = join2(bin, called);
|
|
1087
1103
|
writeFileSync(shim, `#!/bin/sh
|
|
@@ -1117,10 +1133,10 @@ var finish = ({ answers, install }) => {
|
|
|
1117
1133
|
if (install) {
|
|
1118
1134
|
const [command, args] = installer(answers);
|
|
1119
1135
|
const offline = answers.runtime === "pnpm" ? [...args, "--offline"] : args;
|
|
1120
|
-
const first =
|
|
1136
|
+
const first = run2(`${command} install (offline)`, answers.dir, command, offline);
|
|
1121
1137
|
steps.push(first);
|
|
1122
1138
|
if (first.code !== 0) {
|
|
1123
|
-
steps.push(
|
|
1139
|
+
steps.push(run2(`${command} install (network)`, answers.dir, command, args));
|
|
1124
1140
|
}
|
|
1125
1141
|
}
|
|
1126
1142
|
if (answers.kit !== void 0) {
|
|
@@ -1136,7 +1152,7 @@ var finish = ({ answers, install }) => {
|
|
|
1136
1152
|
if (existsSync2(ledger)) {
|
|
1137
1153
|
for (const { target, write: into } of GRAPH_SYNCS) {
|
|
1138
1154
|
steps.push(
|
|
1139
|
-
|
|
1155
|
+
run2(`sync the edges table into ${into}`, answers.dir, ledger, [
|
|
1140
1156
|
"sync",
|
|
1141
1157
|
"--target",
|
|
1142
1158
|
target,
|
|
@@ -1148,7 +1164,7 @@ var finish = ({ answers, install }) => {
|
|
|
1148
1164
|
}
|
|
1149
1165
|
const oxfmt = join2(answers.dir, "node_modules/.bin/oxfmt");
|
|
1150
1166
|
if (existsSync2(oxfmt)) {
|
|
1151
|
-
steps.push(
|
|
1167
|
+
steps.push(run2("format", answers.dir, oxfmt, ["--config", ".oxfmtrc.json", "."]));
|
|
1152
1168
|
}
|
|
1153
1169
|
return steps;
|
|
1154
1170
|
};
|
|
@@ -1167,6 +1183,7 @@ export {
|
|
|
1167
1183
|
UIS,
|
|
1168
1184
|
MEDUSA_RANGE,
|
|
1169
1185
|
parseArgs,
|
|
1186
|
+
scopeOf,
|
|
1170
1187
|
parseAnswers,
|
|
1171
1188
|
wantsInstall,
|
|
1172
1189
|
CORPUS_DIR,
|
|
@@ -9,9 +9,10 @@ import {
|
|
|
9
9
|
finish,
|
|
10
10
|
parseAnswers,
|
|
11
11
|
parseArgs,
|
|
12
|
+
scopeOf,
|
|
12
13
|
wantsInstall,
|
|
13
14
|
writeScaffold
|
|
14
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-YF4AX76D.js";
|
|
15
16
|
|
|
16
17
|
// src/create-geonosis-cli.ts
|
|
17
18
|
import { existsSync as existsSync2 } from "fs";
|
|
@@ -86,7 +87,7 @@ var planAdoption = ({ backend, dir, presets, runtime, ui }) => {
|
|
|
86
87
|
domains: [],
|
|
87
88
|
name: basename(at),
|
|
88
89
|
runtime: chosenRuntime,
|
|
89
|
-
scope:
|
|
90
|
+
scope: scopeOf(basename(at)),
|
|
90
91
|
ui: uiOf(at, chosen("--ui", ui, UIS))
|
|
91
92
|
};
|
|
92
93
|
const drift = checkScaffold(answers);
|
|
@@ -118,7 +119,7 @@ var planAdoption = ({ backend, dir, presets, runtime, ui }) => {
|
|
|
118
119
|
return { declared: declaredCounters.has(key), key };
|
|
119
120
|
}),
|
|
120
121
|
dir: at,
|
|
121
|
-
doors:
|
|
122
|
+
doors: KIT_OWNED_BLOCKS.map((block) => ({
|
|
122
123
|
block,
|
|
123
124
|
declared: declaredPackages.has(doorFor(block)),
|
|
124
125
|
package: doorFor(block)
|
|
@@ -189,7 +190,7 @@ var formatPlan = (plan) => [
|
|
|
189
190
|
|
|
190
191
|
// src/create-geonosis-cli.ts
|
|
191
192
|
var USAGE = `create-geonosis <dir> --runtime pnpm|bun --backend medusa|sagaflow-cf|none --ui tiered-shadcn|atoms-only|none
|
|
192
|
-
|
|
193
|
+
[--medusa <range>]
|
|
193
194
|
[--domains <a,b>] [--brand <name>] [--themekit <name>] [--marketplace <path>] [--kit <path>] [--no-install]
|
|
194
195
|
[--check]
|
|
195
196
|
create-geonosis --plan <dir> [--presets <a,b>] [--runtime \u2026] [--backend \u2026] [--ui \u2026]
|
package/dist/index.d.ts
CHANGED
|
@@ -118,10 +118,10 @@ declare const CORPUS_DIR = "lint-corpus";
|
|
|
118
118
|
* A reach corpus a shipped one cannot be.
|
|
119
119
|
*
|
|
120
120
|
* `geonosis-doctor` asks whether every enabled rule can fire at all, and it asks the corpus the
|
|
121
|
-
* PLUGIN ships. That
|
|
122
|
-
*
|
|
123
|
-
*
|
|
124
|
-
*
|
|
121
|
+
* PLUGIN ships. That cannot answer for a rule whose reach is the repo's own vocabulary: its layer
|
|
122
|
+
* names, its brand, its plugin package root, the `within` its sagas live in. The plugin's corpus
|
|
123
|
+
* says "acme" and `layers/core`, so it is no evidence either way about a repo that says anything
|
|
124
|
+
* else — and those rules would read as firing nowhere.
|
|
125
125
|
*
|
|
126
126
|
* So the repo ships its own, in ITS vocabulary. The corpus is copied to a temp root and linted with
|
|
127
127
|
* `--no-ignore`, so the fixture paths mirror the repo's real ones and the layer patterns match.
|
package/dist/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-geonosis",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.11.1",
|
|
4
4
|
"types": "./dist/index.d.ts",
|
|
5
5
|
"description": "Scaffold a microcompany repo that passes its own geonosis gates on day one.",
|
|
6
6
|
"keywords": [
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"dist"
|
|
36
36
|
],
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@geonosis/cli": "2.
|
|
38
|
+
"@geonosis/cli": "2.11.1"
|
|
39
39
|
},
|
|
40
40
|
"engines": {
|
|
41
41
|
"node": ">=22"
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"access": "public"
|
|
45
45
|
},
|
|
46
46
|
"scripts": {
|
|
47
|
-
"build": "tsup",
|
|
47
|
+
"build": "tsx ../../tooling/build-step.ts tsup",
|
|
48
48
|
"typecheck": "tsc --noEmit",
|
|
49
49
|
"lint": "oxlint --config ../../.oxlintrc.json .",
|
|
50
50
|
"test:unit": "vitest run --project unit --root ../.. --passWithNoTests \"$PWD\"",
|