@feltdb/core 0.7.0 → 0.7.2
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/README.md +27 -1
- package/dist/cli/commands.js +64 -28
- package/dist/cli/index.js +1 -1
- package/dist/collection.d.ts.map +1 -1
- package/dist/collection.js +2 -1
- package/dist/create/package-versions.js +1 -1
- package/dist/create/server-source/crates/feltdb/src/bin/feltdb_node.rs +84 -0
- package/dist/create/server-source/crates/feltdb/src/lib.rs +38 -20
- package/dist/create/server-source/crates/feltdb/src/state_facade.rs +292 -0
- package/dist/create/server-source/crates/feltdb/src/state_model.rs +1856 -0
- package/dist/create/server-source/crates/feltdb/src/transaction_preconditions.rs +43 -11
- package/dist/create/server-source/crates/feltdb/tests/feltdb_state_boundary_tests.rs +634 -0
- package/dist/create/server-source/crates/feltdb/tests/state_model_integration.rs +366 -0
- package/dist/create/server-source/crates/feltdb/tests/state_persistence_integration.rs +270 -0
- package/dist/create/server-source/crates/feltdb-server/src/app_state.rs +13 -0
- package/dist/create/server-source/crates/feltdb-server/src/main.rs +194 -9
- package/dist/db.d.ts +7 -0
- package/dist/db.d.ts.map +1 -1
- package/dist/db.js +12 -1
- package/dist/embedded-transaction.d.ts.map +1 -1
- package/dist/embedded-transaction.js +14 -8
- package/dist/feltdb.d.ts +2 -0
- package/dist/feltdb.d.ts.map +1 -1
- package/dist/http-db.d.ts +24 -0
- package/dist/http-db.d.ts.map +1 -1
- package/dist/http-db.js +13 -0
- package/dist/index-core.d.ts +1 -0
- package/dist/index-core.d.ts.map +1 -1
- package/dist/studio-app/assets/{feltdb_wasm-CD744e5D.js → feltdb_wasm-DVKsw75S.js} +1 -1
- package/dist/studio-app/assets/feltdb_wasm_bg-DNyNf0yy.wasm +0 -0
- package/dist/studio-app/assets/{index-DoROs8yx.js → index-C71X92EK.js} +2 -2
- package/dist/studio-app/index.html +1 -1
- package/dist/wasm/feltdb_wasm_bg.wasm +0 -0
- package/package.json +1 -1
- package/dist/studio-app/assets/feltdb_wasm_bg-CiIXhOLi.wasm +0 -0
package/README.md
CHANGED
|
@@ -77,7 +77,7 @@ across tabs through `BroadcastChannel` when available.
|
|
|
77
77
|
|
|
78
78
|
## Atomic conditional transactions
|
|
79
79
|
|
|
80
|
-
Core 0.7.
|
|
80
|
+
Core 0.7.1 can fence a multi-record transaction on the exact version read by
|
|
81
81
|
the caller. The transaction either commits every operation or writes nothing:
|
|
82
82
|
|
|
83
83
|
```typescript
|
|
@@ -107,6 +107,32 @@ Reusing a successful `transactionId` safely replays its result without
|
|
|
107
107
|
advancing record versions twice. This API requires a FeltDB authority that
|
|
108
108
|
supports transaction-level `ifVersion` preconditions.
|
|
109
109
|
|
|
110
|
+
A write with `requireAbsent: true` is an atomic create. The authority assigns
|
|
111
|
+
it `__version: 1`—overriding any caller-supplied version—so it can immediately
|
|
112
|
+
be updated with `ifVersion: 1`.
|
|
113
|
+
|
|
114
|
+
## Bounded authority queries
|
|
115
|
+
|
|
116
|
+
Remote databases can filter, order, and limit records inside the authority:
|
|
117
|
+
|
|
118
|
+
```typescript
|
|
119
|
+
const page = await db.query({
|
|
120
|
+
collection: 'outbox',
|
|
121
|
+
where: [
|
|
122
|
+
{ field: 'status', eq: 'pending' },
|
|
123
|
+
{ field: 'nextAttemptAt', lte: Date.now() },
|
|
124
|
+
],
|
|
125
|
+
orderBy: [{ field: 'nextAttemptAt', direction: 'asc' }],
|
|
126
|
+
limit: 100,
|
|
127
|
+
cursor,
|
|
128
|
+
});
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
The server adds `recordId` as a deterministic final tie-breaker and returns an
|
|
132
|
+
opaque `nextCursor`. Page snapshots are stable across concurrent inserts and
|
|
133
|
+
deletes. This method is intentionally unavailable on embedded runtimes;
|
|
134
|
+
`Collection.where()` and `Collection.find()` retain their existing behavior.
|
|
135
|
+
|
|
110
136
|
## Durable Operation Management
|
|
111
137
|
|
|
112
138
|
FeltDB provides atomic operation admission and lifecycle management for systems that need to survive process crashes with guaranteed identity stability.
|
package/dist/cli/commands.js
CHANGED
|
@@ -232,35 +232,69 @@ async function handleFlowDeploy(args) {
|
|
|
232
232
|
}
|
|
233
233
|
}
|
|
234
234
|
async function handleServer(args) {
|
|
235
|
-
|
|
236
|
-
const
|
|
237
|
-
const
|
|
238
|
-
const
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
const
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
235
|
+
const require = createRequire(import.meta.url);
|
|
236
|
+
const packageRoot = path.dirname(require.resolve('@feltdb/core/package.json'));
|
|
237
|
+
const executable = process.platform === 'win32' ? 'feltdb-server.exe' : 'feltdb-server';
|
|
238
|
+
const candidates = [
|
|
239
|
+
process.env.FELTDB_SERVER_BIN,
|
|
240
|
+
path.join(packageRoot, 'dist', 'server-bin', `${process.platform}-${process.arch}`, executable),
|
|
241
|
+
path.resolve(packageRoot, '..', '..', 'target', 'release', executable),
|
|
242
|
+
path.resolve(packageRoot, '..', '..', 'target', 'debug', executable),
|
|
243
|
+
].filter((value) => Boolean(value));
|
|
244
|
+
const binary = candidates.find(value => fs.existsSync(value));
|
|
245
|
+
const manifests = [
|
|
246
|
+
path.join(packageRoot, 'dist', 'server-source', 'Cargo.toml'),
|
|
247
|
+
path.join(packageRoot, 'dist', 'create', 'server-source', 'Cargo.toml'),
|
|
248
|
+
];
|
|
249
|
+
const manifest = manifests.find(value => fs.existsSync(value));
|
|
250
|
+
if (process.env.FELTDB_SERVER_BIN && !binary) {
|
|
251
|
+
throw new Error(`FELTDB_SERVER_BIN does not exist: ${process.env.FELTDB_SERVER_BIN}`);
|
|
252
|
+
}
|
|
253
|
+
if (!binary && !manifest) {
|
|
254
|
+
throw new Error('No FeltDB authority is available. Reinstall @feltdb/core or set FELTDB_SERVER_BIN to a feltdb-server executable.');
|
|
255
|
+
}
|
|
256
|
+
const forwarded = [...args];
|
|
257
|
+
if (!forwarded.includes('--host'))
|
|
258
|
+
forwarded.unshift('--host', '0.0.0.0');
|
|
259
|
+
if (!forwarded.includes('--data'))
|
|
260
|
+
forwarded.push('--data', path.resolve('data', 'feltdb.log'));
|
|
261
|
+
const dataIndex = forwarded.indexOf('--data');
|
|
262
|
+
if (dataIndex >= 0 && forwarded[dataIndex + 1]) {
|
|
263
|
+
const data = path.resolve(forwarded[dataIndex + 1]);
|
|
264
|
+
const directoryStyle = data.endsWith(path.sep)
|
|
265
|
+
|| (fs.existsSync(data) && fs.statSync(data).isDirectory())
|
|
266
|
+
|| path.extname(data) === '';
|
|
267
|
+
if (directoryStyle) {
|
|
268
|
+
fs.mkdirSync(data, { recursive: true });
|
|
269
|
+
forwarded[dataIndex + 1] = path.join(data, 'feltdb.log');
|
|
270
|
+
}
|
|
271
|
+
else {
|
|
272
|
+
fs.mkdirSync(path.dirname(data), { recursive: true });
|
|
273
|
+
forwarded[dataIndex + 1] = data;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
const command = binary || 'cargo';
|
|
277
|
+
const commandArgs = binary
|
|
278
|
+
? forwarded
|
|
279
|
+
: ['run', '--quiet', '--locked', '--manifest-path', manifest, '--package', 'feltdb-server', '--', ...forwarded];
|
|
280
|
+
console.log(`Starting FeltDB authority (${binary ? binary : 'packaged Rust source'})`);
|
|
281
|
+
const child = spawn(command, commandArgs, { stdio: 'inherit', env: process.env });
|
|
282
|
+
const forwardSignal = (signal) => {
|
|
283
|
+
if (child.exitCode === null)
|
|
284
|
+
child.kill(signal);
|
|
254
285
|
};
|
|
255
|
-
const
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
286
|
+
const onSigint = () => forwardSignal('SIGINT');
|
|
287
|
+
const onSigterm = () => forwardSignal('SIGTERM');
|
|
288
|
+
process.once('SIGINT', onSigint);
|
|
289
|
+
process.once('SIGTERM', onSigterm);
|
|
290
|
+
const exitCode = await new Promise((resolve, reject) => {
|
|
291
|
+
child.once('error', reject);
|
|
292
|
+
child.once('exit', (code, signal) => resolve(code ?? (signal ? 1 : 0)));
|
|
293
|
+
});
|
|
294
|
+
process.off('SIGINT', onSigint);
|
|
295
|
+
process.off('SIGTERM', onSigterm);
|
|
296
|
+
if (exitCode !== 0)
|
|
297
|
+
throw new Error(`FeltDB authority exited with code ${exitCode}`);
|
|
264
298
|
}
|
|
265
299
|
async function handleKeys(args) {
|
|
266
300
|
const subcommand = args[0];
|
|
@@ -1299,6 +1333,8 @@ Studio:
|
|
|
1299
1333
|
|
|
1300
1334
|
Server Options:
|
|
1301
1335
|
feltdb server [--port 7700] [--data ./data] [--auth]
|
|
1336
|
+
Starts the real Rust HTTP authority. Set FELTDB_MASTER_KEY; Cargo builds the
|
|
1337
|
+
version-matched bundled source when no FELTDB_SERVER_BIN override is set.
|
|
1302
1338
|
|
|
1303
1339
|
API Key Commands:
|
|
1304
1340
|
feltdb keys create [--name dev] [--scope '*']
|
package/dist/cli/index.js
CHANGED
|
@@ -23,7 +23,7 @@ import * as path from 'path';
|
|
|
23
23
|
import * as readline from 'readline';
|
|
24
24
|
import { getClient } from './api-client.js';
|
|
25
25
|
import { loadFeltDBConfig, createDefaultConfig, validateModel, } from './config.js';
|
|
26
|
-
const VERSION = '0.
|
|
26
|
+
const VERSION = '0.7.2';
|
|
27
27
|
function prompt(question) {
|
|
28
28
|
const rl = readline.createInterface({
|
|
29
29
|
input: process.stdin,
|
package/dist/collection.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"collection.d.ts","sourceRoot":"","sources":["../src/collection.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"collection.d.ts","sourceRoot":"","sources":["../src/collection.ts"],"names":[],"mappings":"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"}
|
package/dist/collection.js
CHANGED
|
@@ -333,7 +333,8 @@ export class Collection {
|
|
|
333
333
|
if (!current) {
|
|
334
334
|
throw new Error(`Record ${id} not found`);
|
|
335
335
|
}
|
|
336
|
-
const
|
|
336
|
+
const nextVersion = (current.__version || 1) + 1;
|
|
337
|
+
const updated = { ...current, ...changes, __version: nextVersion };
|
|
337
338
|
const key = `${this.name}:${id}`;
|
|
338
339
|
const result = await (this.db.update
|
|
339
340
|
? this.db.update(key, JSON.stringify(updated))
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
// One release train keeps generated applications installable. The repository
|
|
2
2
|
// validation script checks these values against every workspace manifest.
|
|
3
|
-
export const FELTDB_PACKAGE_VERSION = '0.
|
|
3
|
+
export const FELTDB_PACKAGE_VERSION = '0.7.2';
|
|
4
4
|
export const feltdbPackageRange = `^${FELTDB_PACKAGE_VERSION}`;
|
|
@@ -1051,6 +1051,90 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|
|
1051
1051
|
io::stdout().flush().ok();
|
|
1052
1052
|
}
|
|
1053
1053
|
|
|
1054
|
+
"list-operations" => {
|
|
1055
|
+
let guard = core.lock().await;
|
|
1056
|
+
let (executor, _) = &*guard;
|
|
1057
|
+
|
|
1058
|
+
// Get all operations from the log
|
|
1059
|
+
let mut all_ops = Vec::new();
|
|
1060
|
+
let mut applied_ops = Vec::new();
|
|
1061
|
+
let pending_ops = executor.pending_causal_keys();
|
|
1062
|
+
let deferred_ops = executor.deferred_causal_keys();
|
|
1063
|
+
|
|
1064
|
+
if let Some(ref log) = executor.operation_log {
|
|
1065
|
+
if let Ok(envelopes) = log.load_all() {
|
|
1066
|
+
for env in &envelopes {
|
|
1067
|
+
let key = format!("{}:{}", env.envelope_id.originating_node, env.envelope_id.sequence);
|
|
1068
|
+
all_ops.push(key.clone());
|
|
1069
|
+
}
|
|
1070
|
+
}
|
|
1071
|
+
}
|
|
1072
|
+
|
|
1073
|
+
// Get applied operations from the barrier
|
|
1074
|
+
for key in executor.causal_barrier.applied_keys() {
|
|
1075
|
+
applied_ops.push(key.to_string());
|
|
1076
|
+
}
|
|
1077
|
+
|
|
1078
|
+
println!(
|
|
1079
|
+
"OPERATIONS {}",
|
|
1080
|
+
json!({
|
|
1081
|
+
"all": all_ops,
|
|
1082
|
+
"applied": applied_ops,
|
|
1083
|
+
"pending": pending_ops,
|
|
1084
|
+
"deferred": deferred_ops,
|
|
1085
|
+
"operations_applied": executor.get_replica_state(&node_id).map(|r| r.operations_applied).unwrap_or(0),
|
|
1086
|
+
})
|
|
1087
|
+
);
|
|
1088
|
+
io::stdout().flush().ok();
|
|
1089
|
+
}
|
|
1090
|
+
|
|
1091
|
+
"operation-inventory" => {
|
|
1092
|
+
let guard = core.lock().await;
|
|
1093
|
+
let (executor, _) = &*guard;
|
|
1094
|
+
|
|
1095
|
+
// Get all operations from the log
|
|
1096
|
+
let mut operations = json!({
|
|
1097
|
+
"applied": [],
|
|
1098
|
+
"pending": [],
|
|
1099
|
+
"deferred": [],
|
|
1100
|
+
});
|
|
1101
|
+
|
|
1102
|
+
if let Some(ref log) = executor.operation_log {
|
|
1103
|
+
if let Ok(envelopes) = log.load_all() {
|
|
1104
|
+
let applied_keys = executor.causal_barrier.applied_keys();
|
|
1105
|
+
let pending_keys = executor.pending_causal_keys();
|
|
1106
|
+
let deferred_keys = executor.deferred_causal_keys();
|
|
1107
|
+
|
|
1108
|
+
for env in &envelopes {
|
|
1109
|
+
let key = format!("{}:{}", env.envelope_id.originating_node, env.envelope_id.sequence);
|
|
1110
|
+
|
|
1111
|
+
if applied_keys.contains(&key) {
|
|
1112
|
+
operations["applied"].as_array_mut().unwrap().push(json!({
|
|
1113
|
+
"id": key,
|
|
1114
|
+
"origin": env.envelope_id.originating_node,
|
|
1115
|
+
"sequence": env.envelope_id.sequence,
|
|
1116
|
+
}));
|
|
1117
|
+
} else if pending_keys.contains(&key) {
|
|
1118
|
+
operations["pending"].as_array_mut().unwrap().push(json!({
|
|
1119
|
+
"id": key,
|
|
1120
|
+
"origin": env.envelope_id.originating_node,
|
|
1121
|
+
"sequence": env.envelope_id.sequence,
|
|
1122
|
+
}));
|
|
1123
|
+
} else if deferred_keys.contains(&key) {
|
|
1124
|
+
operations["deferred"].as_array_mut().unwrap().push(json!({
|
|
1125
|
+
"id": key,
|
|
1126
|
+
"origin": env.envelope_id.originating_node,
|
|
1127
|
+
"sequence": env.envelope_id.sequence,
|
|
1128
|
+
}));
|
|
1129
|
+
}
|
|
1130
|
+
}
|
|
1131
|
+
}
|
|
1132
|
+
}
|
|
1133
|
+
|
|
1134
|
+
println!("INVENTORY {}", operations);
|
|
1135
|
+
io::stdout().flush().ok();
|
|
1136
|
+
}
|
|
1137
|
+
|
|
1054
1138
|
"peers" => {
|
|
1055
1139
|
let mut state = json!({});
|
|
1056
1140
|
for link in &send_links {
|
|
@@ -27,6 +27,8 @@ pub mod sharding;
|
|
|
27
27
|
pub mod transaction_preconditions;
|
|
28
28
|
pub mod transactions;
|
|
29
29
|
pub mod state_hash;
|
|
30
|
+
pub mod state_model;
|
|
31
|
+
pub mod state_facade;
|
|
30
32
|
pub mod crash_injection;
|
|
31
33
|
pub mod concurrency_fuzzing;
|
|
32
34
|
pub mod replay_fuzzing;
|
|
@@ -167,6 +169,12 @@ pub use transactions::{
|
|
|
167
169
|
TransitionResult,
|
|
168
170
|
};
|
|
169
171
|
pub use state_hash::{CanonicalState, StateHash};
|
|
172
|
+
pub use state_model::{
|
|
173
|
+
StateId, StateRevision, StateTopology, Relationship, SemanticDiff, SemanticChange,
|
|
174
|
+
ChangeKind, PathComponent, ConflictClassification, ConflictClass, PathConflict,
|
|
175
|
+
ReconciliationPlan, StateReconciliationResult, StateStore, STATE_MODEL_VERSION,
|
|
176
|
+
};
|
|
177
|
+
pub use state_facade::FeltDBStateSystem;
|
|
170
178
|
pub use permutation_scheduler::{OperationSchedule, PermutationScheduler, ScheduleStrategy};
|
|
171
179
|
pub use multi_node_convergence::{
|
|
172
180
|
ConvergenceAggregation, ConvergenceResult, MultiNodeConvergenceSimulator, NodeExecutionResult,
|
|
@@ -1175,18 +1183,27 @@ impl FeltDb {
|
|
|
1175
1183
|
}
|
|
1176
1184
|
}
|
|
1177
1185
|
|
|
1178
|
-
// Every precondition held.
|
|
1179
|
-
//
|
|
1180
|
-
//
|
|
1181
|
-
//
|
|
1182
|
-
//
|
|
1186
|
+
// Every precondition held. The authority owns both ends of the
|
|
1187
|
+
// public document-version lifecycle inside this lock:
|
|
1188
|
+
//
|
|
1189
|
+
// - a create protected by `requireAbsent` writes version 1;
|
|
1190
|
+
// - a write fenced by `expected_version = N` writes N + 1.
|
|
1183
1191
|
//
|
|
1184
|
-
//
|
|
1185
|
-
//
|
|
1186
|
-
// every existing transaction caller, and a guard-only precondition
|
|
1187
|
-
// writes nothing at all. See
|
|
1192
|
+
// An unconditional write remains verbatim, and a guard-only
|
|
1193
|
+
// precondition writes nothing. See
|
|
1188
1194
|
// docs/architecture/transaction-version-contract.md.
|
|
1189
|
-
let
|
|
1195
|
+
let advanced: Vec<AtomicMutation>;
|
|
1196
|
+
let creates: HashSet<(&str, &str)> = preconditions
|
|
1197
|
+
.iter()
|
|
1198
|
+
.filter(|condition| condition.expected_version.is_none())
|
|
1199
|
+
.map(|condition| (condition.capability.as_str(), condition.key.as_str()))
|
|
1200
|
+
.chain(
|
|
1201
|
+
record_preconditions
|
|
1202
|
+
.iter()
|
|
1203
|
+
.filter(|condition| condition.require_absent)
|
|
1204
|
+
.map(|condition| (condition.capability.as_str(), condition.key.as_str())),
|
|
1205
|
+
)
|
|
1206
|
+
.collect();
|
|
1190
1207
|
let fenced: HashMap<(&str, &str), u64> = record_preconditions
|
|
1191
1208
|
.iter()
|
|
1192
1209
|
.filter_map(|condition| {
|
|
@@ -1195,15 +1212,19 @@ impl FeltDb {
|
|
|
1195
1212
|
.map(|version| ((condition.capability.as_str(), condition.key.as_str()), version))
|
|
1196
1213
|
})
|
|
1197
1214
|
.collect();
|
|
1198
|
-
let mutations: &[AtomicMutation] = if fenced.is_empty() {
|
|
1215
|
+
let mutations: &[AtomicMutation] = if fenced.is_empty() && creates.is_empty() {
|
|
1199
1216
|
mutations
|
|
1200
1217
|
} else {
|
|
1201
1218
|
advanced = mutations
|
|
1202
1219
|
.iter()
|
|
1203
1220
|
.map(|mutation| {
|
|
1204
|
-
let
|
|
1205
|
-
|
|
1206
|
-
|
|
1221
|
+
let record = (mutation.capability.as_str(), mutation.key.as_str());
|
|
1222
|
+
let version = if creates.contains(&record) {
|
|
1223
|
+
Some(1)
|
|
1224
|
+
} else {
|
|
1225
|
+
fenced.get(&record).map(|expected| expected + 1)
|
|
1226
|
+
};
|
|
1227
|
+
let Some(version) = version else {
|
|
1207
1228
|
return mutation.clone();
|
|
1208
1229
|
};
|
|
1209
1230
|
// A delete has no record left to carry a version, so a
|
|
@@ -1212,12 +1233,9 @@ impl FeltDb {
|
|
|
1212
1233
|
return mutation.clone();
|
|
1213
1234
|
};
|
|
1214
1235
|
let mut fields = fields;
|
|
1215
|
-
// The
|
|
1216
|
-
//
|
|
1217
|
-
|
|
1218
|
-
// because a caller that computes a different next
|
|
1219
|
-
// version must not be able to install it.
|
|
1220
|
-
fields.insert("__version".to_string(), Value::from(expected + 1));
|
|
1236
|
+
// The caller cannot manufacture either the initial or
|
|
1237
|
+
// next authoritative version.
|
|
1238
|
+
fields.insert("__version".to_string(), Value::from(version));
|
|
1221
1239
|
AtomicMutation {
|
|
1222
1240
|
capability: mutation.capability.clone(),
|
|
1223
1241
|
key: mutation.key.clone(),
|