create-geonosis 2.13.0 → 3.0.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/dist/index.d.ts CHANGED
@@ -1,170 +1,7 @@
1
- declare const RUNTIMES: readonly ["bun", "pnpm"];
2
- declare const BACKENDS: readonly ["medusa", "none", "sagaflow-cf"];
3
- declare const UIS: readonly ["atoms-only", "none", "tiered-shadcn"];
4
- type Runtime = (typeof RUNTIMES)[number];
5
- type Backend = (typeof BACKENDS)[number];
6
- type Ui = (typeof UIS)[number];
7
- type Answers = {
8
- backend: Backend;
9
- brand?: string;
10
- dir: string;
11
- /** The domains this repo composes at its composition root. Empty until the flag names one. */
12
- domains: string[];
13
- /** The kit read from a workspace instead of the registry — a proof aid, never the default. */
14
- kit?: string;
15
- marketplace?: string;
16
- /** The Medusa range a `--backend medusa` tree declares. `MEDUSA_RANGE` unless one is named. */
17
- medusa?: string;
18
- name: string;
19
- runtime: Runtime;
20
- scope: string;
21
- themekit?: string;
22
- ui: Ui;
23
- };
24
- declare const parseAnswers: (argv: readonly string[], cwd: string) => Answers;
25
- declare const wantsInstall: (argv: readonly string[]) => boolean;
26
-
27
- /**
28
- * What an existing repo never receives: whatever the scaffold has learned since it was run.
29
- *
30
- * The comparison is over SHAPE and not over content, because a repo that has been worked in has
31
- * legitimately rewritten every file the scaffold gave it. What it cannot have legitimately done is
32
- * lose a file the scaffold now writes, or miss a declaration a new gate reads — so a missing path is
33
- * drift and a changed value never is.
34
- */
35
- type DriftKind = 'key' | 'missing' | 'unreadable';
36
- type Drift = {
37
- /** The key path inside the file, or `''` when the fact is about the whole file. */
38
- at: string;
39
- file: string;
40
- kind: DriftKind;
41
- };
42
- /**
43
- * The drift between a tree and what the scaffold would write into it today. Reads only.
44
- *
45
- * Nothing in this module opens a file for writing, and nothing calls `writeScaffold`. That is the
46
- * whole contract of `--check`: it is run against a repo somebody is working in.
47
- */
48
- declare const checkScaffold: (answers: Answers) => Drift[];
49
- declare const describeDrift: (drift: readonly Drift[]) => string[];
50
-
51
- /** Where the composition root lives, relative to the workspace that composes: the app. */
52
- declare const PLATFORM_FILE = "src/platform.ts";
53
- /**
54
- * A domain the kit ships, and the two lines composing it takes.
55
- *
56
- * A domain is added here and nowhere else: the flag's vocabulary, the app's dependencies and the
57
- * generated file all read this table, so a domain that ships without a way to compose it cannot be
58
- * offered by name. `version` is the floor package's OWN line — the method's number says nothing
59
- * about it (D-053) — and `tooling/floor-scaffold-versions.test.ts` holds each against the workspace.
60
- */
61
- type DomainScaffold = {
62
- /** Where the package sits in the kit's tree, for `--kit`'s workspace links. */
63
- at: string;
64
- /** What the scope field is called, and the type it is annotated with. */
65
- field: string;
66
- /** Everything the generated file needs above the composition itself. */
67
- declarations: readonly string[];
68
- fieldType: string;
69
- imports: readonly string[];
70
- name: string;
71
- /** How the store is built, once the tenant's transaction is already open. */
72
- built: readonly string[];
73
- version: string;
74
- };
75
- declare const DOMAINS: readonly DomainScaffold[];
76
- declare const DOMAIN_NAMES: readonly string[];
77
- /** The two foundations a composition root is built from, whichever domains it composes. */
78
- declare const FOUNDATIONS: readonly {
79
- at: string;
80
- name: string;
81
- version: string;
82
- }[];
83
- declare const domainsNamed: (named: readonly string[]) => DomainScaffold[];
84
- /** What the composing workspace declares once a domain is named. */
85
- declare const floorDependencies: (named: readonly string[]) => Record<string, string>;
86
- /** Every kit directory a `--kit` link has to reach for the domains this repo named. */
87
- declare const floorDirectories: (named: readonly string[]) => string[];
88
- /**
89
- * The composition root, in the two lifetimes it has.
90
- *
91
- * DEFINITIONS are what this deployment IS — wired once per isolate, before anybody has asked
92
- * anything. SCOPE is who is asking, bound per request. Keeping them apart is what lets the session
93
- * come first: the stores are built INSIDE the tenant's transaction, so a caller cannot hold one
94
- * that was made without a session to run in (D-014 — structural, never an assertion).
95
- */
96
- declare const platformSource: (named: readonly string[]) => string;
97
-
98
- type Workspace = {
99
- dir: string;
100
- /** The files under this workspace, keyed by path relative to the workspace. */
101
- sources: Record<string, string>;
102
- name: string;
103
- /** Packages outside the workspace this one declares, spec and all. */
104
- packages?: Record<string, string>;
105
- tag: string;
106
- mayDependOn: string[];
107
- };
108
- /**
109
- * The workspaces the answers imply, and the edge each one is allowed. Two at minimum — a pure-logic
110
- * package and the app that composes it — because a wall needs something on the other side of it: a
111
- * repo with one tag has no edge to violate, and a `layer-walls` that cannot fire is the inert rule
112
- * this kit exists to catch.
113
- */
114
- declare const workspacesOf: (answers: Answers) => Workspace[];
115
-
116
- declare const CORPUS_DIR = "lint-corpus";
117
- /**
118
- * A reach corpus a shipped one cannot be.
119
- *
120
- * `geonosis-doctor` asks whether every enabled rule can fire at all, and it asks the corpus the
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
- *
126
- * So the repo ships its own, in ITS vocabulary. The corpus is copied to a temp root and linted with
127
- * `--no-ignore`, so the fixture paths mirror the repo's real ones and the layer patterns match.
128
- */
129
- declare const corpusFor: (answers: Answers, workspaces: Workspace[]) => Record<string, string>;
130
-
131
- declare const filesFor: (answers: Answers) => Record<string, string>;
132
- declare const presetsChosenBy: (answers: Answers) => string[];
133
-
134
- /** The kit packages a scaffolded repo RESOLVES, and therefore the ones `--kit` has to link. */
135
- declare const KIT_PACKAGES: string[];
136
- /**
137
- * Every file, and never over the top of anything. A scaffold that overwrote a tree is a scaffold
138
- * nobody dares run in the wrong shell, and the tree it lands in is usually somebody's work.
139
- */
140
- declare const writeScaffold: (answers: Answers) => string[];
141
- type Step = {
142
- code: number;
143
- output: string;
144
- what: string;
145
- };
146
- /**
147
- * The kit read from a workspace instead of the registry — a proof aid, and only that.
148
- *
149
- * It is done by hand, after the install, because neither package manager can do it: pnpm resolves
150
- * `link:` happily, and bun refuses BOTH `link:` and `file:` here, because it follows into the target
151
- * and tries to resolve the `workspace:*` specifiers the kit's own packages use. Symlinking the
152
- * directories and writing the bin shims is the one form of the answer that is true on both.
153
- *
154
- * The manifests are then told what is on disk, because `geonosis-doctor` FAILS a config whose plugin
155
- * no manifest declares — and it should. A link is a declaration it accepts and can verify.
156
- */
157
- declare const linkKit: (answers: Answers, kit: string) => string[];
158
- /**
159
- * What has to happen after the files exist, in the order it has to happen in.
160
- *
161
- * The two `sync` calls are why the scaffold does not compute a boundaries map or a layer list of
162
- * its own: the edges table is the ONE place the graph is written, and the ledger is the one thing
163
- * that reads it. A second generator here would be a second answer to the same question.
164
- */
165
- declare const finish: ({ answers, install }: {
166
- answers: Answers;
167
- install: boolean;
168
- }) => Step[];
169
-
170
- export { type Answers, BACKENDS, type Backend, CORPUS_DIR, DOMAINS, DOMAIN_NAMES, type DomainScaffold, type Drift, type DriftKind, FOUNDATIONS, KIT_PACKAGES, PLATFORM_FILE, RUNTIMES, type Runtime, type Step, UIS, type Ui, type Workspace, checkScaffold, corpusFor, describeDrift, domainsNamed, filesFor, finish, floorDependencies, floorDirectories, linkKit, parseAnswers, platformSource, presetsChosenBy, wantsInstall, workspacesOf, writeScaffold };
1
+ export { type Answers, type Backend, BACKENDS, parseAnswers, type Runtime, RUNTIMES, type Ui, UIS, wantsInstall } from './answers.js';
2
+ export { checkScaffold, describeDrift, type Drift, type DriftKind } from './check.js';
3
+ export { DOMAIN_NAMES, DOMAINS, type DomainScaffold, domainsNamed, floorDependencies, floorDirectories, FOUNDATIONS, PLATFORM_FILE, platformSource } from './composition.js';
4
+ export { CORPUS_DIR, corpusFor } from './corpus.js';
5
+ export { filesFor, presetsChosenBy } from './files.js';
6
+ export { type Workspace, workspacesOf } from './layout.js';
7
+ export { finish, KIT_PACKAGES, linkKit, type Step, writeScaffold } from './scaffold.js';
package/dist/index.js CHANGED
@@ -1,29 +1,26 @@
1
+ // src/index.ts
1
2
  import {
2
3
  BACKENDS,
3
- CORPUS_DIR,
4
- DOMAINS,
5
- DOMAIN_NAMES,
6
- FOUNDATIONS,
7
- KIT_PACKAGES,
8
- PLATFORM_FILE,
4
+ parseAnswers,
9
5
  RUNTIMES,
10
6
  UIS,
11
- checkScaffold,
12
- corpusFor,
13
- describeDrift,
7
+ wantsInstall
8
+ } from "./answers.js";
9
+ import { checkScaffold, describeDrift } from "./check.js";
10
+ import {
11
+ DOMAIN_NAMES,
12
+ DOMAINS,
14
13
  domainsNamed,
15
- filesFor,
16
- finish,
17
14
  floorDependencies,
18
15
  floorDirectories,
19
- linkKit,
20
- parseAnswers,
21
- platformSource,
22
- presetsChosenBy,
23
- wantsInstall,
24
- workspacesOf,
25
- writeScaffold
26
- } from "./chunk-LUUI74BB.js";
16
+ FOUNDATIONS,
17
+ PLATFORM_FILE,
18
+ platformSource
19
+ } from "./composition.js";
20
+ import { CORPUS_DIR, corpusFor } from "./corpus.js";
21
+ import { filesFor, presetsChosenBy } from "./files.js";
22
+ import { workspacesOf } from "./layout.js";
23
+ import { finish, KIT_PACKAGES, linkKit, writeScaffold } from "./scaffold.js";
27
24
  export {
28
25
  BACKENDS,
29
26
  CORPUS_DIR,
@@ -0,0 +1,18 @@
1
+ import { type Answers } from './answers.js';
2
+ export type Workspace = {
3
+ dir: string;
4
+ /** The files under this workspace, keyed by path relative to the workspace. */
5
+ sources: Record<string, string>;
6
+ name: string;
7
+ /** Packages outside the workspace this one declares, spec and all. */
8
+ packages?: Record<string, string>;
9
+ tag: string;
10
+ mayDependOn: string[];
11
+ };
12
+ /**
13
+ * The workspaces the answers imply, and the edge each one is allowed. Two at minimum — a pure-logic
14
+ * package and the app that composes it — because a wall needs something on the other side of it: a
15
+ * repo with one tag has no edge to violate, and a `layer-walls` that cannot fire is the inert rule
16
+ * this kit exists to catch.
17
+ */
18
+ export declare const workspacesOf: (answers: Answers) => Workspace[];
package/dist/layout.js ADDED
@@ -0,0 +1,164 @@
1
+ // src/layout.ts
2
+ import { MEDUSA_PACKAGES, MEDUSA_RANGE } from "./answers.js";
3
+ import { floorDependencies, PLATFORM_FILE, platformSource } from "./composition.js";
4
+ var CORE = `export type Money = { amount: number; currency: string }
5
+
6
+ export const totalOf = (lines: readonly Money[], currency: string): Money => ({
7
+ amount: lines.filter((one) => one.currency === currency).reduce((sum, one) => sum + one.amount, 0),
8
+ currency,
9
+ })
10
+ `;
11
+ var CORE_TEST = (scope) => `import { describe, expect, it } from 'vitest'
12
+
13
+ import { totalOf } from './money'
14
+
15
+ describe('totalOf', () => {
16
+ it('adds the lines that share the currency', () => {
17
+ expect(totalOf([{ amount: 2, currency: 'EUR' }, { amount: 3, currency: 'EUR' }], 'EUR')).toEqual(
18
+ { amount: 5, currency: 'EUR' },
19
+ )
20
+ })
21
+
22
+ it('ignores the ones that do not — ${scope}/core never guesses a rate', () => {
23
+ expect(totalOf([{ amount: 2, currency: 'EUR' }, { amount: 9, currency: 'USD' }], 'EUR').amount)
24
+ .toBe(2)
25
+ })
26
+ })
27
+ `;
28
+ var BUTTON = `import type { ComponentProps } from 'react'
29
+
30
+ export function Button(props: ComponentProps<'button'>) {
31
+ return <button type="button" {...props} />
32
+ }
33
+ `;
34
+ var FIELD = (scope) => `import { Button } from '${scope}/ui/atoms/button'
35
+
36
+ export function Field({ label, onSubmit }: { label: string; onSubmit: () => void }) {
37
+ return (
38
+ <label>
39
+ {label}
40
+ <Button onClick={onSubmit}>Go</Button>
41
+ </label>
42
+ )
43
+ }
44
+ `;
45
+ var UI_TEST = `import { describe, expect, it } from 'vitest'
46
+
47
+ import { Button } from './atoms/button'
48
+
49
+ describe('Button', () => {
50
+ it('is a component, so a tier above can compose it', () => {
51
+ expect(typeof Button).toBe('function')
52
+ })
53
+ })
54
+ `;
55
+ var MEDUSA_SUBSCRIBER = `export const config = {
56
+ context: { subscriberId: 'order-placed' },
57
+ event: 'order.placed',
58
+ }
59
+
60
+ export default async function orderPlaced({ data }: { data: { id: string } }): Promise<string> {
61
+ return data.id
62
+ }
63
+ `;
64
+ var SAGAFLOW_STEP = `export const step = <Input, Output>(
65
+ name: string,
66
+ body: (input: Input) => Promise<Output>,
67
+ undo: (output: Output) => Promise<void>,
68
+ ) => ({ body, name, undo })
69
+ `;
70
+ var SAGAFLOW_FLOW = `import { step } from './step'
71
+
72
+ export const reserve = step(
73
+ 'reserve',
74
+ async (input: { id: string }) => ({ held: input.id }),
75
+ async () => undefined,
76
+ )
77
+ `;
78
+ var BACKEND_TEST = `import { describe, expect, it } from 'vitest'
79
+
80
+ import { handler } from './handler'
81
+
82
+ describe('the backend seam', () => {
83
+ it('answers with what it was asked about', async () => {
84
+ expect(await handler({ id: 'ord_1' })).toBe('ord_1')
85
+ })
86
+ })
87
+ `;
88
+ var HANDLER = `export const handler = async ({ id }: { id: string }): Promise<string> => id
89
+ `;
90
+ var APP = (scope, ui) => ui ? `import { totalOf } from '${scope}/core/money'
91
+ import { Button } from '${scope}/ui/atoms/button'
92
+
93
+ export const boot = () => ({ button: Button, total: totalOf([], 'EUR') })
94
+ ` : `import { totalOf } from '${scope}/core/money'
95
+
96
+ export const boot = () => ({ total: totalOf([], 'EUR') })
97
+ `;
98
+ var APP_TEST = `import { describe, expect, it } from 'vitest'
99
+
100
+ import { boot } from './boot'
101
+
102
+ describe('boot', () => {
103
+ it('starts with an empty total rather than a guess', () => {
104
+ expect(boot().total.amount).toBe(0)
105
+ })
106
+ })
107
+ `;
108
+ var workspacesOf = (answers) => {
109
+ const { scope, ui } = answers;
110
+ const list = [
111
+ {
112
+ dir: "packages/core",
113
+ mayDependOn: [],
114
+ name: `${scope}/core`,
115
+ sources: { "src/money.ts": CORE, "src/money.test.ts": CORE_TEST(scope) },
116
+ tag: "core"
117
+ }
118
+ ];
119
+ if (ui !== "none") {
120
+ list.push({
121
+ dir: "packages/ui",
122
+ mayDependOn: ["core"],
123
+ name: `${scope}/ui`,
124
+ sources: {
125
+ "src/atoms/button.tsx": BUTTON,
126
+ "src/button.test.ts": UI_TEST,
127
+ ...ui === "tiered-shadcn" ? { "src/molecules/field.tsx": FIELD(scope) } : {}
128
+ },
129
+ tag: "ui"
130
+ });
131
+ }
132
+ if (answers.backend !== "none") {
133
+ list.push({
134
+ dir: "packages/backend",
135
+ mayDependOn: ["core"],
136
+ name: `${scope}/backend`,
137
+ ...answers.backend === "medusa" ? {
138
+ packages: Object.fromEntries(MEDUSA_PACKAGES.map((one) => [one, answers.medusa ?? MEDUSA_RANGE]))
139
+ } : {},
140
+ sources: {
141
+ "src/handler.ts": HANDLER,
142
+ "src/handler.test.ts": BACKEND_TEST,
143
+ ...answers.backend === "medusa" ? { "src/subscribers/order-placed.ts": MEDUSA_SUBSCRIBER } : { "src/workflows/step.ts": SAGAFLOW_STEP, "src/workflows/reserve.ts": SAGAFLOW_FLOW }
144
+ },
145
+ tag: "backend"
146
+ });
147
+ }
148
+ list.push({
149
+ dir: "apps/web",
150
+ mayDependOn: list.map((one) => one.tag),
151
+ name: `${scope}/web`,
152
+ packages: floorDependencies(answers.domains),
153
+ sources: {
154
+ "src/boot.ts": APP(scope, ui !== "none"),
155
+ "src/boot.test.ts": APP_TEST,
156
+ ...answers.domains.length === 0 ? {} : { [PLATFORM_FILE]: platformSource(answers.domains) }
157
+ },
158
+ tag: "app"
159
+ });
160
+ return list;
161
+ };
162
+ export {
163
+ workspacesOf
164
+ };
@@ -0,0 +1,36 @@
1
+ import type { Answers } from './answers.js';
2
+ /** The kit packages a scaffolded repo RESOLVES, and therefore the ones `--kit` has to link. */
3
+ export declare const KIT_PACKAGES: string[];
4
+ /**
5
+ * Every file, and never over the top of anything. A scaffold that overwrote a tree is a scaffold
6
+ * nobody dares run in the wrong shell, and the tree it lands in is usually somebody's work.
7
+ */
8
+ export declare const writeScaffold: (answers: Answers) => string[];
9
+ export type Step = {
10
+ code: number;
11
+ output: string;
12
+ what: string;
13
+ };
14
+ /**
15
+ * The kit read from a workspace instead of the registry — a proof aid, and only that.
16
+ *
17
+ * It is done by hand, after the install, because neither package manager can do it: pnpm resolves
18
+ * `link:` happily, and bun refuses BOTH `link:` and `file:` here, because it follows into the target
19
+ * and tries to resolve the `workspace:*` specifiers the kit's own packages use. Symlinking the
20
+ * directories and writing the bin shims is the one form of the answer that is true on both.
21
+ *
22
+ * The manifests are then told what is on disk, because `geonosis-doctor` FAILS a config whose plugin
23
+ * no manifest declares — and it should. A link is a declaration it accepts and can verify.
24
+ */
25
+ export declare const linkKit: (answers: Answers, kit: string) => string[];
26
+ /**
27
+ * What has to happen after the files exist, in the order it has to happen in.
28
+ *
29
+ * The two `sync` calls are why the scaffold does not compute a boundaries map or a layer list of
30
+ * its own: the edges table is the ONE place the graph is written, and the ledger is the one thing
31
+ * that reads it. A second generator here would be a second answer to the same question.
32
+ */
33
+ export declare const finish: ({ answers, install }: {
34
+ answers: Answers;
35
+ install: boolean;
36
+ }) => Step[];
@@ -0,0 +1,173 @@
1
+ // src/scaffold.ts
2
+ import { spawnSync } from "node:child_process";
3
+ import {
4
+ chmodSync,
5
+ existsSync,
6
+ mkdirSync,
7
+ readdirSync,
8
+ readFileSync,
9
+ symlinkSync,
10
+ writeFileSync
11
+ } from "node:fs";
12
+ import { dirname, join } from "node:path";
13
+ import { floorDirectories } from "./composition.js";
14
+ import { filesFor, GRAPH_SYNCS, kitDependencies } from "./files.js";
15
+ import { workspacesOf } from "./layout.js";
16
+ var KIT_PACKAGES = [
17
+ "cli",
18
+ "doctor",
19
+ "ledger",
20
+ "lint-parity",
21
+ "oxlint-plugin-biological-architecture",
22
+ "ratchet",
23
+ "themekit",
24
+ "verify"
25
+ ];
26
+ var write = (dir, file, body) => {
27
+ const path = join(dir, file);
28
+ mkdirSync(dirname(path), { recursive: true });
29
+ writeFileSync(path, body);
30
+ };
31
+ var readJson = (path) => JSON.parse(readFileSync(path, "utf8"));
32
+ var writeScaffold = (answers) => {
33
+ if (existsSync(answers.dir) && readdirSync(answers.dir).length > 0) {
34
+ throw new Error(`${answers.dir} is not empty — nothing was written`);
35
+ }
36
+ const files = filesFor(answers);
37
+ mkdirSync(answers.dir, { recursive: true });
38
+ for (const [file, body] of Object.entries(files))
39
+ write(answers.dir, file, body);
40
+ return Object.keys(files).toSorted();
41
+ };
42
+ var run = (what, dir, command, args) => {
43
+ const done = spawnSync(command, args, { cwd: dir, encoding: "utf8", maxBuffer: 32 * 1024 * 1024 });
44
+ return {
45
+ code: done.error === undefined ? done.status ?? 2 : 2,
46
+ output: `${done.stdout ?? ""}${done.stderr ?? ""}${done.error === undefined ? "" : done.error.message}`,
47
+ what
48
+ };
49
+ };
50
+ var binsOf = (dir) => {
51
+ const manifest = readJson(join(dir, "package.json"));
52
+ if (typeof manifest.bin === "string")
53
+ return [[manifest.name ?? "", manifest.bin]];
54
+ return Object.entries(manifest.bin ?? {});
55
+ };
56
+ var repoint = (path, at, owed) => {
57
+ if (!existsSync(path))
58
+ return [];
59
+ const manifest = readJson(path);
60
+ const moved = [];
61
+ for (const block of ["dependencies", "devDependencies"]) {
62
+ const declared = manifest[block];
63
+ if (declared === undefined)
64
+ continue;
65
+ for (const name of Object.keys(declared)) {
66
+ const from = at.get(name);
67
+ if (from === undefined)
68
+ continue;
69
+ declared[name] = `link:${from}`;
70
+ moved.push(name);
71
+ }
72
+ }
73
+ for (const name of owed) {
74
+ const from = at.get(name);
75
+ if (from === undefined || moved.includes(name))
76
+ continue;
77
+ manifest.dependencies = { ...manifest.dependencies, [name]: `link:${from}` };
78
+ moved.push(name);
79
+ }
80
+ if (moved.length > 0)
81
+ writeFileSync(path, `${JSON.stringify(manifest, undefined, 2)}
82
+ `);
83
+ return moved;
84
+ };
85
+ var linkKit = (answers, kit) => {
86
+ const modules = join(answers.dir, "node_modules");
87
+ const scope = join(modules, "@geonosis");
88
+ const bin = join(modules, ".bin");
89
+ mkdirSync(scope, { recursive: true });
90
+ mkdirSync(bin, { recursive: true });
91
+ const linked = [];
92
+ const at = new Map;
93
+ const manifestPath = join(answers.dir, "package.json");
94
+ const manifest = readJson(manifestPath);
95
+ for (const relative of [
96
+ ...KIT_PACKAGES.map((name) => `packages/${name}`),
97
+ ...floorDirectories(answers.domains)
98
+ ]) {
99
+ const from = join(kit, relative);
100
+ if (!existsSync(from))
101
+ continue;
102
+ const declared = readJson(join(from, "package.json"));
103
+ if (declared.name === undefined)
104
+ continue;
105
+ const to = join(scope, declared.name.replace("@geonosis/", ""));
106
+ if (!existsSync(to))
107
+ symlinkSync(from, to);
108
+ for (const [called, entry] of binsOf(from)) {
109
+ const shim = join(bin, called);
110
+ writeFileSync(shim, `#!/bin/sh
111
+ exec node "${join(from, entry)}" "$@"
112
+ `);
113
+ chmodSync(shim, 493);
114
+ }
115
+ at.set(declared.name, from);
116
+ linked.push(relative);
117
+ }
118
+ const owned = new Set(workspacesOf(answers).flatMap((workspace) => repoint(join(answers.dir, workspace.dir, "package.json"), at, Object.keys(workspace.packages ?? {}))));
119
+ const rootDeclares = new Set(Object.keys(kitDependencies(answers)));
120
+ for (const [name, from] of at) {
121
+ if (!owned.has(name) && rootDeclares.has(name)) {
122
+ manifest.devDependencies = { ...manifest.devDependencies, [name]: `link:${from}` };
123
+ }
124
+ }
125
+ writeFileSync(manifestPath, `${JSON.stringify(manifest, undefined, 2)}
126
+ `);
127
+ return linked;
128
+ };
129
+ var installer = (answers) => answers.runtime === "bun" ? ["bun", ["install"]] : ["pnpm", ["install"]];
130
+ var finish = ({ answers, install }) => {
131
+ const steps = [];
132
+ if (install) {
133
+ const [command, args] = installer(answers);
134
+ const offline = answers.runtime === "pnpm" ? [...args, "--offline"] : args;
135
+ const first = run(`${command} install (offline)`, answers.dir, command, offline);
136
+ steps.push(first);
137
+ if (first.code !== 0) {
138
+ steps.push(run(`${command} install (network)`, answers.dir, command, args));
139
+ }
140
+ }
141
+ if (answers.kit !== undefined) {
142
+ const linked = linkKit(answers, answers.kit);
143
+ steps.push({
144
+ code: 0,
145
+ output: `${linked.join(", ")}
146
+ `,
147
+ what: `link the kit from ${answers.kit}`
148
+ });
149
+ }
150
+ const ledger = join(answers.dir, "node_modules/.bin/geonosis-ledger");
151
+ if (existsSync(ledger)) {
152
+ for (const { target, write: into } of GRAPH_SYNCS) {
153
+ steps.push(run(`sync the edges table into ${into}`, answers.dir, ledger, [
154
+ "sync",
155
+ "--target",
156
+ target,
157
+ "--write",
158
+ into
159
+ ]));
160
+ }
161
+ }
162
+ const oxfmt = join(answers.dir, "node_modules/.bin/oxfmt");
163
+ if (existsSync(oxfmt)) {
164
+ steps.push(run("format", answers.dir, oxfmt, ["--config", ".oxfmtrc.json", "."]));
165
+ }
166
+ return steps;
167
+ };
168
+ export {
169
+ KIT_PACKAGES,
170
+ finish,
171
+ linkKit,
172
+ writeScaffold
173
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-geonosis",
3
- "version": "2.13.0",
3
+ "version": "3.0.0",
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,16 +35,17 @@
35
35
  "dist"
36
36
  ],
37
37
  "dependencies": {
38
- "@geonosis/cli": "2.13.0"
38
+ "@geonosis/cli": "3.0.0"
39
39
  },
40
40
  "engines": {
41
- "node": ">=22"
41
+ "node": ">=22",
42
+ "bun": "^1.4.2"
42
43
  },
43
44
  "publishConfig": {
44
45
  "access": "public"
45
46
  },
46
47
  "scripts": {
47
- "build": "tsx ../../tooling/build-step.ts tsup",
48
+ "build": "bun ../../tooling/build-step.ts bun ../../tooling/bundle.ts",
48
49
  "typecheck": "tsc --noEmit",
49
50
  "lint": "oxlint --config ../../.oxlintrc.json .",
50
51
  "test:unit": "vitest run --project unit --root ../.. --passWithNoTests \"$PWD\"",