@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.
Files changed (35) hide show
  1. package/README.md +27 -1
  2. package/dist/cli/commands.js +64 -28
  3. package/dist/cli/index.js +1 -1
  4. package/dist/collection.d.ts.map +1 -1
  5. package/dist/collection.js +2 -1
  6. package/dist/create/package-versions.js +1 -1
  7. package/dist/create/server-source/crates/feltdb/src/bin/feltdb_node.rs +84 -0
  8. package/dist/create/server-source/crates/feltdb/src/lib.rs +38 -20
  9. package/dist/create/server-source/crates/feltdb/src/state_facade.rs +292 -0
  10. package/dist/create/server-source/crates/feltdb/src/state_model.rs +1856 -0
  11. package/dist/create/server-source/crates/feltdb/src/transaction_preconditions.rs +43 -11
  12. package/dist/create/server-source/crates/feltdb/tests/feltdb_state_boundary_tests.rs +634 -0
  13. package/dist/create/server-source/crates/feltdb/tests/state_model_integration.rs +366 -0
  14. package/dist/create/server-source/crates/feltdb/tests/state_persistence_integration.rs +270 -0
  15. package/dist/create/server-source/crates/feltdb-server/src/app_state.rs +13 -0
  16. package/dist/create/server-source/crates/feltdb-server/src/main.rs +194 -9
  17. package/dist/db.d.ts +7 -0
  18. package/dist/db.d.ts.map +1 -1
  19. package/dist/db.js +12 -1
  20. package/dist/embedded-transaction.d.ts.map +1 -1
  21. package/dist/embedded-transaction.js +14 -8
  22. package/dist/feltdb.d.ts +2 -0
  23. package/dist/feltdb.d.ts.map +1 -1
  24. package/dist/http-db.d.ts +24 -0
  25. package/dist/http-db.d.ts.map +1 -1
  26. package/dist/http-db.js +13 -0
  27. package/dist/index-core.d.ts +1 -0
  28. package/dist/index-core.d.ts.map +1 -1
  29. package/dist/studio-app/assets/{feltdb_wasm-CD744e5D.js → feltdb_wasm-DVKsw75S.js} +1 -1
  30. package/dist/studio-app/assets/feltdb_wasm_bg-DNyNf0yy.wasm +0 -0
  31. package/dist/studio-app/assets/{index-DoROs8yx.js → index-C71X92EK.js} +2 -2
  32. package/dist/studio-app/index.html +1 -1
  33. package/dist/wasm/feltdb_wasm_bg.wasm +0 -0
  34. package/package.json +1 -1
  35. 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.0 can fence a multi-record transaction on the exact version read by
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.
@@ -232,35 +232,69 @@ async function handleFlowDeploy(args) {
232
232
  }
233
233
  }
234
234
  async function handleServer(args) {
235
- console.log('🚀 Starting FeltDB Server\n');
236
- const port = args.includes('--port') ? args[args.indexOf('--port') + 1] || '7700' : '7700';
237
- const dataDir = args.includes('--data') ? args[args.indexOf('--data') + 1] || './data' : './data';
238
- const authEnabled = args.includes('--auth');
239
- console.log('FeltDB Self-Hosted Server');
240
- console.log(` Listen: 0.0.0.0:${port}`);
241
- console.log(` Storage: ${dataDir}`);
242
- console.log(` Auth: ${authEnabled ? 'Enabled' : 'Development (no auth required)'}`);
243
- console.log(` Namespace: default\n`);
244
- // Ensure data directory exists
245
- if (!fs.existsSync(dataDir)) {
246
- fs.mkdirSync(dataDir, { recursive: true });
247
- }
248
- // Store server info
249
- const serverConfig = {
250
- port: parseInt(port),
251
- dataDir,
252
- authEnabled,
253
- startTime: new Date().toISOString(),
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 configFile = path.join(dataDir, 'server.json');
256
- fs.writeFileSync(configFile, JSON.stringify(serverConfig, null, 2));
257
- console.log('🌐 Server listening...');
258
- console.log(` http://0.0.0.0:${port}\n`);
259
- console.log('✅ Ready for connections\n');
260
- console.log('To connect from another machine:');
261
- console.log(` feltdb connect http://localhost:${port}\n`);
262
- // Keep the server running
263
- await new Promise(() => { });
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.6.14';
26
+ const VERSION = '0.7.2';
27
27
  function prompt(question) {
28
28
  const rl = readline.createInterface({
29
29
  input: process.stdin,
@@ -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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"}
@@ -333,7 +333,8 @@ export class Collection {
333
333
  if (!current) {
334
334
  throw new Error(`Record ${id} not found`);
335
335
  }
336
- const updated = { ...current, ...changes };
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.6.14';
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. A write fenced by `expected_version` is a
1179
- // conditional replacement and behaves as `updateIfVersion` does:
1180
- // the authority validated version N above and writes N + 1 here,
1181
- // inside the same lock, so the check and the advance cannot be
1182
- // separated by another writer.
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
- // Only a version-fenced write advances. An unconditional write
1185
- // stores the caller's object verbatim, which is what preserves
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 mut advanced: Vec<AtomicMutation> = Vec::new();
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 Some(expected) =
1205
- fenced.get(&(mutation.capability.as_str(), mutation.key.as_str()))
1206
- else {
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 authority owns the transition. A `__version` the
1216
- // caller put in the value is replaced rather than
1217
- // honoured, exactly as updateIfVersion strips it,
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(),