@amalgm/live 0.1.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/PURPOSE.md +103 -0
- package/README.md +45 -0
- package/dist/contracts/binding.d.ts +49 -0
- package/dist/contracts/binding.d.ts.map +1 -0
- package/dist/contracts/binding.js +23 -0
- package/dist/contracts/binding.js.map +1 -0
- package/dist/contracts/content.d.ts +42 -0
- package/dist/contracts/content.d.ts.map +1 -0
- package/dist/contracts/content.js +72 -0
- package/dist/contracts/content.js.map +1 -0
- package/dist/contracts/docs.d.ts +64 -0
- package/dist/contracts/docs.d.ts.map +1 -0
- package/dist/contracts/docs.js +51 -0
- package/dist/contracts/docs.js.map +1 -0
- package/dist/contracts/events.d.ts +45 -0
- package/dist/contracts/events.d.ts.map +1 -0
- package/dist/contracts/events.js +38 -0
- package/dist/contracts/events.js.map +1 -0
- package/dist/contracts/frames/gateway.d.ts +83 -0
- package/dist/contracts/frames/gateway.d.ts.map +1 -0
- package/dist/contracts/frames/gateway.js +35 -0
- package/dist/contracts/frames/gateway.js.map +1 -0
- package/dist/contracts/frames/stream.d.ts +59 -0
- package/dist/contracts/frames/stream.d.ts.map +1 -0
- package/dist/contracts/frames/stream.js +17 -0
- package/dist/contracts/frames/stream.js.map +1 -0
- package/dist/contracts/frames/tunnel.d.ts +78 -0
- package/dist/contracts/frames/tunnel.d.ts.map +1 -0
- package/dist/contracts/frames/tunnel.js +55 -0
- package/dist/contracts/frames/tunnel.js.map +1 -0
- package/dist/contracts/index.d.ts +19 -0
- package/dist/contracts/index.d.ts.map +1 -0
- package/dist/contracts/index.js +19 -0
- package/dist/contracts/index.js.map +1 -0
- package/dist/contracts/mutation.d.ts +51 -0
- package/dist/contracts/mutation.d.ts.map +1 -0
- package/dist/contracts/mutation.js +55 -0
- package/dist/contracts/mutation.js.map +1 -0
- package/dist/contracts/presence.d.ts +46 -0
- package/dist/contracts/presence.d.ts.map +1 -0
- package/dist/contracts/presence.js +34 -0
- package/dist/contracts/presence.js.map +1 -0
- package/dist/contracts/resources.d.ts +52 -0
- package/dist/contracts/resources.d.ts.map +1 -0
- package/dist/contracts/resources.js +105 -0
- package/dist/contracts/resources.js.map +1 -0
- package/dist/contracts/snapshot.d.ts +24 -0
- package/dist/contracts/snapshot.d.ts.map +1 -0
- package/dist/contracts/snapshot.js +28 -0
- package/dist/contracts/snapshot.js.map +1 -0
- package/dist/contracts/validate.d.ts +32 -0
- package/dist/contracts/validate.d.ts.map +1 -0
- package/dist/contracts/validate.js +25 -0
- package/dist/contracts/validate.js.map +1 -0
- package/dist/detection/adapter-contract.d.ts +134 -0
- package/dist/detection/adapter-contract.d.ts.map +1 -0
- package/dist/detection/adapter-contract.js +162 -0
- package/dist/detection/adapter-contract.js.map +1 -0
- package/dist/detection/classify.d.ts +31 -0
- package/dist/detection/classify.d.ts.map +1 -0
- package/dist/detection/classify.js +28 -0
- package/dist/detection/classify.js.map +1 -0
- package/dist/detection/continuity.d.ts +54 -0
- package/dist/detection/continuity.d.ts.map +1 -0
- package/dist/detection/continuity.js +85 -0
- package/dist/detection/continuity.js.map +1 -0
- package/dist/detection/coverage.d.ts +43 -0
- package/dist/detection/coverage.d.ts.map +1 -0
- package/dist/detection/coverage.js +60 -0
- package/dist/detection/coverage.js.map +1 -0
- package/dist/detection/diff.d.ts +144 -0
- package/dist/detection/diff.d.ts.map +1 -0
- package/dist/detection/diff.js +187 -0
- package/dist/detection/diff.js.map +1 -0
- package/dist/detection/enrollment.d.ts +16 -0
- package/dist/detection/enrollment.d.ts.map +1 -0
- package/dist/detection/enrollment.js +70 -0
- package/dist/detection/enrollment.js.map +1 -0
- package/dist/detection/index.d.ts +29 -0
- package/dist/detection/index.d.ts.map +1 -0
- package/dist/detection/index.js +29 -0
- package/dist/detection/index.js.map +1 -0
- package/dist/detection/refusal.d.ts +33 -0
- package/dist/detection/refusal.d.ts.map +1 -0
- package/dist/detection/refusal.js +40 -0
- package/dist/detection/refusal.js.map +1 -0
- package/dist/entities/bytes.d.ts +18 -0
- package/dist/entities/bytes.d.ts.map +1 -0
- package/dist/entities/bytes.js +61 -0
- package/dist/entities/bytes.js.map +1 -0
- package/dist/entities/cloud.d.ts +82 -0
- package/dist/entities/cloud.d.ts.map +1 -0
- package/dist/entities/cloud.js +136 -0
- package/dist/entities/cloud.js.map +1 -0
- package/dist/entities/index.d.ts +23 -0
- package/dist/entities/index.d.ts.map +1 -0
- package/dist/entities/index.js +23 -0
- package/dist/entities/index.js.map +1 -0
- package/dist/entities/moves.d.ts +42 -0
- package/dist/entities/moves.d.ts.map +1 -0
- package/dist/entities/moves.js +82 -0
- package/dist/entities/moves.js.map +1 -0
- package/dist/entities/repo-states.d.ts +121 -0
- package/dist/entities/repo-states.d.ts.map +1 -0
- package/dist/entities/repo-states.js +127 -0
- package/dist/entities/repo-states.js.map +1 -0
- package/dist/entities/rules.d.ts +104 -0
- package/dist/entities/rules.d.ts.map +1 -0
- package/dist/entities/rules.js +200 -0
- package/dist/entities/rules.js.map +1 -0
- package/dist/entities/types.d.ts +94 -0
- package/dist/entities/types.d.ts.map +1 -0
- package/dist/entities/types.js +40 -0
- package/dist/entities/types.js.map +1 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +17 -0
- package/dist/index.js.map +1 -0
- package/dist/machines/arbitration.d.ts +36 -0
- package/dist/machines/arbitration.d.ts.map +1 -0
- package/dist/machines/arbitration.js +37 -0
- package/dist/machines/arbitration.js.map +1 -0
- package/dist/machines/backoff.d.ts +36 -0
- package/dist/machines/backoff.d.ts.map +1 -0
- package/dist/machines/backoff.js +34 -0
- package/dist/machines/backoff.js.map +1 -0
- package/dist/machines/bootstrap.d.ts +65 -0
- package/dist/machines/bootstrap.d.ts.map +1 -0
- package/dist/machines/bootstrap.js +105 -0
- package/dist/machines/bootstrap.js.map +1 -0
- package/dist/machines/chunks.d.ts +51 -0
- package/dist/machines/chunks.d.ts.map +1 -0
- package/dist/machines/chunks.js +67 -0
- package/dist/machines/chunks.js.map +1 -0
- package/dist/machines/index.d.ts +14 -0
- package/dist/machines/index.d.ts.map +1 -0
- package/dist/machines/index.js +14 -0
- package/dist/machines/index.js.map +1 -0
- package/dist/machines/journal.d.ts +63 -0
- package/dist/machines/journal.d.ts.map +1 -0
- package/dist/machines/journal.js +71 -0
- package/dist/machines/journal.js.map +1 -0
- package/dist/machines/ordering.d.ts +53 -0
- package/dist/machines/ordering.d.ts.map +1 -0
- package/dist/machines/ordering.js +87 -0
- package/dist/machines/ordering.js.map +1 -0
- package/dist/machines/presence.d.ts +26 -0
- package/dist/machines/presence.d.ts.map +1 -0
- package/dist/machines/presence.js +49 -0
- package/dist/machines/presence.js.map +1 -0
- package/dist/merge/index.d.ts +9 -0
- package/dist/merge/index.d.ts.map +1 -0
- package/dist/merge/index.js +9 -0
- package/dist/merge/index.js.map +1 -0
- package/dist/merge/merge3.d.ts +39 -0
- package/dist/merge/merge3.d.ts.map +1 -0
- package/dist/merge/merge3.js +188 -0
- package/dist/merge/merge3.js.map +1 -0
- package/package.json +48 -0
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Batch move validation — the entity model's one-truth-transition law for
|
|
3
|
+
* addresses (engine docs/entity-model.md, "Continuity"): the slot law and
|
|
4
|
+
* acyclicity are STATE invariants — they constrain the graph after the
|
|
5
|
+
* batch, never the order inside it — so movers may hold each other's slots
|
|
6
|
+
* (directories exchange addresses on disk, and detection reports the
|
|
7
|
+
* endpoints). A single move is a batch of one.
|
|
8
|
+
*
|
|
9
|
+
* A PARENTLESS mover is ADOPTION: the whole tree joins another under one
|
|
10
|
+
* parent update — a tree boundary is never an identity boundary, so this
|
|
11
|
+
* is the same lawful move, not a special ceremony. Adoption sheds the root
|
|
12
|
+
* shape in the SAME transition: workspace is the root type only, so a
|
|
13
|
+
* parentless workspace landing under a parent lands as a folder — one
|
|
14
|
+
* write, never a two-step that could tear into a nested workspace.
|
|
15
|
+
*
|
|
16
|
+
* This module validates and PLANS; the host's store applies the returned
|
|
17
|
+
* writes in one transaction.
|
|
18
|
+
*/
|
|
19
|
+
import { type EntityReadPort } from './types.js';
|
|
20
|
+
/** One requested move: the entity, its target parent, its target name. */
|
|
21
|
+
export interface EntityMove {
|
|
22
|
+
readonly uuid: string;
|
|
23
|
+
readonly parentUUID: string;
|
|
24
|
+
readonly name: string;
|
|
25
|
+
}
|
|
26
|
+
/** One planned store write. `type: 'folder'` appears exactly when the
|
|
27
|
+
* mover was a parentless workspace shedding its root shape on adoption. */
|
|
28
|
+
export interface PlannedMove {
|
|
29
|
+
readonly uuid: string;
|
|
30
|
+
readonly parentUuid: string;
|
|
31
|
+
readonly name: string;
|
|
32
|
+
readonly type?: 'folder';
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Validate one batch of moves as one transition and return the writes to
|
|
36
|
+
* land together. Refused whole when any move breaks a law: a mover named
|
|
37
|
+
* twice, an inactive mover, an invalid name, a non-container target, two
|
|
38
|
+
* movers claiming one slot, an occupied slot outside the batch, a cycle in
|
|
39
|
+
* the post-state parent graph, or an inactive target chain.
|
|
40
|
+
*/
|
|
41
|
+
export declare function planMoveBatch(moves: readonly EntityMove[], port: EntityReadPort): PlannedMove[];
|
|
42
|
+
//# sourceMappingURL=moves.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"moves.d.ts","sourceRoot":"","sources":["../../src/entities/moves.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EAAc,KAAK,cAAc,EAAkB,MAAM,YAAY,CAAC;AAG7E,0EAA0E;AAC1E,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED;2EAC2E;AAC3E,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,CAAC,EAAE,QAAQ,CAAC;CAC1B;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,SAAS,UAAU,EAAE,EAAE,IAAI,EAAE,cAAc,GAAG,WAAW,EAAE,CAmD/F"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Batch move validation — the entity model's one-truth-transition law for
|
|
3
|
+
* addresses (engine docs/entity-model.md, "Continuity"): the slot law and
|
|
4
|
+
* acyclicity are STATE invariants — they constrain the graph after the
|
|
5
|
+
* batch, never the order inside it — so movers may hold each other's slots
|
|
6
|
+
* (directories exchange addresses on disk, and detection reports the
|
|
7
|
+
* endpoints). A single move is a batch of one.
|
|
8
|
+
*
|
|
9
|
+
* A PARENTLESS mover is ADOPTION: the whole tree joins another under one
|
|
10
|
+
* parent update — a tree boundary is never an identity boundary, so this
|
|
11
|
+
* is the same lawful move, not a special ceremony. Adoption sheds the root
|
|
12
|
+
* shape in the SAME transition: workspace is the root type only, so a
|
|
13
|
+
* parentless workspace landing under a parent lands as a folder — one
|
|
14
|
+
* write, never a two-step that could tear into a nested workspace.
|
|
15
|
+
*
|
|
16
|
+
* This module validates and PLANS; the host's store applies the returned
|
|
17
|
+
* writes in one transaction.
|
|
18
|
+
*/
|
|
19
|
+
import { CONTAINERS } from './types.js';
|
|
20
|
+
import { mustGet, validName } from './rules.js';
|
|
21
|
+
/**
|
|
22
|
+
* Validate one batch of moves as one transition and return the writes to
|
|
23
|
+
* land together. Refused whole when any move breaks a law: a mover named
|
|
24
|
+
* twice, an inactive mover, an invalid name, a non-container target, two
|
|
25
|
+
* movers claiming one slot, an occupied slot outside the batch, a cycle in
|
|
26
|
+
* the post-state parent graph, or an inactive target chain.
|
|
27
|
+
*/
|
|
28
|
+
export function planMoveBatch(moves, port) {
|
|
29
|
+
const byUuid = port.byUuid.bind(port);
|
|
30
|
+
const rows = new Map(); // uuid -> current row
|
|
31
|
+
const post = new Map(); // uuid -> its move
|
|
32
|
+
for (const move of moves) {
|
|
33
|
+
const { uuid, name } = move;
|
|
34
|
+
if (post.has(uuid))
|
|
35
|
+
throw new Error(`one batch moves ${uuid} once, not twice`);
|
|
36
|
+
const row = mustGet(uuid, byUuid);
|
|
37
|
+
if (row.status !== 'active') {
|
|
38
|
+
throw new Error(`only an active entity moves: ${uuid} is ${row.status}`);
|
|
39
|
+
}
|
|
40
|
+
validName(name);
|
|
41
|
+
rows.set(uuid, row);
|
|
42
|
+
post.set(uuid, move);
|
|
43
|
+
}
|
|
44
|
+
const postParentOf = (uuid) => post.get(uuid)?.parentUUID ?? mustGet(uuid, byUuid).parentUuid;
|
|
45
|
+
const targets = new Set();
|
|
46
|
+
for (const { uuid, parentUUID, name } of moves) {
|
|
47
|
+
const parent = mustGet(parentUUID, byUuid);
|
|
48
|
+
if (!CONTAINERS.has(parent.type)) {
|
|
49
|
+
throw new Error(`only containers hold active children: ${parentUUID} is ${parent.type}, not workspace/folder/repo.git`);
|
|
50
|
+
}
|
|
51
|
+
const slot = `${parentUUID}\0${name}`;
|
|
52
|
+
if (targets.has(slot))
|
|
53
|
+
throw new Error(`slot taken: two movers claim (${parentUUID}, ${name}) in one batch`);
|
|
54
|
+
targets.add(slot);
|
|
55
|
+
const occupant = port.activeChildByName(parentUUID, name);
|
|
56
|
+
if (occupant && occupant.uuid !== uuid && !post.has(occupant.uuid)) {
|
|
57
|
+
throw new Error(`slot taken: active entity ${occupant.uuid} already owns (${parentUUID}, ${name}) — move or trash it first`);
|
|
58
|
+
}
|
|
59
|
+
// The target parent's whole post-state chain: acyclic and effectively
|
|
60
|
+
// active, walked once.
|
|
61
|
+
const seen = new Set();
|
|
62
|
+
let cursor = parentUUID;
|
|
63
|
+
while (cursor !== null) {
|
|
64
|
+
if (cursor === uuid) {
|
|
65
|
+
throw new Error(`the parent graph is acyclic: ${uuid} cannot live under itself`);
|
|
66
|
+
}
|
|
67
|
+
if (seen.has(cursor))
|
|
68
|
+
throw new Error(`registry corrupt: parent cycle at ${cursor}`);
|
|
69
|
+
seen.add(cursor);
|
|
70
|
+
if (mustGet(cursor, byUuid).status !== 'active') {
|
|
71
|
+
throw new Error(`parent ${parentUUID} is not effectively active — restore it before putting entities under it`);
|
|
72
|
+
}
|
|
73
|
+
cursor = postParentOf(cursor);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return moves.map(({ uuid, parentUUID, name }) => {
|
|
77
|
+
const row = rows.get(uuid); // seated in the first pass
|
|
78
|
+
const adoptedWorkspace = row.parentUuid === null && row.type === 'workspace';
|
|
79
|
+
return { uuid, parentUuid: parentUUID, name, ...(adoptedWorkspace ? { type: 'folder' } : {}) };
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=moves.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"moves.js","sourceRoot":"","sources":["../../src/entities/moves.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Card + Checkpoint state codec and its id derivations (engine
|
|
3
|
+
* registration/repo-states.js + repocard/capture.js). A `repo.git`
|
|
4
|
+
* entity's semantic head is `stateId = sha256(cardId + checkpointId)`;
|
|
5
|
+
* this module owns those derivations and the exact JSON the state travels
|
|
6
|
+
* and persists as, byte for byte.
|
|
7
|
+
*
|
|
8
|
+
* Axioms carried over from the engine:
|
|
9
|
+
* - the durable local state deliberately omits the Git bundle — bundles
|
|
10
|
+
* are transport cargo, named by a content head, never identity-store
|
|
11
|
+
* bytes;
|
|
12
|
+
* - a transport document contains its bundle and checkpoint bytes but
|
|
13
|
+
* never names itself: its SHA-256 IS the immutable cloud content head,
|
|
14
|
+
* so bundle packing can vary without changing repository identity;
|
|
15
|
+
* - the identity map (worktree path → { uuid, type }) is transport cargo
|
|
16
|
+
* too: repo children never travel as individual cloud records, so
|
|
17
|
+
* their permanent UUIDs ride inside the one state parcel;
|
|
18
|
+
* - ids derive deterministically — remotes are seed metadata, not
|
|
19
|
+
* identity, and bundle bytes are excluded because packing is not
|
|
20
|
+
* deterministic. Equal ids are silence.
|
|
21
|
+
*
|
|
22
|
+
* `unpack` trusts its input to be `pack`'s output (the host's own durable
|
|
23
|
+
* row or transport parcel); it is a codec, not a wire validator.
|
|
24
|
+
*
|
|
25
|
+
* JSON parity note: derivations and `pack` serialize host-built objects,
|
|
26
|
+
* so nested key order follows the host's object insertion order. Build
|
|
27
|
+
* cards and checkpoints in the field order these interfaces declare (the
|
|
28
|
+
* engine capture order) and the bytes match the engine's exactly.
|
|
29
|
+
*/
|
|
30
|
+
import type { Sha256Hex } from './types.js';
|
|
31
|
+
export interface RepoHead {
|
|
32
|
+
readonly branch: string | null;
|
|
33
|
+
readonly commit: string | null;
|
|
34
|
+
readonly detached: boolean;
|
|
35
|
+
}
|
|
36
|
+
export interface RepoRef {
|
|
37
|
+
readonly name: string;
|
|
38
|
+
readonly commit: string;
|
|
39
|
+
}
|
|
40
|
+
export interface RepoRemote {
|
|
41
|
+
readonly name: string;
|
|
42
|
+
readonly url: string;
|
|
43
|
+
}
|
|
44
|
+
export interface RepoBundle {
|
|
45
|
+
readonly bytes: Uint8Array;
|
|
46
|
+
readonly sha256: string;
|
|
47
|
+
}
|
|
48
|
+
export interface RepoCard {
|
|
49
|
+
readonly version: number;
|
|
50
|
+
readonly head: RepoHead;
|
|
51
|
+
readonly branches: readonly RepoRef[];
|
|
52
|
+
readonly tags: readonly RepoRef[];
|
|
53
|
+
readonly remotes: readonly RepoRemote[];
|
|
54
|
+
readonly bundle: RepoBundle | null;
|
|
55
|
+
}
|
|
56
|
+
/** `content: null` marks a reused (unopened) entry; ids derive from
|
|
57
|
+
* (path, mode, sha256) alone, so a scoped capture's ids are byte-identical
|
|
58
|
+
* to a from-scratch one over the same ground. */
|
|
59
|
+
export interface RepoUntrackedFile {
|
|
60
|
+
readonly path: string;
|
|
61
|
+
readonly mode: string;
|
|
62
|
+
readonly content: Uint8Array | null;
|
|
63
|
+
readonly sha256: string;
|
|
64
|
+
readonly reused?: boolean;
|
|
65
|
+
}
|
|
66
|
+
export interface RepoCheckpoint {
|
|
67
|
+
readonly baseCommit: string | null;
|
|
68
|
+
readonly branch: string | null;
|
|
69
|
+
readonly stagedPatch: Uint8Array;
|
|
70
|
+
readonly unstagedPatch: Uint8Array;
|
|
71
|
+
readonly untracked: readonly RepoUntrackedFile[];
|
|
72
|
+
}
|
|
73
|
+
/** One enrolled worktree entity inside the transport identity map. */
|
|
74
|
+
export interface RepoIdentityEntry {
|
|
75
|
+
readonly path: string;
|
|
76
|
+
readonly uuid: string;
|
|
77
|
+
readonly type: string;
|
|
78
|
+
}
|
|
79
|
+
export interface RepoState {
|
|
80
|
+
readonly stateId: string;
|
|
81
|
+
readonly cardId: string;
|
|
82
|
+
readonly checkpointId: string;
|
|
83
|
+
readonly transportVersion?: string | null;
|
|
84
|
+
readonly identity?: readonly RepoIdentityEntry[] | null;
|
|
85
|
+
readonly card: RepoCard;
|
|
86
|
+
readonly checkpoint: RepoCheckpoint;
|
|
87
|
+
}
|
|
88
|
+
/** A state whose transport head and identity map are settled (what
|
|
89
|
+
* `unpack` and `localState` return). */
|
|
90
|
+
export interface LocalRepoState extends RepoState {
|
|
91
|
+
readonly transportVersion: string | null;
|
|
92
|
+
readonly identity: readonly RepoIdentityEntry[] | null;
|
|
93
|
+
}
|
|
94
|
+
export interface PackOptions {
|
|
95
|
+
readonly includeBundle?: boolean;
|
|
96
|
+
readonly transportVersion?: string | null;
|
|
97
|
+
readonly identity?: readonly RepoIdentityEntry[] | null;
|
|
98
|
+
}
|
|
99
|
+
/** Serialize a state to its durable/transport JSON. The durable local form
|
|
100
|
+
* omits the bundle (`includeBundle: false`); the transport form carries it
|
|
101
|
+
* plus the identity map. */
|
|
102
|
+
export declare function pack(state: RepoState, { includeBundle, transportVersion, identity }?: PackOptions): string;
|
|
103
|
+
/** Decode `pack`'s JSON back into a state with real bytes. */
|
|
104
|
+
export declare function unpack(text: string): LocalRepoState;
|
|
105
|
+
/** The transport parcel: bundle included, no self-naming transport head,
|
|
106
|
+
* the identity map riding inside. Its SHA-256 is the cloud content head. */
|
|
107
|
+
export declare function transportBytes(state: RepoState, identity?: readonly RepoIdentityEntry[] | null): Uint8Array;
|
|
108
|
+
/** The durable local form of a captured state: bundle and identity map
|
|
109
|
+
* shed, the settled transport head recorded. */
|
|
110
|
+
export declare function localState(state: RepoState, transportVersion: string | null): LocalRepoState;
|
|
111
|
+
/** Card identity from the ledger only.
|
|
112
|
+
* Remotes deliberately absent: seed metadata, not identity.
|
|
113
|
+
* Bundle bytes deliberately absent: packing is not deterministic. */
|
|
114
|
+
export declare function deriveCardId(card: Pick<RepoCard, 'version' | 'head' | 'branches' | 'tags'>, sha256Hex: Sha256Hex): string;
|
|
115
|
+
/** Checkpoint identity from the diff base, the patch hashes, and each
|
|
116
|
+
* untracked (path, mode, sha256) — never the untracked contents, so reused
|
|
117
|
+
* entries derive identically. */
|
|
118
|
+
export declare function deriveCheckpointId(checkpoint: Pick<RepoCheckpoint, 'baseCommit' | 'stagedPatch' | 'unstagedPatch' | 'untracked'>, sha256Hex: Sha256Hex): string;
|
|
119
|
+
/** The repository's semantic head: sha256(cardId + checkpointId). */
|
|
120
|
+
export declare const deriveStateId: (cardId: string, checkpointId: string, sha256Hex: Sha256Hex) => string;
|
|
121
|
+
//# sourceMappingURL=repo-states.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"repo-states.d.ts","sourceRoot":"","sources":["../../src/entities/repo-states.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Card + Checkpoint state codec and its id derivations (engine
|
|
3
|
+
* registration/repo-states.js + repocard/capture.js). A `repo.git`
|
|
4
|
+
* entity's semantic head is `stateId = sha256(cardId + checkpointId)`;
|
|
5
|
+
* this module owns those derivations and the exact JSON the state travels
|
|
6
|
+
* and persists as, byte for byte.
|
|
7
|
+
*
|
|
8
|
+
* Axioms carried over from the engine:
|
|
9
|
+
* - the durable local state deliberately omits the Git bundle — bundles
|
|
10
|
+
* are transport cargo, named by a content head, never identity-store
|
|
11
|
+
* bytes;
|
|
12
|
+
* - a transport document contains its bundle and checkpoint bytes but
|
|
13
|
+
* never names itself: its SHA-256 IS the immutable cloud content head,
|
|
14
|
+
* so bundle packing can vary without changing repository identity;
|
|
15
|
+
* - the identity map (worktree path → { uuid, type }) is transport cargo
|
|
16
|
+
* too: repo children never travel as individual cloud records, so
|
|
17
|
+
* their permanent UUIDs ride inside the one state parcel;
|
|
18
|
+
* - ids derive deterministically — remotes are seed metadata, not
|
|
19
|
+
* identity, and bundle bytes are excluded because packing is not
|
|
20
|
+
* deterministic. Equal ids are silence.
|
|
21
|
+
*
|
|
22
|
+
* `unpack` trusts its input to be `pack`'s output (the host's own durable
|
|
23
|
+
* row or transport parcel); it is a codec, not a wire validator.
|
|
24
|
+
*
|
|
25
|
+
* JSON parity note: derivations and `pack` serialize host-built objects,
|
|
26
|
+
* so nested key order follows the host's object insertion order. Build
|
|
27
|
+
* cards and checkpoints in the field order these interfaces declare (the
|
|
28
|
+
* engine capture order) and the bytes match the engine's exactly.
|
|
29
|
+
*/
|
|
30
|
+
import { base64ToBytes, bytesToBase64, utf8Encode } from './bytes.js';
|
|
31
|
+
const encodeBytes = (bytes) => bytesToBase64(bytes ?? new Uint8Array());
|
|
32
|
+
const decodeBytes = (encoded) => base64ToBytes(encoded ?? '');
|
|
33
|
+
/** Serialize a state to its durable/transport JSON. The durable local form
|
|
34
|
+
* omits the bundle (`includeBundle: false`); the transport form carries it
|
|
35
|
+
* plus the identity map. */
|
|
36
|
+
export function pack(state, { includeBundle = true, transportVersion = state.transportVersion ?? null, identity = null } = {}) {
|
|
37
|
+
return JSON.stringify({
|
|
38
|
+
stateId: state.stateId,
|
|
39
|
+
cardId: state.cardId,
|
|
40
|
+
checkpointId: state.checkpointId,
|
|
41
|
+
transportVersion,
|
|
42
|
+
...(identity ? { identity } : {}),
|
|
43
|
+
card: {
|
|
44
|
+
...state.card,
|
|
45
|
+
bundle: includeBundle && state.card.bundle ? {
|
|
46
|
+
...state.card.bundle,
|
|
47
|
+
bytes: encodeBytes(state.card.bundle.bytes),
|
|
48
|
+
} : null,
|
|
49
|
+
},
|
|
50
|
+
checkpoint: {
|
|
51
|
+
...state.checkpoint,
|
|
52
|
+
stagedPatch: encodeBytes(state.checkpoint.stagedPatch),
|
|
53
|
+
unstagedPatch: encodeBytes(state.checkpoint.unstagedPatch),
|
|
54
|
+
untracked: state.checkpoint.untracked.map((file) => ({
|
|
55
|
+
...file,
|
|
56
|
+
content: encodeBytes(file.content),
|
|
57
|
+
})),
|
|
58
|
+
},
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
/** Decode `pack`'s JSON back into a state with real bytes. */
|
|
62
|
+
export function unpack(text) {
|
|
63
|
+
const state = JSON.parse(text);
|
|
64
|
+
return {
|
|
65
|
+
...state,
|
|
66
|
+
transportVersion: typeof state.transportVersion === 'string' ? state.transportVersion : null,
|
|
67
|
+
identity: Array.isArray(state.identity) ? state.identity : null,
|
|
68
|
+
card: {
|
|
69
|
+
...state.card,
|
|
70
|
+
bundle: state.card.bundle && {
|
|
71
|
+
...state.card.bundle,
|
|
72
|
+
bytes: decodeBytes(state.card.bundle.bytes),
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
checkpoint: {
|
|
76
|
+
...state.checkpoint,
|
|
77
|
+
stagedPatch: decodeBytes(state.checkpoint.stagedPatch),
|
|
78
|
+
unstagedPatch: decodeBytes(state.checkpoint.unstagedPatch),
|
|
79
|
+
untracked: state.checkpoint.untracked.map((file) => ({
|
|
80
|
+
...file,
|
|
81
|
+
content: decodeBytes(file.content),
|
|
82
|
+
})),
|
|
83
|
+
},
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
/** The transport parcel: bundle included, no self-naming transport head,
|
|
87
|
+
* the identity map riding inside. Its SHA-256 is the cloud content head. */
|
|
88
|
+
export function transportBytes(state, identity = null) {
|
|
89
|
+
return utf8Encode(pack(state, { includeBundle: true, transportVersion: null, identity }));
|
|
90
|
+
}
|
|
91
|
+
/** The durable local form of a captured state: bundle and identity map
|
|
92
|
+
* shed, the settled transport head recorded. */
|
|
93
|
+
export function localState(state, transportVersion) {
|
|
94
|
+
return {
|
|
95
|
+
...state,
|
|
96
|
+
transportVersion,
|
|
97
|
+
identity: null,
|
|
98
|
+
card: { ...state.card, bundle: null },
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
/** Card identity from the ledger only.
|
|
102
|
+
* Remotes deliberately absent: seed metadata, not identity.
|
|
103
|
+
* Bundle bytes deliberately absent: packing is not deterministic. */
|
|
104
|
+
export function deriveCardId(card, sha256Hex) {
|
|
105
|
+
return sha256Hex(JSON.stringify({
|
|
106
|
+
version: card.version,
|
|
107
|
+
head: card.head,
|
|
108
|
+
branches: card.branches,
|
|
109
|
+
tags: card.tags,
|
|
110
|
+
}));
|
|
111
|
+
}
|
|
112
|
+
/** Checkpoint identity from the diff base, the patch hashes, and each
|
|
113
|
+
* untracked (path, mode, sha256) — never the untracked contents, so reused
|
|
114
|
+
* entries derive identically. */
|
|
115
|
+
export function deriveCheckpointId(checkpoint, sha256Hex) {
|
|
116
|
+
return sha256Hex(JSON.stringify({
|
|
117
|
+
baseCommit: checkpoint.baseCommit,
|
|
118
|
+
stagedPatch: sha256Hex(checkpoint.stagedPatch),
|
|
119
|
+
unstagedPatch: sha256Hex(checkpoint.unstagedPatch),
|
|
120
|
+
untracked: checkpoint.untracked.map((file) => ({
|
|
121
|
+
path: file.path, mode: file.mode, sha256: file.sha256,
|
|
122
|
+
})),
|
|
123
|
+
}));
|
|
124
|
+
}
|
|
125
|
+
/** The repository's semantic head: sha256(cardId + checkpointId). */
|
|
126
|
+
export const deriveStateId = (cardId, checkpointId, sha256Hex) => sha256Hex(cardId + checkpointId);
|
|
127
|
+
//# sourceMappingURL=repo-states.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"repo-states.js","sourceRoot":"","sources":["../../src/entities/repo-states.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The registry's pure rule surface — the laws of the entity model (engine
|
|
3
|
+
* docs/entity-model.md) with every store lookup behind the injected
|
|
4
|
+
* {@link EntityReadPort} and every hash behind {@link Sha256Hex}:
|
|
5
|
+
*
|
|
6
|
+
* - the ratified version formula: version = hash(type, parentUUID, name,
|
|
7
|
+
* status, adapterPayloadVersion) — DERIVED, never stored;
|
|
8
|
+
* - container payloads as sorted active direct membership;
|
|
9
|
+
* - lifecycle inheritance DOWN the ancestor chain as derived effective
|
|
10
|
+
* status (deleted > trashed > own stored status);
|
|
11
|
+
* - one active owner per (parentUUID, name) slot; only containers hold
|
|
12
|
+
* active children; the parent graph is acyclic;
|
|
13
|
+
* - what an attestation may say (leaf ground attests bytes; membership and
|
|
14
|
+
* reference identity are never observed);
|
|
15
|
+
* - the record / syncRecord shapes and the cloud-record normalizer.
|
|
16
|
+
*
|
|
17
|
+
* Refusal messages are ported verbatim from the engine registry — they are
|
|
18
|
+
* the ratified refusal text its exams pin.
|
|
19
|
+
*/
|
|
20
|
+
import { type EntityReadPort, type EntityRecord, type EntityRow, type EntityStatus, type EntitySyncRecord, type EntityType, type Sha256Hex } from './types.js';
|
|
21
|
+
/** The row a UUID must name; absence is the caller's bug, said plainly. */
|
|
22
|
+
export declare function mustGet(uuid: string, byUuid: EntityReadPort['byUuid']): EntityRow;
|
|
23
|
+
/**
|
|
24
|
+
* What an attestation may say — asked of both the type a record wears now
|
|
25
|
+
* and the type its new bytes claim, because an attestation may carry a
|
|
26
|
+
* record across leaf kinds (a pointer replaced by bytes) and both ends
|
|
27
|
+
* must be ground an observation can speak for.
|
|
28
|
+
*/
|
|
29
|
+
export declare function attestable(type: string): void;
|
|
30
|
+
export interface CanonicalVersionInput {
|
|
31
|
+
readonly type: EntityType;
|
|
32
|
+
readonly parentUUID?: string | null;
|
|
33
|
+
readonly name: string;
|
|
34
|
+
readonly status: EntityStatus;
|
|
35
|
+
readonly payloadVersion?: string | null;
|
|
36
|
+
}
|
|
37
|
+
/** The ratified formula: version = hash(type, parentUUID, name, status, payloadVersion). */
|
|
38
|
+
export declare function canonicalVersion({ type, parentUUID, name, status, payloadVersion }: CanonicalVersionInput, sha256Hex: Sha256Hex): string;
|
|
39
|
+
/** A name is one non-empty path segment: never '', '.', '..', a '/' or NUL. */
|
|
40
|
+
export declare function validName(name: unknown): asserts name is string;
|
|
41
|
+
export interface UuidPorts {
|
|
42
|
+
readonly byUuid: EntityReadPort['byUuid'];
|
|
43
|
+
/** Mint a fresh RFC-4122 UUID (the host's crypto.randomUUID). */
|
|
44
|
+
mintUuid(): string;
|
|
45
|
+
}
|
|
46
|
+
/** A UUID may arrive only through a trusted identity binding (for example
|
|
47
|
+
* a cloud user-home bootstrap). The registry still owns the actual insert:
|
|
48
|
+
* accepting a caller-supplied UUID is not accepting a caller-supplied
|
|
49
|
+
* record, parent, status, or version. */
|
|
50
|
+
export declare function mintOrValidateUuid(uuid: string | null | undefined, ports: UuidPorts): string;
|
|
51
|
+
/** Ancestor uuids from the given parent up to the root. */
|
|
52
|
+
export declare function ancestorsOf(parentUuid: string | null, byUuid: EntityReadPort['byUuid']): string[];
|
|
53
|
+
/** Lifecycle inherits down: deleted if any ancestor is deleted, trashed if
|
|
54
|
+
* any is trashed, otherwise the entity's own stored status — one flip on a
|
|
55
|
+
* folder, no descendant record rewritten. */
|
|
56
|
+
export declare function effectiveStatusOf(row: Pick<EntityRow, 'status' | 'parentUuid'>, byUuid: EntityReadPort['byUuid']): EntityStatus;
|
|
57
|
+
/**
|
|
58
|
+
* Container payload: the sorted active direct membership (childUUID, name).
|
|
59
|
+
* Children are sorted here by uuid, codepoint order — the same order the
|
|
60
|
+
* engine's `ORDER BY uuid` (BINARY collation over lowercase-hex uuids)
|
|
61
|
+
* produces — so the hash input is byte-identical however the port returns
|
|
62
|
+
* them. Membership counts direct children by their STORED status, never
|
|
63
|
+
* effective status: effective-status membership would ripple a version
|
|
64
|
+
* change through the entire subtree and defeat the one-flip lifecycle.
|
|
65
|
+
*/
|
|
66
|
+
export declare function membershipHash(activeChildren: readonly Pick<EntityRow, 'uuid' | 'name'>[], sha256Hex: Sha256Hex): string;
|
|
67
|
+
export interface RecordPorts {
|
|
68
|
+
readonly sha256Hex: Sha256Hex;
|
|
69
|
+
/** The container membership hash for a uuid ({@link membershipHash} over
|
|
70
|
+
* its active direct children); consulted only for workspace/folder. */
|
|
71
|
+
membershipHashOf(uuid: string): string;
|
|
72
|
+
}
|
|
73
|
+
/** The public record: exactly the six ratified fields, version derived. */
|
|
74
|
+
export declare function entityRecordOf(row: EntityRow, ports: RecordPorts): EntityRecord;
|
|
75
|
+
/** The cloud form contains identity and the content/head token, never an
|
|
76
|
+
* address, watcher, inode, or SQLite bytes. `payloadVersion` is the
|
|
77
|
+
* unambiguous current head for leaf types; containers derive theirs from
|
|
78
|
+
* membership and retain null here; a repo.git retains its immutable
|
|
79
|
+
* Card/Checkpoint head. */
|
|
80
|
+
export declare function syncRecordOf(row: EntityRow, ports: RecordPorts): EntitySyncRecord;
|
|
81
|
+
/** A cloud record normalized into row spelling, with the version the
|
|
82
|
+
* sender expects this record to carry. */
|
|
83
|
+
export interface NormalizedCloudRecord {
|
|
84
|
+
readonly uuid: string;
|
|
85
|
+
readonly type: EntityType;
|
|
86
|
+
readonly parentUuid: string | null;
|
|
87
|
+
readonly name: string;
|
|
88
|
+
readonly status: EntityStatus;
|
|
89
|
+
readonly payloadVersion: string | null;
|
|
90
|
+
readonly expectedVersion: string;
|
|
91
|
+
}
|
|
92
|
+
/** Validate and normalize a record arriving from the cloud: six fields plus
|
|
93
|
+
* payloadVersion, UUIDs lowercased, root laws enforced. */
|
|
94
|
+
export declare function normalizeCloudRecord(value: unknown): NormalizedCloudRecord;
|
|
95
|
+
/** Equality over the five stored facts plus payload — everything the
|
|
96
|
+
* version fingerprints except the fingerprint itself. */
|
|
97
|
+
export declare function sameLogicalRecord(left: Pick<EntityRow, 'uuid' | 'type' | 'parentUuid' | 'name' | 'status' | 'payloadVersion'>, right: Pick<EntityRow, 'uuid' | 'type' | 'parentUuid' | 'name' | 'status' | 'payloadVersion'>): boolean;
|
|
98
|
+
/** Only containers — workspace, folder, repo.git — hold active children,
|
|
99
|
+
* and only while effectively active themselves. */
|
|
100
|
+
export declare function requireActiveContainerParent(parentUuid: string, port: EntityReadPort): EntityRow;
|
|
101
|
+
/** One active owner per (parentUUID, name) slot. Parentless records are
|
|
102
|
+
* exempt: a top-level tree is placed by its machine binding, never by name. */
|
|
103
|
+
export declare function requireFreeSlot(parentUuid: string | null, name: string, port: EntityReadPort, forUuid?: string | null): void;
|
|
104
|
+
//# sourceMappingURL=rules.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rules.d.ts","sourceRoot":"","sources":["../../src/entities/rules.ts"],"names":[],"mappings":"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"}
|