1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
// SPDX-License-Identifier: Apache-2.0
//! Storage namespace identifiers for the blob KV store.
//!
//! Both SQLite (native) and OPFS (WASM) backends use the same namespace
//! scheme to partition data by engine.
use serde::{Deserialize, Serialize};
/// Storage namespace. Each engine writes to its own namespace in the
/// blob KV store, preventing key collisions.
#[derive(
Debug,
Clone,
Copy,
PartialEq,
Eq,
Hash,
Serialize,
Deserialize,
rkyv::Archive,
rkyv::Serialize,
rkyv::Deserialize,
)]
#[repr(u8)]
#[non_exhaustive]
pub enum Namespace {
/// Database metadata: schema version, config, Shape subscriptions.
Meta = 0,
/// Vector engine: HNSW graph layers, vector data.
Vector = 1,
/// Graph engine: CSR arrays, node/label interning tables.
Graph = 2,
/// CRDT deltas: unsent mutations awaiting sync.
Crdt = 3,
/// Loro state snapshots: compacted CRDT state for fast cold-start.
LoroState = 4,
/// Spatial engine: R-tree checkpoints, geohash indexes.
Spatial = 5,
/// Strict document engine: Binary Tuple rows keyed by PK.
Strict = 6,
/// Columnar engine: compressed segments, delete bitmaps, segment metadata.
Columnar = 7,
/// KV engine: direct key-value storage (bypasses Loro CRDT).
/// Used when sync is disabled or for the local-only KV fast path.
Kv = 8,
/// Array engine: ND sparse arrays, catalog, manifests, segment bytes.
Array = 9,
/// Array CRDT op-log: append-only ops awaiting sync + GC.
ArrayOpLog = 10,
/// Array sync pending queue: ops waiting for transport delivery.
ArrayDelta = 11,
/// Full-text search engine: posting lists, doc-length maps, BM25 stats,
/// fieldnorm blobs, segment bytes, and surrogate maps.
Fts = 12,
/// Bitemporal history table for strict document collections.
///
/// Keys: `{collection}:{system_from_ms_8be}:{pk_bytes}` — value is the
/// full Binary Tuple followed by an 8-byte big-endian `system_to_ms`
/// (u64::MAX = open / still-current at time of supersession).
StrictHistory = 13,
/// Bitemporal history table for graph edge collections.
///
/// Keys: `{collection}:{edge_id_8be}:{system_from_ms_8be}` — value is
/// the MessagePack-encoded edge props followed by an 8-byte big-endian
/// `system_to_ms` (u64::MAX = current / not yet deleted).
GraphHistory = 14,
/// Bitemporal history table for schemaless document collections.
///
/// Keys: `{collection}:{doc_id}\x00{system_from_ms:020}` — value is
/// `[tag:u8][valid_from_ms:i64 LE][valid_until_ms:i64 LE][body_msgpack...]`.
/// `tag = 0x00` (live), `0xFF` (tombstone), `0xFE` (GDPR erased).
DocumentHistory = 15,
/// O(1) pointer to the currently-live DocumentHistory version.
///
/// Keys: `{collection}:{doc_id}` — value is the `system_from_ms` of the
/// live row encoded as a 20-digit zero-padded ASCII decimal (matching the
/// suffix used in `DocumentHistory` keys). Absent when no live version
/// exists (document never written, tombstoned, or GDPR-erased).
///
/// Written atomically alongside every `DocumentHistory` mutation so that
/// `versioned_get_current` can resolve the current version with one index
/// lookup + one history fetch instead of a full prefix scan.
LatestVersion = 16,
}
impl Namespace {
/// Convert from raw u8 (for storage layer deserialization).
pub fn from_u8(v: u8) -> Option<Self> {
match v {
0 => Some(Self::Meta),
1 => Some(Self::Vector),
2 => Some(Self::Graph),
3 => Some(Self::Crdt),
4 => Some(Self::LoroState),
5 => Some(Self::Spatial),
6 => Some(Self::Strict),
7 => Some(Self::Columnar),
8 => Some(Self::Kv),
9 => Some(Self::Array),
10 => Some(Self::ArrayOpLog),
11 => Some(Self::ArrayDelta),
12 => Some(Self::Fts),
13 => Some(Self::StrictHistory),
14 => Some(Self::GraphHistory),
15 => Some(Self::DocumentHistory),
16 => Some(Self::LatestVersion),
_ => None,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn namespace_roundtrip() {
for v in 0u8..=16 {
let ns = Namespace::from_u8(v).unwrap();
assert_eq!(ns as u8, v);
}
assert!(Namespace::from_u8(17).is_none());
}
}