create-githolon 0.7.0 → 0.7.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/package.json
CHANGED
package/template/domains/todo.ts
CHANGED
|
@@ -7,12 +7,9 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import {
|
|
9
9
|
z, aggregate, t, Lww, AddWins, directive, create, instance, set, addToSet,
|
|
10
|
-
query, count, writeTuple, RelationTuple,
|
|
10
|
+
query, count, writeTuple, RelationTuple, relationTuplesByObject,
|
|
11
11
|
} from "@githolon/dsl";
|
|
12
12
|
|
|
13
|
-
// A todo is open or done. (There is no t.bool() — model a flag as a 2-value Lww enum.)
|
|
14
|
-
const DONE = ["open", "done"] as const;
|
|
15
|
-
|
|
16
13
|
// ── what we store ─────────────────────────────────────────────────────────────
|
|
17
14
|
export const TodoList = aggregate("TodoList", {
|
|
18
15
|
title: t.string().merge(Lww), // last writer wins
|
|
@@ -21,15 +18,10 @@ export const TodoList = aggregate("TodoList", {
|
|
|
21
18
|
export const Todo = aggregate("Todo", {
|
|
22
19
|
listId: t.string().merge(Lww), // which list this todo is on (the query key)
|
|
23
20
|
text: t.string().merge(Lww), // last writer wins
|
|
24
|
-
done: t.
|
|
21
|
+
done: t.bool(), // a flag — Lww by default; reads back a real boolean
|
|
25
22
|
tags: t.set(t.string()).merge(AddWins), // concurrent taggers UNION — nobody's tag is lost
|
|
26
23
|
});
|
|
27
24
|
|
|
28
|
-
// Re-export the framework's Zanzibar tuple aggregate so its events fold into THIS
|
|
29
|
-
// chain. relateSubject (an `.ensures(RelationTuple)` directive) is what marks the
|
|
30
|
-
// tuple an ensured type, which shareList's tuple write below needs.
|
|
31
|
-
export { RelationTuple, relateSubject };
|
|
32
|
-
|
|
33
25
|
// ── what people do (directives PLAN ops; the sealed engine replays them) ────────
|
|
34
26
|
export const createList = directive("createList")
|
|
35
27
|
.creates(TodoList) // Nomos MINTS the id — callers never supply one
|
|
@@ -40,7 +32,7 @@ export const addTodo = directive("addTodo")
|
|
|
40
32
|
.creates(Todo)
|
|
41
33
|
.payload(z.object({ listId: z.string().min(1), text: z.string().min(1) }))
|
|
42
34
|
.plan((p) => {
|
|
43
|
-
create(Todo).set("listId", p.listId).set("text", p.text).set("done",
|
|
35
|
+
create(Todo).set("listId", p.listId).set("text", p.text).set("done", false);
|
|
44
36
|
return [];
|
|
45
37
|
});
|
|
46
38
|
|
|
@@ -49,11 +41,11 @@ export const renameTodo = directive("renameTodo")
|
|
|
49
41
|
.payload(z.object({ todoId: z.string(), text: z.string().min(1) }))
|
|
50
42
|
.plan((p) => [set(instance(Todo, p.todoId), "text", p.text)]);
|
|
51
43
|
|
|
52
|
-
// The caller decides
|
|
44
|
+
// The caller decides done/not-done and rides it IN the payload (a plan is pure — no clock,
|
|
53
45
|
// no random). Concurrent toggles are Lww: the later HLC wins, and both peers converge.
|
|
54
46
|
export const toggleTodo = directive("toggleTodo")
|
|
55
47
|
.mutates(Todo)
|
|
56
|
-
.payload(z.object({ todoId: z.string(), done: z.
|
|
48
|
+
.payload(z.object({ todoId: z.string(), done: z.boolean() }))
|
|
57
49
|
.plan((p) => [set(instance(Todo, p.todoId), "done", p.done)]);
|
|
58
50
|
|
|
59
51
|
// addToSet is the ONLY additive write to an AddWins set (set() would overwrite).
|
|
@@ -77,9 +69,9 @@ export const shareList = directive("shareList")
|
|
|
77
69
|
// ── what people read (auto-discovered by shape; routed to every peer) ───────────
|
|
78
70
|
export const todosByList = query("todosByList").key("listId").returns(Todo);
|
|
79
71
|
|
|
80
|
-
// An O(1) maintained tally: how many todos are still open, per list.
|
|
72
|
+
// An O(1) maintained tally: how many todos are still open (done = false), per list.
|
|
81
73
|
export const openTodosByList = count("openTodosByList")
|
|
82
|
-
.of(Todo).where((p) => p.field("done").eq(
|
|
74
|
+
.of(Todo).where((p) => p.field("done").eq(false)).by("listId");
|
|
83
75
|
|
|
84
76
|
// The raw sharing tuples for a list — the audit surface (re-exported so it routes here).
|
|
85
77
|
export { relationTuplesByObject };
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
// The githolon-compile config: one package, one domain, one module file.
|
|
2
2
|
// Read-side declarations (the query + the count) are auto-discovered from the
|
|
3
3
|
// module's exports by shape — nothing to wire here. The framework RelationTuple
|
|
4
|
-
// aggregate
|
|
4
|
+
// aggregate is auto-injected by shareList's `.ensures(RelationTuple)`; todo.ts only
|
|
5
|
+
// re-exports the relationTuplesByObject query (the read side).
|
|
5
6
|
// Building a Flutter app too? Add `dart: true` for the typed Dart frontend
|
|
6
7
|
// (build/dart/) alongside the TS client.
|
|
7
8
|
export default {
|
package/template/test/e2e.mts
CHANGED
|
@@ -73,7 +73,7 @@ ok("typed offline writes — createList + 2 addTodo, under the pulled law");
|
|
|
73
73
|
const seeded = await app.todosByList({ listId });
|
|
74
74
|
if (seeded.length !== 2) fail(`local query: expected 2 todos, got ${seeded.length}`);
|
|
75
75
|
const t1 = seeded.find((row) => row.data.text === "Draft the deck")!.id;
|
|
76
|
-
await app.toggleTodo({ todoId: t1, done:
|
|
76
|
+
await app.toggleTodo({ todoId: t1, done: true }); // Lww boolean (t.bool)
|
|
77
77
|
await app.tagTodo({ todoId: t1, tags: ["urgent", "q3"] }); // AddWins set
|
|
78
78
|
ok(`read back a minted Todo id (${t1.slice(0, 16)}…) → toggled done + tagged`);
|
|
79
79
|
|