create-githolon 0.101.6 → 0.102.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/index.mjs CHANGED
@@ -17,7 +17,7 @@ var NOMOS_VERSION = (() => {
17
17
  var args = process.argv.slice(2);
18
18
  if (args.includes("-h") || args.includes("--help")) {
19
19
  process.stdout.write(
20
- "create-githolon \u2014 scaffold a Nomos business-law package\n\nUsage:\n npm create githolon <dir> (or: npx create-githolon <dir>)\n\nScaffolds one aggregate, one business birth, the compile config, and a\none-page explanation into <dir> (default: my-holon-app). The generated\nproof exercises the real local engine.\n\nFlags:\n --no-git skip git init\n"
20
+ "create-githolon \u2014 scaffold a Nomos business-law package\n\nUsage:\n npm create githolon <dir> (or: npx create-githolon <dir>)\n\nScaffolds one durable business root, one atomic multi-aggregate intent,\none generated application, and a one-page explanation into <dir>\n(default: my-holon-app). The generated\nproof exercises the real local engine.\n\nFlags:\n --no-git skip git init\n"
21
21
  );
22
22
  process.exit(0);
23
23
  }
@@ -40,8 +40,11 @@ if (existsSync(path.join(targetDir, "vscode"))) {
40
40
  renameSync(path.join(targetDir, "vscode"), path.join(targetDir, ".vscode"));
41
41
  }
42
42
  var appHash = `${appName.replace(/[-\s]+/g, "_").replace(/([a-z0-9])([A-Z])/g, "$1_$2").toUpperCase()}_DOMAIN_HASH`;
43
- var subst = (s) => s.replaceAll("__APP_NAME__", appName).replaceAll("__APP_HASH__", appHash).replaceAll("__NOMOS_VERSION__", NOMOS_VERSION);
44
- for (const f of ["package.json", "README.md", "nomos.package.mjs", "CLAUDE.md", "domains/todo.ts"]) {
43
+ var appDartName = appName.replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "") || "nomos_app";
44
+ var appClassNameRaw = appName.split(/[^A-Za-z0-9]+/).filter(Boolean).map((part) => part[0].toUpperCase() + part.slice(1)).join("") || "NomosApp";
45
+ var appClassName = /^[A-Za-z_]/.test(appClassNameRaw) ? appClassNameRaw : `App${appClassNameRaw}`;
46
+ var subst = (s) => s.replaceAll("__APP_NAME__", appName).replaceAll("__APP_DART_NAME__", appDartName).replaceAll("__APP_CLASS_NAME__", appClassName).replaceAll("__APP_HASH__", appHash).replaceAll("__NOMOS_VERSION__", NOMOS_VERSION);
47
+ for (const f of ["package.json", "README.md", "nomos.package.mjs", "nomos.application.mjs", "CLAUDE.md", "domains/estate.ts"]) {
45
48
  const p = path.join(targetDir, f);
46
49
  if (existsSync(p)) writeFileSync(p, subst(readFileSync(p, "utf8")), "utf8");
47
50
  }
@@ -64,10 +67,10 @@ Nomos in one page: ${path.join(rel, "README.md")}
64
67
  Next:
65
68
  cd ${rel}
66
69
  npm install
67
- npx githolon compile # law + typed client + generated proof
68
- npx githolon proof # births your model in the real local engine
70
+ npm run compile # law + generated business application + proof
71
+ npm run proof # drives the business language in the real local engine
69
72
 
70
- The starter exposes one creation idea: app.birth(businessFacts).
73
+ The starter exposes one intent: commissionAsset(businessFacts).
71
74
  Identity, authorship, persistence, synchronisation, provenance, and recovery stay underneath it.
72
75
  `
73
76
  );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-githolon",
3
- "version": "0.101.6",
3
+ "version": "0.102.1",
4
4
  "type": "module",
5
5
  "description": "Scaffold a Nomos domain package: the starter domain + compile config + live e2e. `npm create githolon my-app`.",
6
6
  "license": "SEE LICENSE IN LICENSE.md",
@@ -1,13 +1,20 @@
1
1
  # __APP_NAME__
2
2
 
3
- This is a Nomos business-law package. Keep application code at the level of aggregates, `birth`, directives, and reads.
3
+ Model the business language. Do not recreate Nomos as application infrastructure.
4
+
5
+ - Aggregates are continuing business identities, not database rows or immutable snapshots.
6
+ - `birth(...)` gives one root aggregate a durable life.
7
+ - Public application writes are `intent(...)` declarations collected by `language(...)`.
8
+ - Directives are private consequences composed beneath an intent; never expose them as an application CRUD API.
9
+ - One intent may atomically change any number of aggregates.
10
+ - Relationships use `t.ref(Aggregate)` and `idOf(Aggregate)`; generated code carries `NomosRef<Aggregate>`.
11
+ - Pass the generated application through the UI as the sole business state. Do not add repositories, SQLite
12
+ mirrors, runtime hosts, session providers, custom sync, ID stores, or replacement aggregate versions.
4
13
 
5
14
  ```bash
6
- npx githolon check
7
- npx githolon compile
8
- npx githolon proof
15
+ npm run compile
16
+ npm run proof
9
17
  ```
10
18
 
11
- `check` reports statically provable refusals. `compile` emits the law and typed client. `proof` exercises the generated business path in the real local engine.
12
-
13
- Do not introduce application-facing identity, signing, storage, deployment, or recovery ceremonies. They belong beneath the generated `birth(payload)` surface.
19
+ If either command exposes framework implementation machinery, treat that as a Nomos defect rather than
20
+ teaching the application to handle it.
@@ -1,59 +1,75 @@
1
1
  # __APP_NAME__
2
2
 
3
- Nomos lets you describe a business and leave the compute science to the framework.
3
+ Nomos lets you describe what the business means and leaves the compute science to the framework.
4
4
 
5
- You write three things:
5
+ You write four things:
6
6
 
7
- - aggregates: the business things that exist;
8
- - birth: the facts required to give one of those things a durable life;
9
- - directives and reads: what may happen to it and what the application may ask.
7
+ - **aggregates** — business things with one continuing identity;
8
+ - **birth** — the facts required to give a root aggregate its own durable life;
9
+ - **private directives** — the consequences Nomos may apply;
10
+ - **public intents** — the business verbs people and applications actually use.
10
11
 
11
- The starter in [`domains/todo.ts`](domains/todo.ts) gives a todo list its own durable life:
12
+ The starter's public verb is `commissionAsset`. It creates an asset and claims its RFID/QR identity in one
13
+ atomic accepted change:
12
14
 
13
15
  ```ts
14
- export const TodoList = aggregate("TodoList", {
15
- reference: t.string({ nonempty: true }).fromBirth(),
16
- title: t.string({ nonempty: true }).fromBirth(),
17
- });
18
-
19
- export const todoList = birth(TodoList, {
20
- key: "reference",
21
- });
16
+ export const commissionAsset = intent("commissionAsset")
17
+ .payload(z.object({
18
+ name: z.string().min(1),
19
+ rfidQrIdentity: z.string().min(1),
20
+ }))
21
+ .directs(createCommissionedAsset, ({ name }) => ({ name }))
22
+ .directs(claimRfidQrIdentity, ({ rfidQrIdentity }) => ({ value: rfidQrIdentity }))
23
+ .atomically();
24
+
25
+ export const estateLanguage = language("estate", [commissionAsset]);
22
26
  ```
23
27
 
24
- The generated client exposes the same business idea:
28
+ The generated Flutter application exposes that same language—not its directives:
25
29
 
26
- ```ts
27
- const list = await app.birth({
28
- reference: "PERSONAL",
29
- title: "Things to do",
30
- });
30
+ ```dart
31
+ final estate = await application.estates.birth(
32
+ const CreateEstateInput(reference: 'E-001', name: 'Foundry'),
33
+ );
34
+
35
+ final NomosRef<Asset> asset = await estate.commissionAsset(
36
+ const CommissionAssetInput(
37
+ name: 'Main electricity meter',
38
+ rfidQrIdentity: 'E20034120123456789012345',
39
+ ),
40
+ );
41
+
42
+ estate.assets.watchOne(asset).listen(render);
31
43
  ```
32
44
 
33
- That is the model. A born aggregate is a holon: its law, facts, and history travel together. It can act locally, survive disconnection, converge with other copies, explain every accepted change, and be reconstructed from its history.
45
+ That is the product surface. `EstateApplication` is the application state. Pass it through the UI; do not
46
+ mirror it in repositories, providers, sessions, caches, SQLite tables, or hand-written sync code.
34
47
 
35
- Nomos owns everything underneath `birth`: durable identity, authorship, law installation, local execution, validation, persistence, synchronisation, provenance, and recovery. Those mechanisms are implementation details, not application design choices.
48
+ `NomosRef<Asset>` is the asset's one identity everywhere. The aggregate is not a database row and an update
49
+ does not create a replacement aggregate: its accepted history changes while its reference stays the same.
50
+
51
+ Nomos owns everything underneath the intent: identity, authorship, validation, atomicity, persistence,
52
+ synchronisation, conflict retention, provenance, access, schema evolution, recovery, and replay.
36
53
 
37
54
  The development loop is one path:
38
55
 
39
56
  ```bash
40
57
  npm install
41
- npx githolon compile
42
- npx githolon proof
58
+ npm run compile
59
+ npm run proof
43
60
  ```
44
61
 
45
- `compile` emits the law, typed client, and a proof generated from this domain. `proof` drives the real engine locally, births the list, discards its resident copy, restores it from durable custody, verifies the full history again, and reads the business root back.
46
-
47
- Make it yours by renaming `TodoList` and changing its fields. `.fromBirth()` means the fact is required to
48
- begin that durable life; its field rule is also the birth validation and generated input type. Nomos derives
49
- the initial write. Keep the rule: application code supplies business facts; Nomos owns the machinery that
50
- makes those facts durable and trustworthy.
62
+ `compile` emits the law and generated business application. `proof` drives the same business language in the
63
+ real local engine, discards resident state, restores it from durable custody, verifies its history, and reads
64
+ the business world back.
51
65
 
52
66
  Useful files:
53
67
 
54
- - [`domains/todo.ts`](domains/todo.ts): the business model;
55
- - [`nomos.package.mjs`](nomos.package.mjs): the modules compiled into the package;
56
- - `build/__APP_NAME__.client.ts`: the generated typed client after `compile`;
57
- - `build/__APP_NAME__.proof.mts`: the generated proof after `compile`.
68
+ - [`domains/estate.ts`](domains/estate.ts): nouns, private consequences, and public language;
69
+ - [`nomos.package.mjs`](nomos.package.mjs): business-law authoring input; it emits a published USD package asset;
70
+ - [`nomos.application.mjs`](nomos.application.mjs): application-composition authoring input; only the emitted `.application.usda` travels;
71
+ - `build/application/__APP_DART_NAME___application/`: the sole Flutter application dependency;
72
+ - `build/__APP_NAME__.proof.mts`: the generated local proof.
58
73
 
59
- If you need to understand distributed systems, cryptography, storage topology, or recovery ceremonies to model the business, that concern has leaked out of Nomos.
74
+ If application code needs to understand workspaces, event sourcing, JWTs, signatures, retries, topology,
75
+ serialization, or merge algorithms, a framework concern has leaked out of Nomos.
@@ -0,0 +1,64 @@
1
+ /**
2
+ * __APP_NAME__ — business nouns, one durable life, and one public business verb.
3
+ *
4
+ * The directives below describe private consequences. Tenant applications receive only
5
+ * `commissionAsset`: one intention which changes the business world atomically.
6
+ */
7
+ import {
8
+ aggregate,
9
+ birth,
10
+ create,
11
+ directive,
12
+ intent,
13
+ language,
14
+ set,
15
+ t,
16
+ z,
17
+ } from "@githolon/dsl";
18
+
19
+ export const Estate = aggregate("Estate", {
20
+ reference: t.string({ nonempty: true }).fromBirth(),
21
+ name: t.string({ nonempty: true }).fromBirth(),
22
+ }).owned();
23
+
24
+ export const estate = birth(Estate, {
25
+ key: "reference",
26
+ });
27
+
28
+ export const Asset = aggregate("Asset", {
29
+ name: t.string({ nonempty: true }),
30
+ }).owned();
31
+
32
+ export const RfidQrIdentityClaim = aggregate("RfidQrIdentityClaim", {
33
+ value: t.string({ nonempty: true }),
34
+ }).public();
35
+
36
+ /** Private consequence: create the commissioned asset. */
37
+ export const createCommissionedAsset = directive("createCommissionedAsset")
38
+ .creates(Asset)
39
+ .payload(z.object({ name: z.string().min(1) }))
40
+ .plan((facts) => {
41
+ create(Asset).set("name", facts.name);
42
+ return [];
43
+ });
44
+
45
+ /** Private consequence: reserve the physical identifier in the same accepted change. */
46
+ export const claimRfidQrIdentity = directive("claimRfidQrIdentity")
47
+ .ensures(RfidQrIdentityClaim)
48
+ .payload(z.object({ value: z.string().min(1) }))
49
+ .plan((facts) => [set(RfidQrIdentityClaim, "value", facts.value)]);
50
+
51
+ /** The application-facing verb: business facts in, one atomic change to the world. */
52
+ export const commissionAsset = intent("commissionAsset")
53
+ .contract({
54
+ accepts: z.object({
55
+ name: z.string().min(1),
56
+ rfidQrIdentity: z.string().min(1),
57
+ }),
58
+ })
59
+ .directs(createCommissionedAsset, ({ name }) => ({ name }))
60
+ .directs(claimRfidQrIdentity, ({ rfidQrIdentity }) => ({ value: rfidQrIdentity }))
61
+ .atomically();
62
+
63
+ /** This is the complete public language. The two directives above remain implementation. */
64
+ export const estateLanguage = language("estate", [commissionAsset]);
@@ -0,0 +1,15 @@
1
+ export default {
2
+ application: "__APP_NAME__",
3
+ root: {
4
+ name: "__APP_CLASS_NAME__",
5
+ kind: "assembly",
6
+ children: [{
7
+ name: "Business",
8
+ kind: "component",
9
+ reference: {
10
+ asset: "./build/__APP_NAME__.asset.usda",
11
+ prim: "/__APP_CLASS_NAME__Business",
12
+ },
13
+ }],
14
+ },
15
+ };
@@ -1,5 +1,15 @@
1
- // The package is just the business-law modules Nomos should compile.
1
+ // The package is just the business language Nomos should compile.
2
2
  export default {
3
3
  name: "__APP_NAME__",
4
- domains: [{ key: "todo", modules: ["./domains/todo.ts"] }],
4
+ rootPart: { name: "__APP_CLASS_NAME__Business", kind: "component" },
5
+ domains: [{ key: "estate", modules: ["./domains/estate.ts"] }],
6
+ dart: {
7
+ out: "./build/application",
8
+ support: "package",
9
+ packages: {
10
+ types: "__APP_DART_NAME___business",
11
+ client: "__APP_DART_NAME___application",
12
+ test: "__APP_DART_NAME___scenarios",
13
+ },
14
+ },
5
15
  };
@@ -3,18 +3,18 @@
3
3
  "version": "0.0.0",
4
4
  "private": true,
5
5
  "type": "module",
6
- "description": "A Nomos business-law package: aggregates, birth, directives, and reads.",
6
+ "description": "A Nomos business application: public intents over private atomic consequences.",
7
7
  "scripts": {
8
- "compile": "githolon compile",
8
+ "compile": "githolon compile ./nomos.package.mjs && githolon compile ./nomos.application.mjs",
9
9
  "proof": "githolon proof",
10
10
  "typecheck": "tsc --noEmit"
11
11
  },
12
12
  "dependencies": {
13
- "@githolon/dsl": "^__NOMOS_VERSION__",
14
- "@githolon/client": "^__NOMOS_VERSION__"
13
+ "@githolon/dsl": "^__NOMOS_VERSION__"
15
14
  },
16
15
  "devDependencies": {
17
16
  "githolon": "^__NOMOS_VERSION__",
17
+ "@githolon/testing": "^__NOMOS_VERSION__",
18
18
  "@types/node": "^25.9.2",
19
19
  "tsx": "^4.19.2",
20
20
  "typescript": "^5.6.3"
@@ -11,7 +11,7 @@
11
11
  },
12
12
  "include": [
13
13
  "domains/**/*.ts",
14
- "build/*.client.ts",
14
+ "build/*.client.d.mts",
15
15
  "build/*.proof.mts",
16
16
  "test/**/*.mts"
17
17
  ]
@@ -1,18 +0,0 @@
1
- /**
2
- * __APP_NAME__ — the complete starter model.
3
- *
4
- * The app supplies business facts. Nomos gives the aggregate a durable life and owns identity,
5
- * authorship, local execution, synchronisation, persistence, provenance, and recovery beneath it.
6
- */
7
- import { aggregate, birth, t } from "@githolon/dsl";
8
-
9
- export const TodoList = aggregate("TodoList", {
10
- /** A stable business key the caller already knows. */
11
- reference: t.string({ nonempty: true }).fromBirth(),
12
- title: t.string({ nonempty: true }).fromBirth(),
13
- }).owned();
14
-
15
- /** The one public creation idea: give a todo list its own durable life. */
16
- export const todoList = birth(TodoList, {
17
- key: "reference",
18
- });