@githolon/client 0.1.1 → 0.1.3

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/LICENSE.md CHANGED
@@ -12,7 +12,7 @@ with it; we keep the rest for now.
12
12
  authorizes;
13
13
  - build, run, and ship applications on top of them — including commercial ones;
14
14
  - keep everything that's yours: code you write, and everything these tools
15
- generate FOR you (scaffolds from `create-holon` / `holon generate`, generated
15
+ generate FOR you (scaffolds from `create-githolon` / `githolon generate`, generated
16
16
  clients, compiled domain packages) carries NO restriction from us — it is
17
17
  yours outright.
18
18
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@githolon/client",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "type": "module",
5
5
  "description": "Nomos Cloud web client — a LOCAL holon for offline-first apps: pulls the workspace ledger (git) from Nomos Cloud, replays it into the in-memory SQLite projection inside the wasm32-wasip1 GitHolon, and exposes dispatch / watch / sync. The browser runs the SAME byte-identical holon artifact the edge runs.",
6
6
  "license": "SEE LICENSE IN LICENSE.md",
@@ -9,8 +9,12 @@
9
9
  "url": "git+https://github.com/Captain-App/nomos2.git",
10
10
  "directory": "cloud/web-client"
11
11
  },
12
+ "types": "./src/index.d.ts",
12
13
  "exports": {
13
- ".": "./src/index.mjs"
14
+ ".": {
15
+ "types": "./src/index.d.ts",
16
+ "default": "./src/index.mjs"
17
+ }
14
18
  },
15
19
  "files": [
16
20
  "src"
package/src/index.d.ts ADDED
@@ -0,0 +1,255 @@
1
+ // ─── @githolon/client — hand-authored declarations for src/index.mjs ───────────────────────────
2
+ //
3
+ // Keep this file honest: every member below mirrors what `connect()` in index.mjs actually
4
+ // builds and returns. The runtime is plain JS; this is its reference manual — hover any
5
+ // member for the semantics (offline-first authoring, edge admission, dead letters).
6
+
7
+ /** Options for {@link connect}. */
8
+ export interface ConnectOptions {
9
+ /** Nomos Cloud base URL (e.g. `https://nomos.captainapp.co.uk`). */
10
+ cloud: string;
11
+ /** Workspace name — must already exist (`POST /v1/workspaces/:ws`). */
12
+ workspace: string;
13
+ /**
14
+ * Stable client identity: drives the untrusted session branch (`session/<clientId>`)
15
+ * and the HLC replica id. Defaults to a random id per connect — pass your own so the
16
+ * same app instance keeps the same offer lane across reconnects.
17
+ */
18
+ clientId?: string;
19
+ /**
20
+ * Bearer token for keyed clouds — needed for `sync()`'s push when the cloud sets
21
+ * `NOMOS_CLOUD_KEY`. Reads and admission stay open without it.
22
+ */
23
+ authToken?: string;
24
+ /**
25
+ * A {@link Holon.export} snapshot: hydrate the workspace directory from these bytes
26
+ * INSTEAD of cloning, then catch up via an automatic pull. Pending un-synced local
27
+ * commits survive the restore and remain syncable.
28
+ */
29
+ restoreFrom?: Uint8Array;
30
+ }
31
+
32
+ /**
33
+ * One projection row: the aggregate's wire type, its id, and the folded fields.
34
+ * Fields are partial folds — read them as possibly-absent (`unknown` per key).
35
+ */
36
+ export interface AggregateRow {
37
+ type: string;
38
+ id: string;
39
+ data: Record<string, unknown>;
40
+ }
41
+
42
+ /**
43
+ * One parked intent in the DEAD-LETTER QUEUE: refused-but-LEGITIMATE work, with full
44
+ * provenance. The intent bytes stay in the queue (and ride `export()`/restore) — work
45
+ * is never lost; the app decides (retry after a law fix, or discard explicitly).
46
+ */
47
+ export interface DeadLetter {
48
+ /** The content-derived intent id (absent only if the commit carried none). */
49
+ id?: string;
50
+ /** Short (10-hex) commit oid of the refused local commit — also accepted as a retry/discard key. */
51
+ oid: string;
52
+ /** Who refused it: `"edge"` (the admission gate) or `"local"` (a replay under changed local law). */
53
+ source: "edge" | "local";
54
+ /** The refusal verdict, verbatim from the law (truncated to 300 chars). */
55
+ error: string;
56
+ /** The intent's domain, when it could be read from the intent bytes. */
57
+ domain: string | null;
58
+ /** The intent's directive id, when it could be read from the intent bytes. */
59
+ directiveId: string | null;
60
+ /** ISO timestamp of the (latest) refusal. */
61
+ at: string;
62
+ }
63
+
64
+ /** One refusal inside an admission report (`deadLettered` marks a domain rejection vs an attack-lane drop). */
65
+ export interface AdmissionRejection {
66
+ oid?: string;
67
+ error?: string;
68
+ /** True when the edge parked the work in the workspace DLQ (domain rejection) rather than dropping it. */
69
+ deadLettered?: boolean;
70
+ [k: string]: unknown;
71
+ }
72
+
73
+ /** One judged session branch inside an admission report. */
74
+ export interface AdmissionSession {
75
+ /** Intents the edge re-admitted and merged to main. */
76
+ admitted?: unknown[];
77
+ /** Intents the edge refused (routed to the DLQ or dropped — see {@link AdmissionRejection.deadLettered}). */
78
+ rejected?: AdmissionRejection[];
79
+ [k: string]: unknown;
80
+ }
81
+
82
+ /**
83
+ * The edge holon's admission verdict (`POST /v1/workspaces/:ws/admit`): each pending
84
+ * session branch judged — every carried intent re-admitted under the workspace's own
85
+ * law (verification, never trust) and merged to `main`, or refused.
86
+ */
87
+ export interface AdmissionReport {
88
+ ok: boolean;
89
+ sessions?: AdmissionSession[];
90
+ [k: string]: unknown;
91
+ }
92
+
93
+ /**
94
+ * Result of {@link Holon.pull} (and the `.converged` leg of {@link Holon.sync}):
95
+ * incremental convergence onto canonical main. When the remote advanced, local main is
96
+ * reset onto it and the local-only intents the canon doesn't already carry (by
97
+ * content-derived id) are rebase-replayed through `apply_intent` — the same
98
+ * re-admission primitive the edge runs.
99
+ */
100
+ export interface PullResult {
101
+ /** True when remote main already equals the local head — nothing to do. */
102
+ upToDate: boolean;
103
+ /** The canonical remote main sha we now sit on. */
104
+ remoteHead: string;
105
+ /** Local-only intents re-admitted locally on top of the adopted canon. */
106
+ replayed?: { oid: string; id?: string | null; head?: string }[];
107
+ /** Local intents the canon already carried (edge-sealed copies, same intent id) — deduped, never double-folded. */
108
+ skipped?: { oid: string; id?: string | null }[];
109
+ /** Replays the local law refused or that errored (domain refusals also land in the DLQ). */
110
+ rejected?: { oid: string; id?: string | null; error: string }[];
111
+ /** Edge-refused attack-lane intents dropped from the lineage (present only when non-empty). */
112
+ dropped?: { oid: string; id?: string | null; edgeRejected: true }[];
113
+ /** Edge- or locally-refused LEGITIMATE work parked in the dead-letter queue (present only when non-empty). */
114
+ deadLettered?: { oid: string; id?: string | null; error?: string }[];
115
+ }
116
+
117
+ /**
118
+ * Result of {@link Holon.sync} — the offline-first exchange:
119
+ * up (push local commits to the untrusted session branch), judge (edge admission),
120
+ * down (converge onto canonical main in the same call).
121
+ */
122
+ export interface SyncResult {
123
+ /** The session branch the local commits were pushed to (`session/<clientId>`), or null when there was nothing local to push. */
124
+ pushed: string | null;
125
+ /**
126
+ * The edge holon's admission verdict for this round. NULL when there was nothing to
127
+ * push (or `admit: false`): a pull-only sync converging in place is normal, not an
128
+ * error — the edge is only asked to judge when we actually offered work.
129
+ */
130
+ admission: AdmissionReport | null;
131
+ /** The in-call convergence (adopt canonical main + rebase-replay) that follows a successful admission; null otherwise. */
132
+ converged: PullResult | null;
133
+ /** True when remote main advanced and we adopted it (the next query folds the delta). */
134
+ pulled: boolean;
135
+ /** True when remote main advanced while we hold un-merged local work — wait for edge admission instead of force-adopting. */
136
+ diverged: boolean;
137
+ /** Always false — convergence happens in place on this instance; no reconnect is ever required. */
138
+ reconnectRequired: boolean;
139
+ /** The local ledger tip at the start of the sync. */
140
+ localHead: string;
141
+ /** Remote main as of this sync (undefined only if the remote advertised no main). */
142
+ remoteMain: string | undefined;
143
+ }
144
+
145
+ /** Outcome of {@link Holon.retryDeadLetter}. */
146
+ export interface RetryDeadLetterResult {
147
+ ok: boolean;
148
+ /** New local head when the retry re-applied cleanly (it rides the next sync). */
149
+ head?: string;
150
+ /** True when the pull found the intent already admitted on canonical main — nothing to re-apply. */
151
+ alreadyOnMain?: boolean;
152
+ /** The (fresh) refusal when the law still says no — the entry stays parked, error updated. */
153
+ error?: string;
154
+ }
155
+
156
+ /**
157
+ * A LOCAL holon: the byte-identical wasm32-wasip1 GitHolon the edge runs, hydrated from
158
+ * the workspace's git ledger and folded into an in-memory SQLite projection. All reads
159
+ * and writes are local; `sync()` is the only network exchange. All operations are
160
+ * serialized onto the one wasm instance.
161
+ */
162
+ export interface Holon {
163
+ /** The stable client identity — names the session branch (`session/<clientId>`) and seeds the HLC replica. */
164
+ clientId: string;
165
+ /** The 63-bit HLC replica id derived from `clientId`, as a decimal string. */
166
+ replica: string;
167
+
168
+ /** Local head — the client's own ledger tip (commit sha of local `main`). */
169
+ head(): Promise<string>;
170
+
171
+ /** Read an aggregate's row(s) from the LOCAL projection by id (folds any un-folded ledger delta first). */
172
+ queryById(aggregateId: string): Promise<AggregateRow[]>;
173
+
174
+ /** Run a declared domain query against the LOCAL projection (routed by the read manifest — works offline). */
175
+ query(queryId: string, params?: Record<string, unknown>): Promise<AggregateRow[]>;
176
+
177
+ /**
178
+ * LOCAL-FIRST write: author an intent under the workspace's INSTALLED law (resolved from
179
+ * the pulled ledger — works fully offline) and commit it to the local ledger. Returns the
180
+ * new local head; the write becomes durable on the cloud via `sync()`. A law refusal
181
+ * throws, verbatim — the write never silently disappears. `domainHash` is required: it is
182
+ * the content hash of the installed law you are authoring under (the generated client
183
+ * bakes it in).
184
+ */
185
+ dispatch(domain: string, directiveId: string, payload: unknown, opts: { domainHash: string }): Promise<string>;
186
+
187
+ /**
188
+ * Watch a declared query: polls the local projection and fires `cb(rows)` whenever the
189
+ * result changes (purely local — sync separately). Returns a `stop()` function.
190
+ */
191
+ watch(queryId: string, params: Record<string, unknown> | undefined, cb: (rows: AggregateRow[]) => void, opts?: { intervalMs?: number }): () => void;
192
+
193
+ /** Watch a single aggregate id (local reactive read, like {@link Holon.watch}). Returns a `stop()` function. */
194
+ watchById(aggregateId: string, cb: (rows: AggregateRow[]) => void, opts?: { intervalMs?: number }): () => void;
195
+
196
+ /** A declared count's maintained O(1) value from the LOCAL projection. */
197
+ count(countId: string, groupKey?: string): Promise<number>;
198
+
199
+ /** A declared sum's maintained O(1) total from the LOCAL projection. */
200
+ sum(sumId: string, groupKey?: string): Promise<number>;
201
+
202
+ /**
203
+ * SYNC — the offline-first exchange: push local commits to the untrusted session branch
204
+ * (the edge holon validates and merges to main — admission, not trust), then converge
205
+ * onto canonical main in the same call. With `admit: false` the push lands but the edge
206
+ * is not asked to judge now (pushes also self-admit via a debounced alarm).
207
+ */
208
+ sync(opts?: { admit?: boolean }): Promise<SyncResult>;
209
+
210
+ /**
211
+ * PULL — incremental convergence onto canonical main on demand: fetch origin main; if
212
+ * unchanged, `{ upToDate: true }`. Otherwise adopt the remote head (the projection
213
+ * re-folds from it at the next query) and rebase-replay the local-only intents the
214
+ * canon doesn't already carry.
215
+ */
216
+ pull(): Promise<PullResult>;
217
+
218
+ /**
219
+ * EXPORT — serialize the workspace directory (the bare nomos.git ledger + manifests +
220
+ * dead letters) to bytes. Persist them anywhere (IndexedDB, a file); hand them back via
221
+ * `connect({ restoreFrom })` to resume WITHOUT a clone — pending un-synced commits included.
222
+ */
223
+ export(): Promise<Uint8Array>;
224
+
225
+ /**
226
+ * The DEAD-LETTER QUEUE: refused-but-legitimate intents with full provenance. Refused
227
+ * work is NEVER lost — it parks here (durably; rides export/restore) until the app
228
+ * retries it (after a law fix) or explicitly discards it. The raw intent bytes stay in
229
+ * the queue but are not returned here — only the metadata.
230
+ */
231
+ deadLetters(): Promise<DeadLetter[]>;
232
+
233
+ /**
234
+ * Retry one dead-lettered intent by id (or short oid) — the UNJAM after the domain dev
235
+ * ships a law fix: pulls canon first (it may already be admitted), else re-applies
236
+ * locally so it rides the next sync. Resolves (removes) the entry on success; on
237
+ * refusal the entry stays with its error refreshed.
238
+ */
239
+ retryDeadLetter(id: string): Promise<RetryDeadLetterResult>;
240
+
241
+ /** Discard one dead letter by id (or short oid) — the APP's explicit choice, never the runtime's. */
242
+ discardDeadLetter(id: string): Promise<{ ok: boolean; removed: number }>;
243
+
244
+ /** Runtime vitals: the wasm instance's current linear-memory footprint in MiB. */
245
+ stats(): { wasmLinearMemMB: number };
246
+ }
247
+
248
+ /**
249
+ * Open a LOCAL holon for `workspace`, hydrated from Nomos Cloud: fetches the
250
+ * byte-identical holon wasm the edge runs, git-pulls the workspace ledger `main`, and
251
+ * replays it locally — verifying as it goes (content-addressed law; custody is never
252
+ * trusted). The returned {@link Holon} reads and writes entirely locally; `sync()` is
253
+ * the only exchange with the cloud.
254
+ */
255
+ export function connect(opts: ConnectOptions): Promise<Holon>;