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.
@@ -1,1224 +0,0 @@
1
- // src/floor-versions.ts
2
- var FLOOR_VERSIONS = {
3
- "@geonosis/assistant": "0.1.0",
4
- "@geonosis/conformance": "0.4.0",
5
- "@geonosis/db": "0.14.1",
6
- "@geonosis/document": "0.9.1",
7
- "@geonosis/events": "2.2.0",
8
- "@geonosis/extraction": "0.1.0",
9
- "@geonosis/files": "0.1.0",
10
- "@geonosis/inbox": "0.1.1",
11
- "@geonosis/interchange": "0.1.0",
12
- "@geonosis/money": "0.1.0",
13
- "@geonosis/placement": "0.1.0",
14
- "@geonosis/plans": "0.1.0",
15
- "@geonosis/recurring": "0.1.0",
16
- "@geonosis/search": "0.4.1",
17
- "@geonosis/ui": "0.2.0",
18
- "@geonosis/workflows": "0.6.0"
19
- };
20
-
21
- // src/composition.ts
22
- var PLATFORM_FILE = "src/platform.ts";
23
- var SEARCH = {
24
- at: "domains/search",
25
- built: [
26
- " search: pgFtsSearchIndex({",
27
- " executor: tx,",
28
- " nowMs: definitions.nowMs,",
29
- " projections: SEARCH_PROJECTIONS,",
30
- " }),"
31
- ],
32
- declarations: [
33
- "/** How each kind this repo indexes becomes searchable text \u2014 declared once, never per call. */",
34
- "const SEARCH_PROJECTIONS: SearchProjections<'note'> = {",
35
- " note: {",
36
- " body: (fields) => fields.notes ?? '',",
37
- " title: (fields) => fields.heading ?? '',",
38
- " },",
39
- "}"
40
- ],
41
- field: "search",
42
- fieldType: "SearchIndex<'note'>",
43
- imports: [
44
- "import { pgFtsSearchIndex, type SearchIndex, type SearchProjections } from '@geonosis/search'"
45
- ],
46
- name: "@geonosis/search",
47
- version: FLOOR_VERSIONS["@geonosis/search"]
48
- };
49
- var DOCUMENT = {
50
- at: "domains/document",
51
- built: [" document: DOCUMENT_ENTITIES,"],
52
- declarations: [
53
- "/** Every `Document.extend` this repo declares, by the name its rows carry \u2014 grow this map. */",
54
- "const DOCUMENT_ENTITIES = documentEntities({})"
55
- ],
56
- field: "document",
57
- fieldType: "typeof DOCUMENT_ENTITIES",
58
- imports: ["import { documentEntities } from '@geonosis/document'"],
59
- name: "@geonosis/document",
60
- version: FLOOR_VERSIONS["@geonosis/document"]
61
- };
62
- var DOMAINS = [SEARCH, DOCUMENT];
63
- var DOMAIN_NAMES = DOMAINS.map((one) => one.field);
64
- var FOUNDATIONS = [
65
- { at: "foundations/db", name: "@geonosis/db", version: FLOOR_VERSIONS["@geonosis/db"] },
66
- {
67
- at: "foundations/events",
68
- name: "@geonosis/events",
69
- version: FLOOR_VERSIONS["@geonosis/events"]
70
- }
71
- ];
72
- var domainsNamed = (named) => named.map((one) => {
73
- const found = DOMAINS.find((domain) => domain.field === one);
74
- if (found === void 0) {
75
- throw new Error(
76
- `--domains "${one}" is not a domain this kit ships \u2014 it ships ${DOMAIN_NAMES.join(", ")}. A domain nothing publishes cannot be composed, and a scaffold that wrote the import anyway would leave a repo that does not install.`
77
- );
78
- }
79
- return found;
80
- });
81
- var floorDependencies = (named) => named.length === 0 ? {} : Object.fromEntries(
82
- [...FOUNDATIONS, ...domainsNamed(named)].map((one) => [one.name, `^${one.version}`]).toSorted(([a], [b]) => String(a).localeCompare(String(b), "en-US"))
83
- );
84
- var floorDirectories = (named) => named.length === 0 ? [] : [...FOUNDATIONS, ...domainsNamed(named)].map((one) => one.at);
85
- var platformSource = (named) => {
86
- const domains = domainsNamed(named);
87
- return [
88
- "import { createSessionSeam, type Executor, type SessionSeam } from '@geonosis/db'",
89
- "import { createEmitDoor, type EmitDoor, type Extensions } from '@geonosis/events'",
90
- ...domains.flatMap((one) => one.imports),
91
- "",
92
- "/** Every name a foundation cannot pick for this repo, answered here and only here. */",
93
- "export type PlatformConfig = {",
94
- " newId: () => string",
95
- " nowMs: () => number",
96
- " /** What maintenance sets to see every tenant, through a lever no request path reaches. */",
97
- " opsSetting: string",
98
- " /** The producer every envelope names (D-057) \u2014 spelled here, never at an emit call. */",
99
- " source: string",
100
- " /** What a transaction sets to name its tenant, and what the RLS policies read. */",
101
- " tenantSetting: string",
102
- "}",
103
- "",
104
- "export type Definitions = {",
105
- " emit: EmitDoor<Extensions>",
106
- " nowMs: () => number",
107
- " session: SessionSeam",
108
- "}",
109
- "",
110
- "/** Once per isolate: what this platform is, before anybody has asked it anything. */",
111
- "export const definitionsOf = (config: PlatformConfig): Definitions => ({",
112
- " emit: createEmitDoor({",
113
- " extensions: [],",
114
- " newId: config.newId,",
115
- " now: config.nowMs,",
116
- " source: config.source,",
117
- " }),",
118
- " nowMs: config.nowMs,",
119
- " session: createSessionSeam({",
120
- " settings: { opsSetting: config.opsSetting, tenantSetting: config.tenantSetting },",
121
- " }),",
122
- "})",
123
- "",
124
- ...domains.flatMap((one) => [...one.declarations, ""]),
125
- "/** Who is asking, and the connection they may ask on. */",
126
- "export type Caller = { connection: Executor; tenantId: string }",
127
- "",
128
- "/** Every domain this repo composed, built for one caller. */",
129
- "export type Scope = {",
130
- ...domains.map((one) => ` ${one.field}: ${one.fieldType}`),
131
- "}",
132
- "",
133
- "/**",
134
- " * Per request. The tenant session opens FIRST and the stores are built inside it, so a store",
135
- " * that could run outside the transaction that named its tenant does not exist to be held.",
136
- " */",
137
- "export const inScope = async <Result>(",
138
- " definitions: Definitions,",
139
- " caller: Caller,",
140
- " run: (scope: Scope) => Promise<Result>,",
141
- "): Promise<Result> =>",
142
- " definitions.session.inTenant(caller.connection, caller.tenantId, async (tx) =>",
143
- " run({",
144
- ...domains.flatMap((one) => [...one.built]),
145
- " }),",
146
- " )",
147
- ""
148
- ].join("\n");
149
- };
150
-
151
- // src/answers.ts
152
- import { basename, resolve } from "path";
153
- var RUNTIMES = ["bun", "pnpm"];
154
- var BACKENDS = ["medusa", "none", "sagaflow-cf"];
155
- var UIS = ["atoms-only", "none", "tiered-shadcn"];
156
- var MEDUSA_RANGE = "^2.19.0";
157
- var MEDUSA_PACKAGES = ["@medusajs/framework", "@medusajs/medusa"];
158
- var VALUED = /* @__PURE__ */ new Set([
159
- "--backend",
160
- "--presets",
161
- "--brand",
162
- "--domains",
163
- "--kit",
164
- "--marketplace",
165
- "--medusa",
166
- "--runtime",
167
- "--themekit",
168
- "--ui"
169
- ]);
170
- var SWITCHES = /* @__PURE__ */ new Set(["--check", "--help", "-h", "--no-install", "--plan"]);
171
- var parseArgs = (argv) => {
172
- const flags = {};
173
- let dir;
174
- for (let at = 0; at < argv.length; at += 1) {
175
- const token = argv[at] ?? "";
176
- if (!token.startsWith("--")) {
177
- if (dir === void 0) dir = token;
178
- continue;
179
- }
180
- const equals = token.indexOf("=");
181
- const flag = equals === -1 ? token : token.slice(0, equals);
182
- if (!VALUED.has(flag) && !SWITCHES.has(flag)) {
183
- throw new Error(
184
- `${flag} is not an option create-geonosis takes. Run create-geonosis --help for the ones it does.`
185
- );
186
- }
187
- if (equals !== -1) {
188
- flags[flag] = token.slice(equals + 1);
189
- continue;
190
- }
191
- if (SWITCHES.has(token)) {
192
- flags[token] = "true";
193
- continue;
194
- }
195
- const value = argv[at + 1];
196
- if (value === void 0) throw new Error(`${token} needs a value`);
197
- flags[token] = value;
198
- at += 1;
199
- }
200
- return { ...dir === void 0 ? {} : { dir }, flags };
201
- };
202
- var choose = (flag, given, allowed) => {
203
- if (given === void 0) {
204
- throw new Error(`${flag} is required \u2014 it is one of ${allowed.join(", ")}, and never a default`);
205
- }
206
- if (!allowed.includes(given)) {
207
- throw new Error(`${flag} "${given}" is not one of ${allowed.join(", ")}`);
208
- }
209
- return given;
210
- };
211
- var scopeOf = (name) => `@${name.toLowerCase().replaceAll(/[^a-z0-9-]/g, "-")}`;
212
- var domainsFrom = (given) => {
213
- const named = (given ?? "").split(",").map((one) => one.trim()).filter((one) => one !== "");
214
- domainsNamed(named);
215
- return named;
216
- };
217
- var parseAnswers = (argv, cwd) => {
218
- const { dir, flags } = parseArgs(argv);
219
- if (dir === void 0) {
220
- throw new Error("create-geonosis needs a directory to write the repo into");
221
- }
222
- if (flags["--presets"] !== void 0) {
223
- throw new Error(
224
- "--presets is a --plan option: a scaffold takes its presets from --backend and --ui, and would ignore the ones named here"
225
- );
226
- }
227
- const name = basename(resolve(cwd, dir));
228
- const domains = domainsFrom(flags["--domains"]);
229
- return {
230
- backend: choose("--backend", flags["--backend"], BACKENDS),
231
- ...flags["--brand"] === void 0 ? {} : { brand: flags["--brand"] },
232
- dir: resolve(cwd, dir),
233
- domains,
234
- ...flags["--kit"] === void 0 ? {} : { kit: resolve(cwd, flags["--kit"]) },
235
- ...flags["--marketplace"] === void 0 ? {} : { marketplace: flags["--marketplace"] },
236
- ...flags["--medusa"] === void 0 ? {} : { medusa: flags["--medusa"] },
237
- name,
238
- runtime: choose("--runtime", flags["--runtime"], RUNTIMES),
239
- scope: scopeOf(name),
240
- ...flags["--themekit"] === void 0 ? {} : { themekit: flags["--themekit"] },
241
- ui: choose("--ui", flags["--ui"], UIS)
242
- };
243
- };
244
- var wantsInstall = (argv) => !argv.includes("--no-install");
245
-
246
- // src/corpus.ts
247
- var CORPUS_DIR = "lint-corpus";
248
- var PREFIX = "biological-architecture";
249
- var corpusFor = (answers, workspaces) => {
250
- const files = {};
251
- const rules = [];
252
- const walled = workspaces.find((one) => one.mayDependOn.length === 0);
253
- const forbidden = workspaces.find((one) => one.tag === "app");
254
- if (walled !== void 0 && forbidden !== void 0) {
255
- rules.push(`${PREFIX}/layer-walls`);
256
- files[`${CORPUS_DIR}/${walled.dir}/src/bad-reaches-up.ts`] = `// BREACH: the ${walled.tag} layer may import nothing, and this reaches ${forbidden.tag}.
257
- import { boot } from '${forbidden.name}/boot'
258
-
259
- export const reachesUp = () => boot
260
- `;
261
- }
262
- if (answers.brand !== void 0) {
263
- rules.push(`${PREFIX}/no-brand-names`);
264
- files[`${CORPUS_DIR}/brand/bad-brand.ts`] = `// BREACH: the brand name spelled into source, which is what the rule is configured to find.
265
- export const FOOTER_NOTE = 'Powered by ${answers.brand}'
266
- `;
267
- }
268
- if (answers.backend === "medusa") {
269
- rules.push(`${PREFIX}/plugin-route-namespaced`);
270
- files[`${CORPUS_DIR}/packages/medusa-plugins/example/src/api/store/route.ts`] = `// BREACH: a store route with no namespace under it, in a plugin package.
271
- export async function GET(req: unknown, res: { json: (body: unknown) => void }) {
272
- res.json({ ok: true, req })
273
- }
274
- `;
275
- }
276
- if (answers.backend === "sagaflow-cf") {
277
- rules.push(`${PREFIX}/step-opens-its-own-cell`);
278
- files[`${CORPUS_DIR}/packages/backend/src/workflows/bad-engine-step.ts`] = `// BREACH: a saga inside the configured \`within\` reaching the engine's own step.
279
- import { step } from 'sagaflow-js'
280
-
281
- export const issue = step('issue', async () => ({ ok: true }))
282
- `;
283
- }
284
- files[`${CORPUS_DIR}/manifest.json`] = `${JSON.stringify({ plugin: PREFIX, rules }, void 0, 2)}
285
- `;
286
- return files;
287
- };
288
-
289
- // src/layout.ts
290
- var CORE = `export type Money = { amount: number; currency: string }
291
-
292
- export const totalOf = (lines: readonly Money[], currency: string): Money => ({
293
- amount: lines.filter((one) => one.currency === currency).reduce((sum, one) => sum + one.amount, 0),
294
- currency,
295
- })
296
- `;
297
- var CORE_TEST = (scope) => `import { describe, expect, it } from 'vitest'
298
-
299
- import { totalOf } from './money'
300
-
301
- describe('totalOf', () => {
302
- it('adds the lines that share the currency', () => {
303
- expect(totalOf([{ amount: 2, currency: 'EUR' }, { amount: 3, currency: 'EUR' }], 'EUR')).toEqual(
304
- { amount: 5, currency: 'EUR' },
305
- )
306
- })
307
-
308
- it('ignores the ones that do not \u2014 ${scope}/core never guesses a rate', () => {
309
- expect(totalOf([{ amount: 2, currency: 'EUR' }, { amount: 9, currency: 'USD' }], 'EUR').amount)
310
- .toBe(2)
311
- })
312
- })
313
- `;
314
- var BUTTON = `import type { ComponentProps } from 'react'
315
-
316
- export function Button(props: ComponentProps<'button'>) {
317
- return <button type="button" {...props} />
318
- }
319
- `;
320
- var FIELD = (scope) => `import { Button } from '${scope}/ui/atoms/button'
321
-
322
- export function Field({ label, onSubmit }: { label: string; onSubmit: () => void }) {
323
- return (
324
- <label>
325
- {label}
326
- <Button onClick={onSubmit}>Go</Button>
327
- </label>
328
- )
329
- }
330
- `;
331
- var UI_TEST = `import { describe, expect, it } from 'vitest'
332
-
333
- import { Button } from './atoms/button'
334
-
335
- describe('Button', () => {
336
- it('is a component, so a tier above can compose it', () => {
337
- expect(typeof Button).toBe('function')
338
- })
339
- })
340
- `;
341
- var MEDUSA_SUBSCRIBER = `export const config = {
342
- context: { subscriberId: 'order-placed' },
343
- event: 'order.placed',
344
- }
345
-
346
- export default async function orderPlaced({ data }: { data: { id: string } }): Promise<string> {
347
- return data.id
348
- }
349
- `;
350
- var SAGAFLOW_STEP = `export const step = <Input, Output>(
351
- name: string,
352
- body: (input: Input) => Promise<Output>,
353
- undo: (output: Output) => Promise<void>,
354
- ) => ({ body, name, undo })
355
- `;
356
- var SAGAFLOW_FLOW = `import { step } from './step'
357
-
358
- export const reserve = step(
359
- 'reserve',
360
- async (input: { id: string }) => ({ held: input.id }),
361
- async () => undefined,
362
- )
363
- `;
364
- var BACKEND_TEST = `import { describe, expect, it } from 'vitest'
365
-
366
- import { handler } from './handler'
367
-
368
- describe('the backend seam', () => {
369
- it('answers with what it was asked about', async () => {
370
- expect(await handler({ id: 'ord_1' })).toBe('ord_1')
371
- })
372
- })
373
- `;
374
- var HANDLER = `export const handler = async ({ id }: { id: string }): Promise<string> => id
375
- `;
376
- var APP = (scope, ui) => ui ? `import { totalOf } from '${scope}/core/money'
377
- import { Button } from '${scope}/ui/atoms/button'
378
-
379
- export const boot = () => ({ button: Button, total: totalOf([], 'EUR') })
380
- ` : `import { totalOf } from '${scope}/core/money'
381
-
382
- export const boot = () => ({ total: totalOf([], 'EUR') })
383
- `;
384
- var APP_TEST = `import { describe, expect, it } from 'vitest'
385
-
386
- import { boot } from './boot'
387
-
388
- describe('boot', () => {
389
- it('starts with an empty total rather than a guess', () => {
390
- expect(boot().total.amount).toBe(0)
391
- })
392
- })
393
- `;
394
- var workspacesOf = (answers) => {
395
- const { scope, ui } = answers;
396
- const list = [
397
- {
398
- dir: "packages/core",
399
- mayDependOn: [],
400
- name: `${scope}/core`,
401
- sources: { "src/money.ts": CORE, "src/money.test.ts": CORE_TEST(scope) },
402
- tag: "core"
403
- }
404
- ];
405
- if (ui !== "none") {
406
- list.push({
407
- dir: "packages/ui",
408
- mayDependOn: ["core"],
409
- name: `${scope}/ui`,
410
- sources: {
411
- "src/atoms/button.tsx": BUTTON,
412
- "src/button.test.ts": UI_TEST,
413
- ...ui === "tiered-shadcn" ? { "src/molecules/field.tsx": FIELD(scope) } : {}
414
- },
415
- tag: "ui"
416
- });
417
- }
418
- if (answers.backend !== "none") {
419
- list.push({
420
- dir: "packages/backend",
421
- mayDependOn: ["core"],
422
- name: `${scope}/backend`,
423
- ...answers.backend === "medusa" ? {
424
- packages: Object.fromEntries(
425
- MEDUSA_PACKAGES.map((one) => [one, answers.medusa ?? MEDUSA_RANGE])
426
- )
427
- } : {},
428
- sources: {
429
- "src/handler.ts": HANDLER,
430
- "src/handler.test.ts": BACKEND_TEST,
431
- ...answers.backend === "medusa" ? { "src/subscribers/order-placed.ts": MEDUSA_SUBSCRIBER } : { "src/workflows/step.ts": SAGAFLOW_STEP, "src/workflows/reserve.ts": SAGAFLOW_FLOW }
432
- },
433
- tag: "backend"
434
- });
435
- }
436
- list.push({
437
- dir: "apps/web",
438
- mayDependOn: list.map((one) => one.tag),
439
- name: `${scope}/web`,
440
- packages: floorDependencies(answers.domains),
441
- sources: {
442
- "src/boot.ts": APP(scope, ui !== "none"),
443
- "src/boot.test.ts": APP_TEST,
444
- ...answers.domains.length === 0 ? {} : { [PLATFORM_FILE]: platformSource(answers.domains) }
445
- },
446
- tag: "app"
447
- });
448
- return list;
449
- };
450
-
451
- // src/files.ts
452
- import {
453
- countersFor,
454
- LAW_SOURCE,
455
- LOADED_VERSION,
456
- oxlintConfigFor,
457
- rulesFor,
458
- tiersFor
459
- } from "@geonosis/cli";
460
- var VERSION = LOADED_VERSION;
461
- var CATALOG = {
462
- "@types/node": "^24.3.0",
463
- oxfmt: "^0.62.0",
464
- oxlint: "^1.80.0",
465
- typescript: "^5.9.2",
466
- vitest: "^4.1.11"
467
- };
468
- var REACT = { "@types/react": "^19.0.0", react: "^19.0.0" };
469
- var json = (value) => `${JSON.stringify(value, void 0, 2)}
470
- `;
471
- var exec = (runtime) => runtime === "bun" ? "bunx" : "npx";
472
- var run = (runtime) => runtime === "bun" ? "bun run" : "pnpm";
473
- var everyWorkspace = (runtime, script) => runtime === "bun" ? `bun run --filter '*' ${script}` : `pnpm -r ${script}`;
474
- var presetsFor = (answers) => [
475
- "tooling",
476
- ...answers.ui === "none" ? [] : ["frontend"],
477
- ...answers.backend === "none" ? [] : ["tenant-scoping"],
478
- ...answers.backend === "medusa" ? ["backend-medusa"] : [],
479
- // #197 (plugin): `backend-sagaflow-cf` is a deprecated alias for `backend-workflows-cf` that
480
- // resolves to the same rules until the next major, then goes — ask for the current name, never
481
- // the alias, so a scaffolded repo's config does not go stale the day the alias is dropped.
482
- ...answers.backend === "sagaflow-cf" ? ["backend-workflows-cf"] : [],
483
- ...answers.themekit === void 0 ? [] : ["themekit"]
484
- ];
485
- var tokenPrefix = (name) => `--${name.toLowerCase().replaceAll(/[^a-z]/g, "").slice(0, 3)}-`;
486
- var answersForRules = (answers) => ({
487
- "layer-walls": ["error", { layers: [] }],
488
- // The scope is allowed on purpose: a repo whose brand is also its package scope would otherwise
489
- // have every one of its own import paths reported as a brand leak, and the first thing anybody
490
- // would do about that is turn the rule off.
491
- ...answers.brand === void 0 ? {} : {
492
- "no-brand-names": [
493
- "error",
494
- { allowedSubstrings: [`${answers.scope}/`], brands: [answers.brand] }
495
- ]
496
- },
497
- ...answers.ui === "none" ? {} : {
498
- "no-raw-html-atoms": [
499
- "error",
500
- [{ atom: "Button", element: "button", importPath: `${answers.scope}/ui/atoms/button` }]
501
- ],
502
- "no-runtime-dead-affordance": [
503
- "error",
504
- [{ atom: "Button", element: "button", importPath: `${answers.scope}/ui/atoms/button` }]
505
- ]
506
- },
507
- ...answers.backend === "medusa" ? {
508
- "plugin-route-namespaced": [
509
- "error",
510
- {
511
- packages: [
512
- {
513
- name: `${answers.scope}/{dir}`,
514
- namespaced: true,
515
- under: "packages/medusa-plugins"
516
- }
517
- ]
518
- }
519
- ]
520
- } : {},
521
- ...answers.backend === "sagaflow-cf" ? {
522
- "step-opens-its-own-cell": [
523
- "error",
524
- {
525
- engine: "sagaflow-js",
526
- within: "packages/backend/src/workflows/",
527
- wrapper: "packages/backend/src/workflows/step\\.ts$"
528
- }
529
- ]
530
- } : {},
531
- ...answers.themekit === void 0 ? {} : {
532
- "font-roles-only": ["error", { roles: ["body", "display"] }],
533
- "tokens-only-colors": ["error", { tokenPrefix: [tokenPrefix(answers.name)] }]
534
- }
535
- });
536
- var exportsOf = (workspace) => Object.fromEntries(
537
- Object.keys(workspace.sources).filter((file) => !file.includes(".test.")).toSorted().map((file) => [`./${file.replace(/^src\//, "").replace(/\.tsx?$/, "")}`, `./${file}`])
538
- );
539
- var workspaceManifest = (workspace, answers, workspaces) => {
540
- const react = workspace.dir === "packages/ui";
541
- return json({
542
- name: workspace.name,
543
- version: "0.0.0",
544
- private: true,
545
- type: "module",
546
- exports: exportsOf(workspace),
547
- scripts: {
548
- lint: "oxlint --config ../../.oxlintrc.json .",
549
- // The runner is asked for its own report, never trusted for a status code.
550
- test: "vitest run --reporter=default --reporter=json --outputFile=.geonosis/vitest.json",
551
- typecheck: "tsc --noEmit"
552
- },
553
- devDependencies: {
554
- "@types/node": "catalog:",
555
- ...react ? { "@types/react": "catalog:", react: "catalog:" } : {},
556
- oxlint: "catalog:",
557
- typescript: "catalog:",
558
- vitest: "catalog:"
559
- },
560
- dependencies: {
561
- ...Object.fromEntries(
562
- workspace.mayDependOn.flatMap((tag) => {
563
- const found = workspaces.find((one) => one.tag === tag);
564
- return found === void 0 || found.name === workspace.name ? [] : [[found.name, "workspace:*"]];
565
- })
566
- ),
567
- // With --kit the FLOORS are linked from a workspace AFTER the install, and the manifest is
568
- // rewritten to say so — the same reason the root's kit versions are left out. Everything else
569
- // a workspace declares is third-party and stays: an engine dropped here is an engine the
570
- // doctor cannot find, and a rule that presumes it then reads as a rule with no finding.
571
- ...Object.fromEntries(
572
- Object.entries(workspace.packages ?? {}).filter(
573
- ([name]) => answers.kit === void 0 || !name.startsWith("@geonosis/")
574
- )
575
- )
576
- }
577
- });
578
- };
579
- var kitDependencies = (answers) => ({
580
- "@geonosis/cli": `^${VERSION}`,
581
- "@geonosis/doctor": `^${VERSION}`,
582
- "@geonosis/ledger": `^${VERSION}`,
583
- "@geonosis/oxlint-plugin-biological-architecture": `^${VERSION}`,
584
- "@geonosis/ratchet": `^${VERSION}`,
585
- "@geonosis/verify": `^${VERSION}`,
586
- ...answers.themekit === void 0 ? {} : { "@geonosis/themekit": `^${VERSION}` }
587
- });
588
- var GRAPH_SYNCS = [
589
- { target: "turbo", write: "turbo.json" },
590
- { target: "layer-walls", write: ".oxlintrc.json" }
591
- ];
592
- var graphScript = GRAPH_SYNCS.map(
593
- ({ target, write: write2 }) => `geonosis-ledger sync --target ${target} --write ${write2}`
594
- ).join(" && ");
595
- var HOOK_INSTALLER = "scripts/install-hooks.mjs";
596
- var hookInstaller = () => [
597
- "import { spawnSync } from 'node:child_process'",
598
- "import { existsSync, lstatSync } from 'node:fs'",
599
- "import { join, resolve } from 'node:path'",
600
- "",
601
- "const git = (...args) => {",
602
- " const done = spawnSync('git', args, { encoding: 'utf8' })",
603
- "",
604
- " return done.status === 0 ? done.stdout.trim() : undefined",
605
- "}",
606
- "",
607
- "const say = (line) => process.stdout.write(`install-hooks: ${line}\n`)",
608
- "",
609
- "const common = git('rev-parse', '--path-format=absolute', '--git-common-dir')",
610
- "if (common === undefined) {",
611
- " say('git says nothing about this directory, so there is no hooks path to install into.')",
612
- " process.exit(0)",
613
- "}",
614
- "",
615
- "// A worktree's own .git is a FILE; the hooks it would install into belong to the checkout the",
616
- "// common dir names, and writing them from here changes the tree somebody else is in.",
617
- "const here = resolve(process.cwd(), '.git')",
618
- "if (existsSync(here) && lstatSync(here).isFile() && resolve(common) !== here) {",
619
- " say(`this is a git worktree and its hooks path is shared with ${common} \u2014 install them there instead.`)",
620
- " process.exit(0)",
621
- "}",
622
- "",
623
- "const lefthook = join(process.cwd(), 'node_modules/.bin/lefthook')",
624
- "if (!existsSync(lefthook)) {",
625
- " say('lefthook is not installed here yet, so no hook was written.')",
626
- " process.exit(0)",
627
- "}",
628
- "",
629
- "const done = spawnSync(lefthook, ['install'], { stdio: 'inherit' })",
630
- "process.exit(done.status ?? 0)",
631
- ""
632
- ].join("\n");
633
- var rootScripts = ({ runtime }) => ({
634
- doctor: "geonosis-doctor",
635
- prepare: `node ${HOOK_INSTALLER}`,
636
- "format:check": "oxfmt --config .oxfmtrc.json --check .",
637
- format: "oxfmt --config .oxfmtrc.json .",
638
- lint: "oxlint --config .oxlintrc.json .",
639
- // A counter nothing has ever seen read its own planted finding has not been shown to measure.
640
- prove: "geonosis-ratchet --prove",
641
- ratchet: "geonosis-ratchet",
642
- "sync:graph": graphScript,
643
- sync: "geonosis sync",
644
- test: everyWorkspace(runtime, "test"),
645
- typecheck: everyWorkspace(runtime, "typecheck"),
646
- verify: "geonosis-verify"
647
- });
648
- var rootManifest = (answers) => {
649
- const { runtime } = answers;
650
- const globs = ["apps/*", "packages/*"];
651
- return json({
652
- name: answers.name,
653
- version: "0.0.0",
654
- private: true,
655
- type: "module",
656
- ...runtime === "bun" ? {
657
- workspaces: {
658
- catalog: { ...CATALOG, ...answers.ui === "none" ? {} : REACT },
659
- packages: globs
660
- }
661
- } : {},
662
- scripts: rootScripts(answers),
663
- devDependencies: {
664
- // With --kit the kit is linked from a workspace AFTER the install, and the manifest is
665
- // rewritten to say so. Declaring published versions here would make the install go looking
666
- // for them first, which is the one thing --kit exists to avoid.
667
- ...answers.kit === void 0 ? kitDependencies(answers) : {},
668
- "@types/node": "catalog:",
669
- ...answers.ui === "none" ? {} : { "@types/react": "catalog:", react: "catalog:" },
670
- lefthook: "^2.1.0",
671
- oxfmt: "catalog:",
672
- oxlint: "catalog:",
673
- turbo: "^2.10.0",
674
- typescript: "catalog:",
675
- vitest: "catalog:"
676
- }
677
- });
678
- };
679
- var workspaceYaml = (answers) => [
680
- "packages:",
681
- " - apps/*",
682
- " - packages/*",
683
- "",
684
- "# One version of a shared dependency, named once. A repo whose workspaces each pin their own",
685
- "# typescript is a repo where one of them typechecks against a compiler the others do not run.",
686
- "catalog:",
687
- ...Object.entries({ ...CATALOG, ...answers.ui === "none" ? {} : REACT }).toSorted(([a], [b]) => a.localeCompare(b, "en-US")).map(([name, spec]) => ` '${name}': '${spec}'`),
688
- "",
689
- "# pnpm 11 runs no dependency's install script unless it is named here. esbuild fetches its",
690
- "# platform binary in one, and without it vitest cannot start \u2014 a test suite that cannot run",
691
- "# reports nothing, which is the shape of green this kit exists to refuse.",
692
- "allowBuilds:",
693
- " esbuild: true",
694
- " lefthook: true",
695
- ""
696
- ].join("\n");
697
- var architecture = (answers, workspaces) => [
698
- `# ${answers.name} \u2014 target architecture`,
699
- "",
700
- "The edges table is the ONE place this graph is written down. `geonosis sync --target turbo`",
701
- "generates turbo's `boundaries.tags` from it, and `--target layer-walls` generates the linter's",
702
- "layers from it, so a tag added here reaches both gates and neither can drift from the other.",
703
- "",
704
- "| tag | packages | may depend on |",
705
- "| --- | --- | --- |",
706
- ...workspaces.map(
707
- (one) => `| ${one.tag} | ${one.name} | ${one.mayDependOn.join(", ") || "\u2014"} |`
708
- ),
709
- "",
710
- ...answers.backend === "medusa" ? [
711
- "Medusa plugins go under `packages/medusa-plugins/<name>`; the `plugin-route-namespaced`",
712
- "rule is configured for that root, so a store route without a namespace is refused.",
713
- ""
714
- ] : []
715
- ].join("\n");
716
- var pipeline = (answers) => [
717
- "# Pipeline: order placed",
718
- "",
719
- "One page per pipeline, and this is the template. Say what enters, what each hop does to it, and",
720
- "what leaves \u2014 then keep the diagram beside the code that implements it.",
721
- "",
722
- "```mermaid",
723
- "graph LR",
724
- ` IN[request] --> APP[${answers.scope}/web]`,
725
- ` APP --> CORE[${answers.scope}/core]`,
726
- ...answers.backend === "none" ? [] : [` APP --> BACK[${answers.scope}/backend]`],
727
- " CORE --> OUT[response]",
728
- "```",
729
- "",
730
- "| Hop | Owns | Fails how |",
731
- "| --- | --- | --- |",
732
- "| entry | shape of the request | refuses, never guesses |",
733
- "| core | the calculation | pure, so it is tested without a server |",
734
- "| exit | the response | one shape, named |",
735
- ""
736
- ].join("\n");
737
- var law = (answers) => [
738
- `# ${answers.name} \u2014 repo doctrine`,
739
- "",
740
- `A ${answers.runtime} monorepo on the geonosis kit. \`docs/architecture.md\` is the graph;`,
741
- "`docs/pipelines/` is one page per flow. Depth belongs in skills and in the rules, never here.",
742
- "",
743
- "## The laws",
744
- "",
745
- "**1. RED \u2192 GREEN.** Every new rule, constraint or invariant begins with a failing test. Write",
746
- "it, watch it fail, then write the code that makes it pass. A test written afterwards validates",
747
- "what was built, not what was intended.",
748
- "",
749
- "**2. Never downgrade a rule.** Setting one to `warn` or `off`, narrowing its globs, adding a",
750
- "file to `ignorePatterns` or silencing it with a disable comment \u2014 all forbidden without asking.",
751
- "A gate breaking because real code violates a real invariant is enforcement working.",
752
- "",
753
- "**3. No suppression.** No `@ts-ignore`, no `@ts-expect-error`. Fix the type, or ask.",
754
- "",
755
- `**4. oxfmt only.** \`${run(answers.runtime)} format\` writes, \`${run(answers.runtime)} format:check\` verifies. Never`,
756
- "prettier: it is not a dependency here, so it downloads itself and reformats with its own defaults.",
757
- "",
758
- "**5. Nothing is hardcoded to a repo.** Every list a rule needs is an option.",
759
- "",
760
- "**6. Test what the code does, not how.** Never assert on class names or DOM structure.",
761
- "",
762
- "## The gates",
763
- "",
764
- "```bash",
765
- "geonosis verify full # typecheck \u2192 lint \u2192 test \u2192 format \u2192 ratchet",
766
- "geonosis doctor # loaded, exercised, baseline, runner, drift",
767
- "```",
768
- "",
769
- "`gate-baseline.json` holds one number per counter, and a number may only shrink. A counter that",
770
- "grew fails; one that shrank rewrites the baseline down in the commit that earned it.",
771
- "",
772
- "## The method",
773
- "",
774
- "The `geonosis` Claude Code plugin carries the hooks, agents and skills:",
775
- "",
776
- "```bash",
777
- `claude plugin marketplace add ${answers.marketplace ?? "microcompanies/geonosis"}`,
778
- "claude plugin install geonosis@geonosis",
779
- "```",
780
- "",
781
- "`AGENTS.md` and `.cursor/rules/` are GENERATED from this file by `geonosis sync` \u2014 edit this",
782
- "one. `lint-corpus/` is fixture code that must stay broken: it is how the doctor proves the rules",
783
- "configured for this repo can fire at all.",
784
- ""
785
- ].join("\n");
786
- var ci = (answers) => [
787
- "name: verify",
788
- "",
789
- "on:",
790
- " push:",
791
- " branches: [main]",
792
- " pull_request:",
793
- "",
794
- "jobs:",
795
- " verify:",
796
- " runs-on: ubuntu-latest",
797
- // #161: a job with no ceiling ran three hours before failing in a consumer. The runner's
798
- // timeout is not the repo's budget — it is what stops a gate nobody can see from billing.
799
- " timeout-minutes: 30",
800
- " steps:",
801
- " - uses: actions/checkout@v4",
802
- ...answers.runtime === "bun" ? [" - uses: oven-sh/setup-bun@v2", " - run: bun install --frozen-lockfile"] : [
803
- " - uses: pnpm/action-setup@v4",
804
- " - uses: actions/setup-node@v4",
805
- " with:",
806
- " node-version: '24'",
807
- " cache: 'pnpm'",
808
- " - run: pnpm install --frozen-lockfile"
809
- ],
810
- ` - run: ${exec(answers.runtime)} geonosis-verify full`,
811
- ` - run: ${exec(answers.runtime)} geonosis-doctor`,
812
- "",
813
- "# When @geonosis/testbed is installed, add its matrix here: one job per adapter the repo",
814
- "# declares in geonosis.json \u2192 testbed.adapters. A job switched off in a condition is a job",
815
- "# that lies, so the disabledCiJobs counter counts them and the ratchet holds the number at 0.",
816
- ""
817
- ].join("\n");
818
- var settings = (answers) => json({
819
- extraKnownMarketplaces: {
820
- geonosis: answers.marketplace === void 0 ? { source: { repo: "microcompanies/geonosis", source: "github" } } : { source: { path: answers.marketplace, source: "directory" } }
821
- },
822
- enabledPlugins: { "geonosis@geonosis": true }
823
- });
824
- var tsconfigBase = (answers) => json({
825
- compilerOptions: {
826
- target: "ES2023",
827
- lib: ["ES2023", ...answers.ui === "none" ? [] : ["DOM"]],
828
- module: "ESNext",
829
- moduleResolution: "bundler",
830
- moduleDetection: "force",
831
- ...answers.ui === "none" ? {} : { jsx: "react-jsx" },
832
- types: ["node"],
833
- strict: true,
834
- noUncheckedIndexedAccess: true,
835
- noImplicitOverride: true,
836
- noFallthroughCasesInSwitch: true,
837
- verbatimModuleSyntax: true,
838
- isolatedModules: true,
839
- skipLibCheck: true,
840
- esModuleInterop: true,
841
- resolveJsonModule: true,
842
- noEmit: true
843
- }
844
- });
845
- var filesFor = (answers) => {
846
- const workspaces = workspacesOf(answers);
847
- const counters = countersFor(
848
- answers.runtime,
849
- LAW_SOURCE,
850
- workspaces.map((one) => ({ dir: one.dir, name: one.name }))
851
- );
852
- const { rules } = rulesFor(presetsFor(answers), answersForRules(answers));
853
- const files = {
854
- "package.json": rootManifest(answers),
855
- ...answers.runtime === "pnpm" ? { "pnpm-workspace.yaml": workspaceYaml(answers) } : {},
856
- // D-054 from day one: the law is written once, CLAUDE.md imports it, and the three
857
- // declarations below point at the same file — a repo with nothing to migrate later.
858
- [LAW_SOURCE]: law(answers),
859
- "CLAUDE.md": `@${LAW_SOURCE}
860
- `,
861
- "docs/architecture.md": architecture(answers, workspaces),
862
- "docs/pipelines/order-placed.md": pipeline(answers),
863
- ".github/workflows/ci.yml": ci(answers),
864
- ".claude/settings.json": settings(answers),
865
- ".gitignore": ["node_modules/", "dist/", ".turbo/", ".geonosis/", "coverage/", ""].join("\n"),
866
- ".oxfmtrc.json": json({
867
- printWidth: 100,
868
- singleQuote: true,
869
- semi: false,
870
- trailingComma: "none",
871
- arrowParens: "always",
872
- endOfLine: "lf",
873
- tabWidth: 2,
874
- quoteProps: "as-needed",
875
- sortImports: true,
876
- sortPackageJson: true,
877
- ignorePatterns: [
878
- "node_modules",
879
- "dist",
880
- ".turbo",
881
- "coverage",
882
- "**/*.md",
883
- "**/*.json",
884
- CORPUS_DIR
885
- ]
886
- }),
887
- ".oxlintrc.json": json(oxlintConfigFor(rules, [CORPUS_DIR])),
888
- "geonosis.json": json({
889
- verify: tiersFor({
890
- rules,
891
- runtime: answers.runtime,
892
- scripts: Object.keys(rootScripts(answers))
893
- }),
894
- doctor: { corpus: CORPUS_DIR },
895
- law: { file: LAW_SOURCE },
896
- ledger: {
897
- architecture: "docs/architecture.md",
898
- law: LAW_SOURCE,
899
- plans: "plans",
900
- progress: "plans/PROGRESS.md",
901
- proofs: "proofs"
902
- },
903
- walk: { probes: [], urls: [] },
904
- testbed: {
905
- adapters: answers.backend === "medusa" ? ["medusa-built"] : answers.backend === "sagaflow-cf" ? ["workerd"] : []
906
- }
907
- }),
908
- "geonosis.ratchet.json": json({ baseline: "gate-baseline.json", counters }),
909
- "gate-baseline.json": json(
910
- Object.fromEntries(
911
- counters.map((one) => [
912
- typeof one.key === "string" ? one.key : String(one.counter),
913
- // Zero is the floor for debt this repo has not earned. It is the wrong number for a
914
- // counter that measures something already written: the law has its length the moment it
915
- // lands, and a baseline of 0 would fail the first ratchet run over a clean tree.
916
- one.counter === "lawLineCount" ? law(answers).split("\n").length - 1 : 0
917
- ])
918
- )
919
- ),
920
- "skills-lock.json": json({ skills: {}, version: 1 }),
921
- [HOOK_INSTALLER]: hookInstaller(),
922
- "lefthook.yml": [
923
- "# Staged scope only, so a commit never waits on a file it did not touch. When one fires, fix",
924
- "# or delete the code \u2014 never downgrade the rule to get past it.",
925
- "#",
926
- "# Each binary is called through node_modules/.bin, never through npx, bunx or pnpm: a git",
927
- "# hook's PATH is not the shell's and carries no version-manager shim, so the runner fails to",
928
- "# START and what you read is its message rather than the gate refusing anything.",
929
- "pre-commit:",
930
- " parallel: true",
931
- " commands:",
932
- " fmt:",
933
- " glob: '*.{ts,tsx,js,json}'",
934
- " run: node_modules/.bin/oxfmt --config .oxfmtrc.json --check {staged_files}",
935
- " lint:",
936
- " glob: '*.{ts,tsx}'",
937
- " run: node_modules/.bin/oxlint --config .oxlintrc.json {staged_files}",
938
- ""
939
- ].join("\n"),
940
- "turbo.json": json({
941
- $schema: "https://turborepo.dev/schema.json",
942
- boundaries: { tags: {} },
943
- tasks: {
944
- build: { dependsOn: ["^build"] },
945
- lint: {},
946
- test: { dependsOn: ["^build"] },
947
- typecheck: { dependsOn: ["^typecheck"] }
948
- }
949
- }),
950
- "tsconfig.base.json": tsconfigBase(answers),
951
- "tsconfig.json": json({
952
- extends: "./tsconfig.base.json",
953
- include: [
954
- "apps/*/src/**/*.ts",
955
- "apps/*/src/**/*.tsx",
956
- "packages/*/src/**/*.ts",
957
- "packages/*/src/**/*.tsx"
958
- ],
959
- exclude: ["**/dist/**", "**/node_modules/**", `${CORPUS_DIR}/**`]
960
- }),
961
- ...corpusFor(answers, workspaces)
962
- };
963
- for (const workspace of workspaces) {
964
- files[`${workspace.dir}/package.json`] = workspaceManifest(workspace, answers, workspaces);
965
- files[`${workspace.dir}/tsconfig.json`] = json({
966
- extends: "../../tsconfig.base.json",
967
- include: ["src"]
968
- });
969
- for (const [file, body] of Object.entries(workspace.sources)) {
970
- files[`${workspace.dir}/${file}`] = body;
971
- }
972
- }
973
- return files;
974
- };
975
- var presetsChosenBy = presetsFor;
976
-
977
- // src/check.ts
978
- import { existsSync, readFileSync } from "fs";
979
- import { join } from "path";
980
- var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
981
- var pathsIn = (value, prefix = "") => {
982
- if (!isRecord(value)) return [];
983
- return Object.entries(value).flatMap(([key, child]) => {
984
- const here = prefix === "" ? key : `${prefix}.${key}`;
985
- return [here, ...pathsIn(child, here)];
986
- });
987
- };
988
- var parsed = (body) => {
989
- try {
990
- const value = JSON.parse(body);
991
- return isRecord(value) ? value : void 0;
992
- } catch {
993
- return void 0;
994
- }
995
- };
996
- var missingPaths = (want, has) => {
997
- const held = new Set(pathsIn(has));
998
- return pathsIn(want).filter((path) => !held.has(path));
999
- };
1000
- var isCompositionRoot = (file) => file.endsWith(`/${PLATFORM_FILE}`);
1001
- var checkScaffold = (answers) => {
1002
- const drift = [];
1003
- const seeded = workspacesOf(answers).map((one) => `${one.dir}/`);
1004
- for (const [file, body] of Object.entries(filesFor(answers))) {
1005
- if (!isCompositionRoot(file) && seeded.some((prefix) => file.startsWith(prefix))) continue;
1006
- const at = join(answers.dir, file);
1007
- if (!existsSync(at)) {
1008
- drift.push({ at: "", file, kind: "missing" });
1009
- continue;
1010
- }
1011
- const want = parsed(body);
1012
- if (want === void 0) continue;
1013
- const has = parsed(readFileSync(at, "utf8"));
1014
- if (has === void 0) {
1015
- drift.push({ at: "", file, kind: "unreadable" });
1016
- continue;
1017
- }
1018
- for (const path of missingPaths(want, has)) drift.push({ at: path, file, kind: "key" });
1019
- }
1020
- return drift;
1021
- };
1022
- var SAID = {
1023
- key: (one) => `${one.file} \u2192 ${one.at} \u2014 the scaffold writes this key and your file has none`,
1024
- missing: (one) => `${one.file} \u2014 the scaffold writes this file and your repo has none`,
1025
- unreadable: (one) => `${one.file} \u2014 the scaffold writes plain JSON here and yours is not (comments? trailing commas?), so this check could not read its keys`
1026
- };
1027
- var describeDrift = (drift) => drift.map((one) => SAID[one.kind](one));
1028
-
1029
- // src/scaffold.ts
1030
- import { spawnSync } from "child_process";
1031
- import {
1032
- chmodSync,
1033
- existsSync as existsSync2,
1034
- mkdirSync,
1035
- readdirSync,
1036
- readFileSync as readFileSync2,
1037
- symlinkSync,
1038
- writeFileSync
1039
- } from "fs";
1040
- import { dirname, join as join2 } from "path";
1041
- var KIT_PACKAGES = [
1042
- "cli",
1043
- "doctor",
1044
- "ledger",
1045
- "lint-parity",
1046
- "oxlint-plugin-biological-architecture",
1047
- "ratchet",
1048
- "themekit",
1049
- "verify"
1050
- ];
1051
- var write = (dir, file, body) => {
1052
- const path = join2(dir, file);
1053
- mkdirSync(dirname(path), { recursive: true });
1054
- writeFileSync(path, body);
1055
- };
1056
- var readJson = (path) => JSON.parse(readFileSync2(path, "utf8"));
1057
- var writeScaffold = (answers) => {
1058
- if (existsSync2(answers.dir) && readdirSync(answers.dir).length > 0) {
1059
- throw new Error(`${answers.dir} is not empty \u2014 nothing was written`);
1060
- }
1061
- const files = filesFor(answers);
1062
- mkdirSync(answers.dir, { recursive: true });
1063
- for (const [file, body] of Object.entries(files)) write(answers.dir, file, body);
1064
- return Object.keys(files).toSorted();
1065
- };
1066
- var run2 = (what, dir, command, args) => {
1067
- const done = spawnSync(command, args, { cwd: dir, encoding: "utf8", maxBuffer: 32 * 1024 * 1024 });
1068
- return {
1069
- code: done.error === void 0 ? done.status ?? 2 : 2,
1070
- output: `${done.stdout ?? ""}${done.stderr ?? ""}${done.error === void 0 ? "" : done.error.message}`,
1071
- what
1072
- };
1073
- };
1074
- var binsOf = (dir) => {
1075
- const manifest = readJson(
1076
- join2(dir, "package.json")
1077
- );
1078
- if (typeof manifest.bin === "string") return [[manifest.name ?? "", manifest.bin]];
1079
- return Object.entries(manifest.bin ?? {});
1080
- };
1081
- var repoint = (path, at, owed) => {
1082
- if (!existsSync2(path)) return [];
1083
- const manifest = readJson(path);
1084
- const moved = [];
1085
- for (const block of ["dependencies", "devDependencies"]) {
1086
- const declared = manifest[block];
1087
- if (declared === void 0) continue;
1088
- for (const name of Object.keys(declared)) {
1089
- const from = at.get(name);
1090
- if (from === void 0) continue;
1091
- declared[name] = `link:${from}`;
1092
- moved.push(name);
1093
- }
1094
- }
1095
- for (const name of owed) {
1096
- const from = at.get(name);
1097
- if (from === void 0 || moved.includes(name)) continue;
1098
- manifest.dependencies = { ...manifest.dependencies, [name]: `link:${from}` };
1099
- moved.push(name);
1100
- }
1101
- if (moved.length > 0) writeFileSync(path, `${JSON.stringify(manifest, void 0, 2)}
1102
- `);
1103
- return moved;
1104
- };
1105
- var linkKit = (answers, kit) => {
1106
- const modules = join2(answers.dir, "node_modules");
1107
- const scope = join2(modules, "@geonosis");
1108
- const bin = join2(modules, ".bin");
1109
- mkdirSync(scope, { recursive: true });
1110
- mkdirSync(bin, { recursive: true });
1111
- const linked = [];
1112
- const at = /* @__PURE__ */ new Map();
1113
- const manifestPath = join2(answers.dir, "package.json");
1114
- const manifest = readJson(manifestPath);
1115
- for (const relative of [
1116
- ...KIT_PACKAGES.map((name) => `packages/${name}`),
1117
- ...floorDirectories(answers.domains)
1118
- ]) {
1119
- const from = join2(kit, relative);
1120
- if (!existsSync2(from)) continue;
1121
- const declared = readJson(join2(from, "package.json"));
1122
- if (declared.name === void 0) continue;
1123
- const to = join2(scope, declared.name.replace("@geonosis/", ""));
1124
- if (!existsSync2(to)) symlinkSync(from, to);
1125
- for (const [called, entry] of binsOf(from)) {
1126
- const shim = join2(bin, called);
1127
- writeFileSync(shim, `#!/bin/sh
1128
- exec node "${join2(from, entry)}" "$@"
1129
- `);
1130
- chmodSync(shim, 493);
1131
- }
1132
- at.set(declared.name, from);
1133
- linked.push(relative);
1134
- }
1135
- const owned = new Set(
1136
- workspacesOf(answers).flatMap(
1137
- (workspace) => repoint(
1138
- join2(answers.dir, workspace.dir, "package.json"),
1139
- at,
1140
- Object.keys(workspace.packages ?? {})
1141
- )
1142
- )
1143
- );
1144
- const rootDeclares = new Set(Object.keys(kitDependencies(answers)));
1145
- for (const [name, from] of at) {
1146
- if (!owned.has(name) && rootDeclares.has(name)) {
1147
- manifest.devDependencies = { ...manifest.devDependencies, [name]: `link:${from}` };
1148
- }
1149
- }
1150
- writeFileSync(manifestPath, `${JSON.stringify(manifest, void 0, 2)}
1151
- `);
1152
- return linked;
1153
- };
1154
- var installer = (answers) => answers.runtime === "bun" ? ["bun", ["install"]] : ["pnpm", ["install"]];
1155
- var finish = ({ answers, install }) => {
1156
- const steps = [];
1157
- if (install) {
1158
- const [command, args] = installer(answers);
1159
- const offline = answers.runtime === "pnpm" ? [...args, "--offline"] : args;
1160
- const first = run2(`${command} install (offline)`, answers.dir, command, offline);
1161
- steps.push(first);
1162
- if (first.code !== 0) {
1163
- steps.push(run2(`${command} install (network)`, answers.dir, command, args));
1164
- }
1165
- }
1166
- if (answers.kit !== void 0) {
1167
- const linked = linkKit(answers, answers.kit);
1168
- steps.push({
1169
- code: 0,
1170
- output: `${linked.join(", ")}
1171
- `,
1172
- what: `link the kit from ${answers.kit}`
1173
- });
1174
- }
1175
- const ledger = join2(answers.dir, "node_modules/.bin/geonosis-ledger");
1176
- if (existsSync2(ledger)) {
1177
- for (const { target, write: into } of GRAPH_SYNCS) {
1178
- steps.push(
1179
- run2(`sync the edges table into ${into}`, answers.dir, ledger, [
1180
- "sync",
1181
- "--target",
1182
- target,
1183
- "--write",
1184
- into
1185
- ])
1186
- );
1187
- }
1188
- }
1189
- const oxfmt = join2(answers.dir, "node_modules/.bin/oxfmt");
1190
- if (existsSync2(oxfmt)) {
1191
- steps.push(run2("format", answers.dir, oxfmt, ["--config", ".oxfmtrc.json", "."]));
1192
- }
1193
- return steps;
1194
- };
1195
-
1196
- export {
1197
- PLATFORM_FILE,
1198
- DOMAINS,
1199
- DOMAIN_NAMES,
1200
- FOUNDATIONS,
1201
- domainsNamed,
1202
- floorDependencies,
1203
- floorDirectories,
1204
- platformSource,
1205
- RUNTIMES,
1206
- BACKENDS,
1207
- UIS,
1208
- MEDUSA_RANGE,
1209
- parseArgs,
1210
- scopeOf,
1211
- parseAnswers,
1212
- wantsInstall,
1213
- CORPUS_DIR,
1214
- corpusFor,
1215
- workspacesOf,
1216
- filesFor,
1217
- presetsChosenBy,
1218
- checkScaffold,
1219
- describeDrift,
1220
- KIT_PACKAGES,
1221
- writeScaffold,
1222
- linkKit,
1223
- finish
1224
- };