@feltdb/core 0.13.0 → 0.16.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/dist/agent-runtime.d.ts +64 -2
- package/dist/agent-runtime.d.ts.map +1 -1
- package/dist/agent-runtime.js +121 -11
- package/dist/agent-state.d.ts +68 -0
- package/dist/agent-state.d.ts.map +1 -1
- package/dist/agent-state.js +210 -12
- package/dist/agent.d.ts +10 -1
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +10 -1
- package/dist/build-identity.json +12 -4
- package/dist/cli/commands.js +214 -10
- package/dist/cli/publish-engine.js +116 -16
- package/dist/collection.d.ts +3 -1
- package/dist/collection.d.ts.map +1 -1
- package/dist/collection.js +33 -8
- package/dist/create/build-identity.json +12 -4
- package/dist/create/cli.js +1 -1
- package/dist/create/create.js +4 -6
- package/dist/create/managed-account.js +93 -35
- package/dist/create/package-versions.js +1 -1
- package/dist/create/server-source/Cargo.lock +8 -511
- package/dist/create/server-source/Cargo.toml +2 -13
- package/dist/create/server-source/crates/feltdb-client/Cargo.toml +1 -1
- package/dist/create/server-source/crates/feltdb-server/Cargo.toml +1 -1
- package/dist/create/server-source/crates/feltdb-server/README.md +6 -1
- package/dist/create/server-source/crates/feltdb-server/src/app_state.rs +9 -1
- package/dist/create/server-source/crates/feltdb-server/src/auth.rs +85 -25
- package/dist/create/server-source/crates/feltdb-server/src/backup.rs +4 -0
- package/dist/create/server-source/crates/feltdb-server/src/console_accounts.rs +1009 -0
- package/dist/create/server-source/crates/feltdb-server/src/control_authorization.rs +441 -0
- package/dist/create/server-source/crates/feltdb-server/src/identity.rs +68 -6
- package/dist/create/server-source/crates/feltdb-server/src/key_management.rs +355 -23
- package/dist/create/server-source/crates/feltdb-server/src/lib.rs +3 -0
- package/dist/create/server-source/crates/feltdb-server/src/main.rs +1134 -96
- package/dist/create/server-source/crates/feltdb-server/src/redaction.rs +208 -0
- package/dist/create/server-source/crates/feltdb-server/src/tenancy.rs +198 -19
- package/dist/create/server-source/crates/feltdb-server/tests/transaction_precondition_contract.rs +403 -0
- package/dist/create/server-source/crates/feltdb-wasm/Cargo.toml +1 -1
- package/dist/db.d.ts +75 -10
- package/dist/db.d.ts.map +1 -1
- package/dist/db.js +168 -29
- package/dist/deployment.d.ts +19 -0
- package/dist/deployment.d.ts.map +1 -1
- package/dist/deployment.js +38 -2
- package/dist/embedded-query.d.ts.map +1 -1
- package/dist/embedded-query.js +5 -4
- package/dist/embedded-transaction.d.ts +8 -4
- package/dist/embedded-transaction.d.ts.map +1 -1
- package/dist/embedded-transaction.js +55 -19
- package/dist/error-codes.d.ts +36 -2
- package/dist/error-codes.d.ts.map +1 -1
- package/dist/error-codes.js +50 -3
- package/dist/feltdb.d.ts +7 -0
- package/dist/feltdb.d.ts.map +1 -1
- package/dist/file-db.d.ts +27 -0
- package/dist/file-db.d.ts.map +1 -1
- package/dist/file-db.js +145 -7
- package/dist/http-client.d.ts +11 -3
- package/dist/http-client.d.ts.map +1 -1
- package/dist/http-client.js +27 -14
- package/dist/http-db.d.ts +43 -1
- package/dist/http-db.d.ts.map +1 -1
- package/dist/http-db.js +287 -59
- package/dist/index.js +1 -1
- package/dist/local-state-lock.d.ts +34 -0
- package/dist/local-state-lock.d.ts.map +1 -0
- package/dist/local-state-lock.js +225 -0
- package/dist/managed-recovery.d.ts +6 -0
- package/dist/managed-recovery.d.ts.map +1 -1
- package/dist/managed-recovery.js +9 -0
- package/dist/reactive-graph.d.ts +0 -3
- package/dist/reactive-graph.d.ts.map +1 -1
- package/dist/reactive-graph.js +11 -1
- package/dist/runtime-registry.d.ts +7 -2
- package/dist/runtime-registry.d.ts.map +1 -1
- package/dist/runtime-registry.js +2 -2
- package/dist/studio/components/index.js +1 -1
- package/dist/studio/{components-BSGmHfif.js → components-CJmzntBi.js} +2 -1
- package/dist/studio/index.js +1 -1
- package/dist/studio-app/assets/{feltdb_wasm-3hDngl1h.js → feltdb_wasm-DtybUY0q.js} +1 -1
- package/dist/studio-app/assets/feltdb_wasm_bg-DsgIUwaB.wasm +0 -0
- package/dist/studio-app/assets/index-C4OffsZl.js +37 -0
- package/dist/studio-app/index.html +1 -1
- package/dist/transaction.d.ts +27 -11
- package/dist/transaction.d.ts.map +1 -1
- package/dist/transaction.js +20 -14
- package/dist/wasm/feltdb_wasm_bg.wasm +0 -0
- package/dist/wasm/package.json +1 -1
- package/dist/workload.d.ts +86 -6
- package/dist/workload.d.ts.map +1 -1
- package/dist/workload.js +51 -3
- package/dist/workspace/workspace-connection.d.ts.map +1 -1
- package/dist/workspace/workspace-connection.js +14 -3
- package/package.json +1 -1
- package/dist/create/server-source/crates/feltdb/Cargo.lock +0 -175
- package/dist/create/server-source/crates/feltdb/Cargo.toml +0 -102
- package/dist/create/server-source/crates/feltdb/LICENSE +0 -21
- package/dist/create/server-source/crates/feltdb/README.md +0 -75
- package/dist/create/server-source/crates/feltdb/benches/baselines/gate-13-redux.json +0 -370
- package/dist/create/server-source/crates/feltdb/benches/gate13_baseline.rs +0 -589
- package/dist/create/server-source/crates/feltdb/benches/gate13_phase_7_1_release_economics.rs +0 -247
- package/dist/create/server-source/crates/feltdb/benches/gate_13_redux.rs +0 -440
- package/dist/create/server-source/crates/feltdb/benches/gate_13_regression_runner.rs +0 -275
- package/dist/create/server-source/crates/feltdb/benches/gate_14a_concurrent_writer_scaling.rs +0 -381
- package/dist/create/server-source/crates/feltdb/benches/gate_14a_production_admission_revalidation.rs +0 -467
- package/dist/create/server-source/crates/feltdb/benches/gate_14a_rc2_admission_contract.rs +0 -489
- package/dist/create/server-source/crates/feltdb/benches/gate_14a_rc_root_cause.rs +0 -281
- package/dist/create/server-source/crates/feltdb/benches/gate_14a_sync1_queued_prototype.rs +0 -606
- package/dist/create/server-source/crates/feltdb/benches/gate_14a_sync_economics.rs +0 -531
- package/dist/create/server-source/crates/feltdb/benches/gate_14b_causal_backlog_scaling.rs +0 -462
- package/dist/create/server-source/crates/feltdb/benches/gate_14c_replication_contract_test.rs +0 -505
- package/dist/create/server-source/crates/feltdb/benches/gate_14c_replication_scaling.rs +0 -484
- package/dist/create/server-source/crates/feltdb/benches/gate_14d_combined_dimension_scaling.rs +0 -758
- package/dist/create/server-source/crates/feltdb/benches/phase_7_1_2_optimization_benchmark.rs +0 -421
- package/dist/create/server-source/crates/feltdb/benches/phase_7_1_3_crossover_analysis.rs +0 -329
- package/dist/create/server-source/crates/feltdb/src/acceptance_tests.rs +0 -1705
- package/dist/create/server-source/crates/feltdb/src/acquisition.rs +0 -286
- package/dist/create/server-source/crates/feltdb/src/admission.rs +0 -185
- package/dist/create/server-source/crates/feltdb/src/admission_contract_tests.rs +0 -507
- package/dist/create/server-source/crates/feltdb/src/adversarial_transport.rs +0 -539
- package/dist/create/server-source/crates/feltdb/src/agent_state.rs +0 -320
- package/dist/create/server-source/crates/feltdb/src/agent_transition_contract.rs +0 -1545
- package/dist/create/server-source/crates/feltdb/src/analytics.rs +0 -521
- package/dist/create/server-source/crates/feltdb/src/application.rs +0 -2674
- package/dist/create/server-source/crates/feltdb/src/application_runtime.rs +0 -1070
- package/dist/create/server-source/crates/feltdb/src/authority.rs +0 -479
- package/dist/create/server-source/crates/feltdb/src/authority_failover.rs +0 -847
- package/dist/create/server-source/crates/feltdb/src/authorization.rs +0 -1030
- package/dist/create/server-source/crates/feltdb/src/authorization_security_tests.rs +0 -1122
- package/dist/create/server-source/crates/feltdb/src/bin/feltdb-authority-client.rs +0 -32
- package/dist/create/server-source/crates/feltdb/src/bin/feltdb-authority.rs +0 -19
- package/dist/create/server-source/crates/feltdb/src/bin/feltdb_node.rs +0 -1474
- package/dist/create/server-source/crates/feltdb/src/capabilities/mod.rs +0 -8
- package/dist/create/server-source/crates/feltdb/src/capabilities/search.rs +0 -418
- package/dist/create/server-source/crates/feltdb/src/capabilities/vector.rs +0 -482
- package/dist/create/server-source/crates/feltdb/src/capability.rs +0 -903
- package/dist/create/server-source/crates/feltdb/src/cardinality_diagnostics.rs +0 -263
- package/dist/create/server-source/crates/feltdb/src/cardinality_endpoint.rs +0 -77
- package/dist/create/server-source/crates/feltdb/src/causal_backlog_bound.rs +0 -496
- package/dist/create/server-source/crates/feltdb/src/causal_dependency_barrier.rs +0 -2574
- package/dist/create/server-source/crates/feltdb/src/causal_dependency_barrier_phase_7_1.rs +0 -212
- package/dist/create/server-source/crates/feltdb/src/concurrency_fuzzing.rs +0 -422
- package/dist/create/server-source/crates/feltdb/src/consistency_contract.rs +0 -445
- package/dist/create/server-source/crates/feltdb/src/content_distribution.rs +0 -465
- package/dist/create/server-source/crates/feltdb/src/convergence.rs +0 -634
- package/dist/create/server-source/crates/feltdb/src/crash_atomic_boundary.rs +0 -427
- package/dist/create/server-source/crates/feltdb/src/crash_injection.rs +0 -376
- package/dist/create/server-source/crates/feltdb/src/crash_recovery_tests.rs +0 -365
- package/dist/create/server-source/crates/feltdb/src/cron.rs +0 -294
- package/dist/create/server-source/crates/feltdb/src/dedup_bound_investigation.rs +0 -483
- package/dist/create/server-source/crates/feltdb/src/distributed_indexing.rs +0 -477
- package/dist/create/server-source/crates/feltdb/src/distributed_tests.rs +0 -1003
- package/dist/create/server-source/crates/feltdb/src/distributed_transactions.rs +0 -1334
- package/dist/create/server-source/crates/feltdb/src/durability_guarantees.rs +0 -368
- package/dist/create/server-source/crates/feltdb/src/durable_dedup_set.rs +0 -231
- package/dist/create/server-source/crates/feltdb/src/durable_operation_identity.rs +0 -482
- package/dist/create/server-source/crates/feltdb/src/durable_operation_log.rs +0 -203
- package/dist/create/server-source/crates/feltdb/src/durable_sync.rs +0 -317
- package/dist/create/server-source/crates/feltdb/src/equality_index.rs +0 -595
- package/dist/create/server-source/crates/feltdb/src/execution.rs +0 -426
- package/dist/create/server-source/crates/feltdb/src/in_process_transport.rs +0 -214
- package/dist/create/server-source/crates/feltdb/src/indexing.rs +0 -776
- package/dist/create/server-source/crates/feltdb/src/lib.rs +0 -7274
- package/dist/create/server-source/crates/feltdb/src/managed_cas_tests.rs +0 -234
- package/dist/create/server-source/crates/feltdb/src/materialization.rs +0 -184
- package/dist/create/server-source/crates/feltdb/src/metrics.rs +0 -266
- package/dist/create/server-source/crates/feltdb/src/multi_node_convergence.rs +0 -310
- package/dist/create/server-source/crates/feltdb/src/multi_operation_transaction.rs +0 -736
- package/dist/create/server-source/crates/feltdb/src/observability.rs +0 -379
- package/dist/create/server-source/crates/feltdb/src/operation.rs +0 -290
- package/dist/create/server-source/crates/feltdb/src/operation_algebra.rs +0 -439
- package/dist/create/server-source/crates/feltdb/src/operation_log.rs +0 -349
- package/dist/create/server-source/crates/feltdb/src/p1_application_atomicity.rs +0 -332
- package/dist/create/server-source/crates/feltdb/src/p1_atomicity_acceptance.rs +0 -718
- package/dist/create/server-source/crates/feltdb/src/partition_reconciliation.rs +0 -487
- package/dist/create/server-source/crates/feltdb/src/peer_registry.rs +0 -166
- package/dist/create/server-source/crates/feltdb/src/permutation_scheduler.rs +0 -289
- package/dist/create/server-source/crates/feltdb/src/persistence_reality.rs +0 -566
- package/dist/create/server-source/crates/feltdb/src/phase1b_acceptance.rs +0 -3391
- package/dist/create/server-source/crates/feltdb/src/phase1c1_acceptance.rs +0 -203
- package/dist/create/server-source/crates/feltdb/src/phase1c2_acceptance.rs +0 -261
- package/dist/create/server-source/crates/feltdb/src/phase1c3_acceptance.rs +0 -502
- package/dist/create/server-source/crates/feltdb/src/phase1c_atomicity_proof.rs +0 -216
- package/dist/create/server-source/crates/feltdb/src/phase5_integration.rs +0 -303
- package/dist/create/server-source/crates/feltdb/src/phase5_scenarios.rs +0 -323
- package/dist/create/server-source/crates/feltdb/src/phase6_adversarial_scenarios.rs +0 -531
- package/dist/create/server-source/crates/feltdb/src/phase6_convergence_validator.rs +0 -406
- package/dist/create/server-source/crates/feltdb/src/phase6_persistence.rs +0 -436
- package/dist/create/server-source/crates/feltdb/src/phase_1c_real_tcp.rs +0 -387
- package/dist/create/server-source/crates/feltdb/src/phase_2a_failures.rs +0 -378
- package/dist/create/server-source/crates/feltdb/src/phase_2b_network.rs +0 -359
- package/dist/create/server-source/crates/feltdb/src/phase_2c_cascading.rs +0 -372
- package/dist/create/server-source/crates/feltdb/src/phase_3_durability.rs +0 -404
- package/dist/create/server-source/crates/feltdb/src/phase_4_baseline.rs +0 -376
- package/dist/create/server-source/crates/feltdb/src/phase_5_soak.rs +0 -461
- package/dist/create/server-source/crates/feltdb/src/policy_evaluation.rs +0 -2125
- package/dist/create/server-source/crates/feltdb/src/production_api.rs +0 -418
- package/dist/create/server-source/crates/feltdb/src/provenance.rs +0 -207
- package/dist/create/server-source/crates/feltdb/src/query_execution_diagnostics.rs +0 -126
- package/dist/create/server-source/crates/feltdb/src/query_performance.rs +0 -218
- package/dist/create/server-source/crates/feltdb/src/read_basis.rs +0 -257
- package/dist/create/server-source/crates/feltdb/src/references.rs +0 -404
- package/dist/create/server-source/crates/feltdb/src/replay_fuzzing.rs +0 -401
- package/dist/create/server-source/crates/feltdb/src/replica_acknowledgements.rs +0 -501
- package/dist/create/server-source/crates/feltdb/src/replica_membership.rs +0 -680
- package/dist/create/server-source/crates/feltdb/src/replication_manager.rs +0 -163
- package/dist/create/server-source/crates/feltdb/src/replication_protocol.rs +0 -133
- package/dist/create/server-source/crates/feltdb/src/routing.rs +0 -409
- package/dist/create/server-source/crates/feltdb/src/semantic_decision.rs +0 -2029
- package/dist/create/server-source/crates/feltdb/src/semantic_query.rs +0 -599
- package/dist/create/server-source/crates/feltdb/src/sharding.rs +0 -549
- package/dist/create/server-source/crates/feltdb/src/state_conflict_contract.rs +0 -516
- package/dist/create/server-source/crates/feltdb/src/state_contract.rs +0 -5894
- package/dist/create/server-source/crates/feltdb/src/state_diff_contract.rs +0 -222
- package/dist/create/server-source/crates/feltdb/src/state_facade.rs +0 -320
- package/dist/create/server-source/crates/feltdb/src/state_hash.rs +0 -291
- package/dist/create/server-source/crates/feltdb/src/state_model.rs +0 -4158
- package/dist/create/server-source/crates/feltdb/src/state_transition_store.rs +0 -379
- package/dist/create/server-source/crates/feltdb/src/state_trigger.rs +0 -673
- package/dist/create/server-source/crates/feltdb/src/storage.rs +0 -273
- package/dist/create/server-source/crates/feltdb/src/submission.rs +0 -471
- package/dist/create/server-source/crates/feltdb/src/sync.rs +0 -495
- package/dist/create/server-source/crates/feltdb/src/sync_contract.rs +0 -822
- package/dist/create/server-source/crates/feltdb/src/tcp_transport.rs +0 -433
- package/dist/create/server-source/crates/feltdb/src/transaction_api.rs +0 -462
- package/dist/create/server-source/crates/feltdb/src/transaction_invariants.rs +0 -965
- package/dist/create/server-source/crates/feltdb/src/transaction_preconditions.rs +0 -1120
- package/dist/create/server-source/crates/feltdb/src/transactions.rs +0 -643
- package/dist/create/server-source/crates/feltdb/src/trigger.rs +0 -390
- package/dist/create/server-source/crates/feltdb/src/trigger_contract.rs +0 -749
- package/dist/create/server-source/crates/feltdb/src/versioned_object_substrate.rs +0 -708
- package/dist/create/server-source/crates/feltdb/src/worker_mesh.rs +0 -1133
- package/dist/create/server-source/crates/feltdb/src/workflow.rs +0 -713
- package/dist/create/server-source/crates/feltdb/src/workflow_acceptance_tests.rs +0 -510
- package/dist/create/server-source/crates/feltdb/src/workflow_integration.rs +0 -354
- package/dist/create/server-source/crates/feltdb/src/workflow_runtime.rs +0 -594
- package/dist/create/server-source/crates/feltdb/src/workload.rs +0 -2886
- package/dist/create/server-source/crates/feltdb/src/workload_diagnostics.rs +0 -456
- package/dist/create/server-source/crates/feltdb/tests/agent_state_native.rs +0 -1188
- package/dist/create/server-source/crates/feltdb/tests/agent_transition_contract_conformance.rs +0 -2884
- package/dist/create/server-source/crates/feltdb/tests/authority_process.rs +0 -297
- package/dist/create/server-source/crates/feltdb/tests/authority_transaction_identity.rs +0 -534
- package/dist/create/server-source/crates/feltdb/tests/bounded_read_contract.rs +0 -132
- package/dist/create/server-source/crates/feltdb/tests/branching_evidence.rs +0 -299
- package/dist/create/server-source/crates/feltdb/tests/common/mod.rs +0 -96
- package/dist/create/server-source/crates/feltdb/tests/compaction_stall_contract.rs +0 -272
- package/dist/create/server-source/crates/feltdb/tests/crash_durability_contract.rs +0 -467
- package/dist/create/server-source/crates/feltdb/tests/current_revision_authority_evidence.rs +0 -309
- package/dist/create/server-source/crates/feltdb/tests/durable_backup_contract.rs +0 -445
- package/dist/create/server-source/crates/feltdb/tests/durable_corruption_contract.rs +0 -518
- package/dist/create/server-source/crates/feltdb/tests/durable_format_compatibility.rs +0 -392
- package/dist/create/server-source/crates/feltdb/tests/feltdb_state_boundary_tests.rs +0 -850
- package/dist/create/server-source/crates/feltdb/tests/fixtures/agent_transition_corpus.json +0 -23340
- package/dist/create/server-source/crates/feltdb/tests/fixtures/state_conflict_contract_corpus.json +0 -1916
- package/dist/create/server-source/crates/feltdb/tests/fixtures/state_diff_contract_corpus.json +0 -1878
- package/dist/create/server-source/crates/feltdb/tests/fixtures/trigger_contract_corpus.json +0 -1862
- package/dist/create/server-source/crates/feltdb/tests/generalized_version_control_evidence.rs +0 -123
- package/dist/create/server-source/crates/feltdb/tests/generalized_version_control_substrate_completion.rs +0 -299
- package/dist/create/server-source/crates/feltdb/tests/local_process_ownership.rs +0 -69
- package/dist/create/server-source/crates/feltdb/tests/managed_incident_regression.rs +0 -191
- package/dist/create/server-source/crates/feltdb/tests/operational_health_contract.rs +0 -278
- package/dist/create/server-source/crates/feltdb/tests/pr34_query_collection.rs +0 -234
- package/dist/create/server-source/crates/feltdb/tests/pr35_equality_index.rs +0 -947
- package/dist/create/server-source/crates/feltdb/tests/pr7_self_authorization_proof.rs +0 -404
- package/dist/create/server-source/crates/feltdb/tests/pr8_vocabulary_assessment.rs +0 -916
- package/dist/create/server-source/crates/feltdb/tests/pr9_phase2_boundary_tests.rs +0 -1045
- package/dist/create/server-source/crates/feltdb/tests/pr9_phase3a_path_a_tests.rs +0 -347
- package/dist/create/server-source/crates/feltdb/tests/pr9_phase3c_authorized_mutations.rs +0 -361
- package/dist/create/server-source/crates/feltdb/tests/pr9_phase3c_role_based_authorization.rs +0 -330
- package/dist/create/server-source/crates/feltdb/tests/pr9_phase3c_simple_auth_delete.rs +0 -93
- package/dist/create/server-source/crates/feltdb/tests/pr9_phase3c_team_delete_role_authorization.rs +0 -622
- package/dist/create/server-source/crates/feltdb/tests/pr9_teams_role_based_access.rs +0 -517
- package/dist/create/server-source/crates/feltdb/tests/production_certification.rs +0 -1153
- package/dist/create/server-source/crates/feltdb/tests/production_contract.rs +0 -771
- package/dist/create/server-source/crates/feltdb/tests/production_readiness_contract.rs +0 -1383
- package/dist/create/server-source/crates/feltdb/tests/reconciliation_application.rs +0 -868
- package/dist/create/server-source/crates/feltdb/tests/reconciliation_wire_format_evidence.rs +0 -221
- package/dist/create/server-source/crates/feltdb/tests/replicated_history_contract.rs +0 -417
- package/dist/create/server-source/crates/feltdb/tests/resource_scoped_revisions.rs +0 -338
- package/dist/create/server-source/crates/feltdb/tests/revision_identity_contract.rs +0 -1039
- package/dist/create/server-source/crates/feltdb/tests/revision_model_decision.rs +0 -739
- package/dist/create/server-source/crates/feltdb/tests/revision_retention_boundary_evidence.rs +0 -452
- package/dist/create/server-source/crates/feltdb/tests/saas_authorization_integration.rs +0 -81
- package/dist/create/server-source/crates/feltdb/tests/saas_invitation_lifecycle.rs +0 -438
- package/dist/create/server-source/crates/feltdb/tests/semantic_decision.rs +0 -1165
- package/dist/create/server-source/crates/feltdb/tests/semantic_query.rs +0 -767
- package/dist/create/server-source/crates/feltdb/tests/semantic_query_contract.rs +0 -363
- package/dist/create/server-source/crates/feltdb/tests/state_conflict_contract_conformance.rs +0 -1799
- package/dist/create/server-source/crates/feltdb/tests/state_diff_contract_conformance.rs +0 -1316
- package/dist/create/server-source/crates/feltdb/tests/state_model_integration.rs +0 -358
- package/dist/create/server-source/crates/feltdb/tests/state_persistence_integration.rs +0 -365
- package/dist/create/server-source/crates/feltdb/tests/state_ref_authority.rs +0 -918
- package/dist/create/server-source/crates/feltdb/tests/state_store_boundary_evidence.rs +0 -299
- package/dist/create/server-source/crates/feltdb/tests/storage_independent_versioned_object_substrate.rs +0 -328
- package/dist/create/server-source/crates/feltdb/tests/sync_divergence_evidence.rs +0 -255
- package/dist/create/server-source/crates/feltdb/tests/three_way_input_boundary_evidence.rs +0 -249
- package/dist/create/server-source/crates/feltdb/tests/trigger_contract_conformance.rs +0 -994
- package/dist/create/server-source/crates/feltdb/tests/workload_envelope_contract.rs +0 -484
- package/dist/studio-app/assets/feltdb_wasm_bg-CFQT4gCy.wasm +0 -0
- package/dist/studio-app/assets/index-Dql1hS5m.js +0 -30
package/dist/http-db.js
CHANGED
|
@@ -1,6 +1,111 @@
|
|
|
1
1
|
import { FeltDBErrorCode, FeltDBServiceError } from './error-codes.js';
|
|
2
|
+
import { assertBrowserSafeCredential } from './deployment.js';
|
|
2
3
|
import { conditionalRefusal } from './transaction.js';
|
|
3
4
|
import { BoundedBackoff, SingleFlightValue, cancellableDelay, describeFailure, } from './managed-recovery.js';
|
|
5
|
+
/** The origin of a URL, without any userinfo, for messages. */
|
|
6
|
+
function displayOrigin(url) {
|
|
7
|
+
try {
|
|
8
|
+
return new URL(url).origin;
|
|
9
|
+
}
|
|
10
|
+
catch {
|
|
11
|
+
return url.replace(/\/\/[^@/]*@/, '//');
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
const AUTHENTICATION_CODES = new Set(['AUTHENTICATION_REQUIRED', 'AUTHENTICATION_FAILED', 'UNAUTHORIZED', 'UNAUTHENTICATED']);
|
|
15
|
+
const LOAD_CODES = new Set(['SERVICE_OVERLOADED', 'TOO_BUSY', 'RATE_LIMITED', 'TOO_MANY_REQUESTS', 'QUERY_LIMIT_EXCEEDED']);
|
|
16
|
+
/**
|
|
17
|
+
* What to do next about a refusal the authority answered, or undefined when
|
|
18
|
+
* the authority's own message already says it. The code is never changed;
|
|
19
|
+
* this only adds the remedy a developer would otherwise have to look up.
|
|
20
|
+
*/
|
|
21
|
+
function refusalRemedy(code, status, message, context) {
|
|
22
|
+
const application = context.applicationId ? `application "${context.applicationId}"` : 'this authority';
|
|
23
|
+
const environment = context.environment ?? 'production';
|
|
24
|
+
if (code === 'AUTHORITY_SCOPE_MISMATCH') {
|
|
25
|
+
return `The tenant named in createFeltDB({ authorityScope }) does not own ${application}; use the tenant the application belongs to, or omit authorityScope.`;
|
|
26
|
+
}
|
|
27
|
+
if (status === 401 || AUTHENTICATION_CODES.has(code)) {
|
|
28
|
+
return context.hasToken
|
|
29
|
+
? `${context.origin} did not accept the credential. Check FELTDB_TOKEN: it must be an unexpired, unrevoked data key${context.applicationId ? ` issued for ${application}` : ''}. Keep it server-side only.`
|
|
30
|
+
: 'No credential was sent. Set FELTDB_TOKEN in the server process (never in browser code), or pass createFeltDB({ server: { token } }).';
|
|
31
|
+
}
|
|
32
|
+
if (code === 'UNKNOWN_COLLECTION') {
|
|
33
|
+
const collection = context.collection ?? (/^[A-Za-z_][\w.-]*$/.test(message) ? message : undefined);
|
|
34
|
+
return `Collection ${collection ? `"${collection}" ` : ''}is not declared in the contract promoted to environment "${environment}" for ${application}. Add it to the contract and publish and promote a new revision.`;
|
|
35
|
+
}
|
|
36
|
+
if (status === 403 || code === 'FORBIDDEN') {
|
|
37
|
+
// Discovery checks application:read; a canonical read or write also
|
|
38
|
+
// resolves the promoted revision (application:revision:read).
|
|
39
|
+
const scope = context.operation === 'write' ? 'the application:revision:read and state:write scopes'
|
|
40
|
+
: context.operation === 'discover' ? 'the application:read scope'
|
|
41
|
+
: context.operation === 'read' ? 'the application:revision:read and state:read scopes'
|
|
42
|
+
: 'the scopes this operation needs (state:read to read, state:write to write)';
|
|
43
|
+
return `The credential was accepted but is not authorized for ${application}${context.applicationId ? ` in environment "${environment}"` : ''}. Use a data key minted for this application with ${scope}`
|
|
44
|
+
+ (context.operation === 'write' && context.collection
|
|
45
|
+
? `; a write to a collection the promoted contract does not declare ("${context.collection}" may be one) is refused the same way.`
|
|
46
|
+
: '.');
|
|
47
|
+
}
|
|
48
|
+
if (status === 404 && context.applicationId) {
|
|
49
|
+
if (/no active contract/i.test(message)) {
|
|
50
|
+
return `No contract revision is promoted to environment "${environment}" for ${application}. Publish and promote one (feltdb publish --environment ${environment}), or set FELTDB_ENVIRONMENT to an environment that has one.`;
|
|
51
|
+
}
|
|
52
|
+
if (/application not found/i.test(message)) {
|
|
53
|
+
return `Check FELTDB_APPLICATION_ID: ${context.origin} has no ${application} visible to this credential.`;
|
|
54
|
+
}
|
|
55
|
+
if (/revision not found/i.test(message)) {
|
|
56
|
+
return `The revision promoted for ${application} is not readable; promote the revision again.`;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
if ((code === 'VALIDATION_FAILED' || code === 'VALIDATION_ERROR') && /unknown field/i.test(message)) {
|
|
60
|
+
return `Declare the field on ${context.collection ? `collection "${context.collection}"` : 'the collection'} in the contract and promote it, or stop writing it.`;
|
|
61
|
+
}
|
|
62
|
+
if (code === FeltDBErrorCode.DURABILITY_FAILURE) {
|
|
63
|
+
return 'Whether the commit is durable is unknown: retry with the same transactionId, and never treat this as a refusal.';
|
|
64
|
+
}
|
|
65
|
+
if (status === 429 || LOAD_CODES.has(code) || (status === 503 && code === FeltDBErrorCode.SERVICE_UNAVAILABLE)) {
|
|
66
|
+
return context.operation === 'write'
|
|
67
|
+
? 'The authority is shedding load: back off (honour Retry-After) and retry with the same transactionId.'
|
|
68
|
+
: 'The authority is shedding load: back off (honour Retry-After) and retry the read.';
|
|
69
|
+
}
|
|
70
|
+
return undefined;
|
|
71
|
+
}
|
|
72
|
+
/** The authority's message plus its code, status, request id and remedy. */
|
|
73
|
+
function refusalMessage(message, code, status, requestId, context) {
|
|
74
|
+
if (!context)
|
|
75
|
+
return message;
|
|
76
|
+
const remedy = refusalRemedy(code, status, message, context);
|
|
77
|
+
const where = [status ? `HTTP ${status}` : undefined, code, requestId ? `request ${requestId}` : undefined].filter(Boolean).join(', ');
|
|
78
|
+
return `${message} (${where})${remedy ? `. ${remedy}` : ''}`;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* The typed failure for a request that ended without an answer. A read is
|
|
82
|
+
* `SERVICE_UNAVAILABLE`; a mutation may have reached the authority and
|
|
83
|
+
* committed, so it is `COMMIT_OUTCOME_UNKNOWN`, never a refusal.
|
|
84
|
+
*/
|
|
85
|
+
function transportFailure(error, mutating, context) {
|
|
86
|
+
if (error instanceof FeltDBServiceError)
|
|
87
|
+
return error;
|
|
88
|
+
const raw = error instanceof Error ? error.message : String(error);
|
|
89
|
+
const cause = error?.cause;
|
|
90
|
+
const name = error?.name;
|
|
91
|
+
const timedOut = name === 'TimeoutError' || name === 'AbortError';
|
|
92
|
+
// Only a request that ended without an answer gets the connection advice;
|
|
93
|
+
// any other local error (a malformed key, say) keeps its own message.
|
|
94
|
+
const transport = timedOut || error instanceof TypeError || typeof cause?.code === 'string';
|
|
95
|
+
const detail = typeof cause?.code === 'string' ? cause.code : typeof cause?.message === 'string' ? cause.message : raw;
|
|
96
|
+
const message = !context || !transport ? raw
|
|
97
|
+
: `${timedOut ? `No answer from FeltDB at ${context.origin} within ${context.timeoutMs} ms` : `Could not reach FeltDB at ${context.origin} (${detail})`}. `
|
|
98
|
+
+ (mutating
|
|
99
|
+
? 'The write may have been applied: retry only with the same transactionId, never a new one. '
|
|
100
|
+
: 'Nothing was changed. ')
|
|
101
|
+
+ `Check FELTDB_URL and the service: GET ${context.origin}/health answers while the process is up, ${context.origin}/ready only when it can serve.`;
|
|
102
|
+
return new FeltDBServiceError(message, mutating ? FeltDBErrorCode.COMMIT_OUTCOME_UNKNOWN : FeltDBErrorCode.SERVICE_UNAVAILABLE);
|
|
103
|
+
}
|
|
104
|
+
/** A failed `JsResult` that keeps the typed failure. */
|
|
105
|
+
function failed(error, mutating, context) {
|
|
106
|
+
const cause = transportFailure(error, mutating, context);
|
|
107
|
+
return { success: false, error: cause.message, cause };
|
|
108
|
+
}
|
|
4
109
|
/**
|
|
5
110
|
* A durable identity for one conditional write attempt.
|
|
6
111
|
*
|
|
@@ -29,6 +134,19 @@ function managedCollectionRecord(value) {
|
|
|
29
134
|
delete record._version;
|
|
30
135
|
return record;
|
|
31
136
|
}
|
|
137
|
+
/** The collection a canonical request body addresses, for failure messages. */
|
|
138
|
+
function requestCollection(body) {
|
|
139
|
+
if (typeof body !== 'string')
|
|
140
|
+
return undefined;
|
|
141
|
+
try {
|
|
142
|
+
const value = JSON.parse(body);
|
|
143
|
+
const collection = value.query?.collection ?? value.transaction?.operations?.[0]?.collection;
|
|
144
|
+
return typeof collection === 'string' ? collection : undefined;
|
|
145
|
+
}
|
|
146
|
+
catch {
|
|
147
|
+
return undefined;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
32
150
|
/** Network adapter implementing the same collection runtime contract as WASM. */
|
|
33
151
|
export class HttpJsDb {
|
|
34
152
|
collection_queries_via_authority = true;
|
|
@@ -60,6 +178,8 @@ export class HttpJsDb {
|
|
|
60
178
|
changeListeners = new Set();
|
|
61
179
|
changeStream;
|
|
62
180
|
constructor(options) {
|
|
181
|
+
if (!options.allowServiceKeyInBrowser)
|
|
182
|
+
assertBrowserSafeCredential(options.token, typeof window !== 'undefined' && typeof document !== 'undefined', 'the HTTP runtime token');
|
|
63
183
|
this.url = options.url.replace(/\/$/, '');
|
|
64
184
|
this.token = options.token || '';
|
|
65
185
|
this.applicationId = options.applicationId;
|
|
@@ -127,6 +247,14 @@ export class HttpJsDb {
|
|
|
127
247
|
...(json ? { 'Content-Type': 'application/json' } : {}),
|
|
128
248
|
};
|
|
129
249
|
}
|
|
250
|
+
/** Where this client's requests go, for actionable failure messages. */
|
|
251
|
+
context(operation, collection) {
|
|
252
|
+
return {
|
|
253
|
+
origin: displayOrigin(this.url), timeoutMs: this.requestTimeoutMs, hasToken: !!this.token,
|
|
254
|
+
...(this.applicationId ? { applicationId: this.applicationId, environment: this.environment } : {}),
|
|
255
|
+
...(operation ? { operation } : {}), ...(collection ? { collection } : {}),
|
|
256
|
+
};
|
|
257
|
+
}
|
|
130
258
|
updateToken(token) {
|
|
131
259
|
this.token = token;
|
|
132
260
|
// Credentials changed, so a permanent authorization failure may no longer
|
|
@@ -202,11 +330,33 @@ export class HttpJsDb {
|
|
|
202
330
|
revisionState() {
|
|
203
331
|
return this.revisionCache.state;
|
|
204
332
|
}
|
|
205
|
-
|
|
333
|
+
/**
|
|
334
|
+
* A canonical *read* (`/query`, `/transactions/read-basis`). It is a POST
|
|
335
|
+
* only because its request has a body; an interrupted read changed nothing,
|
|
336
|
+
* so it is `SERVICE_UNAVAILABLE`, never `COMMIT_OUTCOME_UNKNOWN`.
|
|
337
|
+
*/
|
|
338
|
+
/**
|
|
339
|
+
* Run a request under the discovered revision. The authority answers only the revision
|
|
340
|
+
* active in this environment; after a promotion it refuses the old one with
|
|
341
|
+
* `REVISION_NOT_ACTIVE` and applies nothing. That refusal reaches the caller unchanged
|
|
342
|
+
* (the request is not resent under a different contract), and the revision is forgotten,
|
|
343
|
+
* so the next request runs under the one now active.
|
|
344
|
+
*/
|
|
345
|
+
async withRevision(request) {
|
|
206
346
|
const revisionId = await this.revisionId();
|
|
207
|
-
|
|
347
|
+
try {
|
|
348
|
+
return await request(revisionId);
|
|
349
|
+
}
|
|
350
|
+
catch (error) {
|
|
351
|
+
if (error?.code === FeltDBErrorCode.REVISION_NOT_ACTIVE)
|
|
352
|
+
this.revisionCache.forget(revisionId);
|
|
353
|
+
throw error;
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
async canonical(path, payload) {
|
|
357
|
+
return this.withRevision(revisionId => this.application_request(path, { method: 'POST', body: JSON.stringify({
|
|
208
358
|
application_id: this.applicationId, revision_id: revisionId, environment: this.environment, ...payload,
|
|
209
|
-
}) });
|
|
359
|
+
}) }, { read: true }));
|
|
210
360
|
}
|
|
211
361
|
transactionDocument(revisionId, operations, transactionId = crypto.randomUUID(), preconditions = [], fences = []) {
|
|
212
362
|
const tenantId = this.authorityScope?.kind === 'tenant' ? this.authorityScope.tenantId : '';
|
|
@@ -215,8 +365,7 @@ export class HttpJsDb {
|
|
|
215
365
|
...(fences.length ? { fences } : {}) };
|
|
216
366
|
}
|
|
217
367
|
async canonicalTransaction(operations, transactionId, preconditions = [], fences = []) {
|
|
218
|
-
|
|
219
|
-
return this.application_request('/transactions', { method: 'POST', body: JSON.stringify({ application_id: this.applicationId, revision_id: revisionId, environment: this.environment, transaction: this.transactionDocument(revisionId, operations, transactionId, preconditions, fences) }) });
|
|
368
|
+
return this.withRevision(revisionId => this.application_request('/transactions', { method: 'POST', body: JSON.stringify({ application_id: this.applicationId, revision_id: revisionId, environment: this.environment, transaction: this.transactionDocument(revisionId, operations, transactionId, preconditions, fences) }) }));
|
|
220
369
|
}
|
|
221
370
|
async get(key) {
|
|
222
371
|
try {
|
|
@@ -229,6 +378,7 @@ export class HttpJsDb {
|
|
|
229
378
|
}
|
|
230
379
|
const response = await fetch(`${this.url}/collections/${encodeURIComponent(collection)}/${encodeURIComponent(id)}`, {
|
|
231
380
|
headers: this.headers(),
|
|
381
|
+
signal: AbortSignal.timeout(this.requestTimeoutMs),
|
|
232
382
|
});
|
|
233
383
|
if (response.status === 404)
|
|
234
384
|
return { success: true };
|
|
@@ -238,7 +388,7 @@ export class HttpJsDb {
|
|
|
238
388
|
return { success: true, data: JSON.stringify(record.value) };
|
|
239
389
|
}
|
|
240
390
|
catch (error) {
|
|
241
|
-
return
|
|
391
|
+
return failed(error, false, this.context('read'));
|
|
242
392
|
}
|
|
243
393
|
}
|
|
244
394
|
async query(collection) {
|
|
@@ -257,6 +407,7 @@ export class HttpJsDb {
|
|
|
257
407
|
}
|
|
258
408
|
const response = await fetch(`${this.url}/collections/${encodeURIComponent(collection)}`, {
|
|
259
409
|
headers: this.headers(),
|
|
410
|
+
signal: AbortSignal.timeout(this.requestTimeoutMs),
|
|
260
411
|
});
|
|
261
412
|
if (!response.ok)
|
|
262
413
|
return this.failure(response);
|
|
@@ -264,7 +415,7 @@ export class HttpJsDb {
|
|
|
264
415
|
return { success: true, data: JSON.stringify(records.map(record => record.value)) };
|
|
265
416
|
}
|
|
266
417
|
catch (error) {
|
|
267
|
-
return
|
|
418
|
+
return failed(error, false, this.context('read'));
|
|
268
419
|
}
|
|
269
420
|
}
|
|
270
421
|
async authority_query(query) {
|
|
@@ -289,17 +440,21 @@ export class HttpJsDb {
|
|
|
289
440
|
} });
|
|
290
441
|
return { records: result.records.map(managedCollectionRecord), nextCursor: result.next_cursor, exhausted: !result.next_cursor, plan: result.plan };
|
|
291
442
|
}
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
error.code = body.code;
|
|
299
|
-
error.status = response.status;
|
|
300
|
-
throw error;
|
|
443
|
+
let response;
|
|
444
|
+
try {
|
|
445
|
+
response = await fetch(`${this.url}/query`, {
|
|
446
|
+
method: 'POST', headers: this.headers(true), body: JSON.stringify(query),
|
|
447
|
+
signal: AbortSignal.timeout(this.requestTimeoutMs),
|
|
448
|
+
});
|
|
301
449
|
}
|
|
302
|
-
|
|
450
|
+
catch (error) {
|
|
451
|
+
// A bounded query is a read: an interruption is SERVICE_UNAVAILABLE.
|
|
452
|
+
this.recordFailure(error);
|
|
453
|
+
throw transportFailure(error, false, this.context('read', query.collection));
|
|
454
|
+
}
|
|
455
|
+
if (!response.ok)
|
|
456
|
+
throw await this.refusal(response);
|
|
457
|
+
return await response.json();
|
|
303
458
|
}
|
|
304
459
|
async insert(key, value) {
|
|
305
460
|
try {
|
|
@@ -313,13 +468,14 @@ export class HttpJsDb {
|
|
|
313
468
|
method: 'POST',
|
|
314
469
|
headers: this.headers(true),
|
|
315
470
|
body: JSON.stringify({ ...record, id }),
|
|
471
|
+
signal: AbortSignal.timeout(this.requestTimeoutMs),
|
|
316
472
|
});
|
|
317
473
|
if (!response.ok)
|
|
318
474
|
return this.failure(response);
|
|
319
475
|
return { success: true, data: key };
|
|
320
476
|
}
|
|
321
477
|
catch (error) {
|
|
322
|
-
return
|
|
478
|
+
return failed(error, true, this.context('write'));
|
|
323
479
|
}
|
|
324
480
|
}
|
|
325
481
|
async update(key, value) {
|
|
@@ -333,13 +489,14 @@ export class HttpJsDb {
|
|
|
333
489
|
method: 'PATCH',
|
|
334
490
|
headers: this.headers(true),
|
|
335
491
|
body: value,
|
|
492
|
+
signal: AbortSignal.timeout(this.requestTimeoutMs),
|
|
336
493
|
});
|
|
337
494
|
if (!response.ok)
|
|
338
495
|
return this.failure(response);
|
|
339
496
|
return { success: true, data: key };
|
|
340
497
|
}
|
|
341
498
|
catch (error) {
|
|
342
|
-
return
|
|
499
|
+
return failed(error, true, this.context('write'));
|
|
343
500
|
}
|
|
344
501
|
}
|
|
345
502
|
/**
|
|
@@ -351,7 +508,10 @@ export class HttpJsDb {
|
|
|
351
508
|
async commit_transaction(request) {
|
|
352
509
|
if (this.applicationId) {
|
|
353
510
|
const operations = request.operations.map(operation => ({
|
|
354
|
-
|
|
511
|
+
// A write without `requireAbsent` creates an absent record and replaces an
|
|
512
|
+
// existing one, as on every other runtime (`put`). `update` would merge into an
|
|
513
|
+
// existing record and refuse an absent one.
|
|
514
|
+
kind: operation.value === undefined ? 'delete' : operation.requireAbsent ? 'insert' : 'put',
|
|
355
515
|
collection: operation.collection,
|
|
356
516
|
id: operation.id,
|
|
357
517
|
value: operation.value && typeof operation.value === 'object' && !Array.isArray(operation.value)
|
|
@@ -395,7 +555,7 @@ export class HttpJsDb {
|
|
|
395
555
|
}
|
|
396
556
|
catch (error) {
|
|
397
557
|
this.recordFailure(error);
|
|
398
|
-
throw
|
|
558
|
+
throw transportFailure(error, true, this.context('write', request.operations[0]?.collection));
|
|
399
559
|
}
|
|
400
560
|
if (!response.ok) {
|
|
401
561
|
// The authority answers a refused transaction with a structured
|
|
@@ -407,12 +567,13 @@ export class HttpJsDb {
|
|
|
407
567
|
body = await response.json();
|
|
408
568
|
}
|
|
409
569
|
catch { /* not JSON */ }
|
|
410
|
-
const
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
570
|
+
const code = body.code
|
|
571
|
+
?? (response.status >= 500 ? FeltDBErrorCode.SERVICE_UNAVAILABLE : FeltDBErrorCode.INVALID_REQUEST);
|
|
572
|
+
const error = new FeltDBServiceError(refusalMessage((code === body.error ? body.message : body.error) || body.message || `transaction failed with HTTP ${response.status}`, code, response.status, body.request_id, this.context('write', request.operations[0]?.collection)), code, body.request_id, response.status);
|
|
573
|
+
error.feltdbCode = code;
|
|
414
574
|
if (body.failure)
|
|
415
575
|
error.feltdbConflict = body.failure;
|
|
576
|
+
this.recordFailure(error);
|
|
416
577
|
throw error;
|
|
417
578
|
}
|
|
418
579
|
const body = await response.json();
|
|
@@ -566,17 +727,28 @@ export class HttpJsDb {
|
|
|
566
727
|
try {
|
|
567
728
|
const [collection, id] = this.splitKey(params.key);
|
|
568
729
|
const parsed = JSON.parse(params.value);
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
730
|
+
let response;
|
|
731
|
+
try {
|
|
732
|
+
response = await fetch(`${this.url}/collections/${encodeURIComponent(collection)}/${encodeURIComponent(id)}/cas`, {
|
|
733
|
+
method: 'POST',
|
|
734
|
+
headers: this.headers(true),
|
|
735
|
+
body: JSON.stringify({
|
|
736
|
+
expectedVersion: params.expectedVersion,
|
|
737
|
+
expectedEpoch: params.expectedEpoch,
|
|
738
|
+
expectedLeaseId: params.expectedLeaseId,
|
|
739
|
+
rejectIfDiverged: params.rejectIfDiverged,
|
|
740
|
+
value: parsed,
|
|
741
|
+
}),
|
|
742
|
+
signal: AbortSignal.timeout(this.requestTimeoutMs),
|
|
743
|
+
});
|
|
744
|
+
}
|
|
745
|
+
catch (error) {
|
|
746
|
+
// The request may have reached the authority and committed. A CAS
|
|
747
|
+
// carries no identity to retry by, so the caller reconciles by
|
|
748
|
+
// re-reading: a retried CAS is refused by the version it advanced.
|
|
749
|
+
this.recordFailure(error);
|
|
750
|
+
throw new FeltDBServiceError(error instanceof Error ? error.message : String(error), FeltDBErrorCode.COMMIT_OUTCOME_UNKNOWN);
|
|
751
|
+
}
|
|
580
752
|
// Handle conflict response (structured error)
|
|
581
753
|
if (response.status === 409) {
|
|
582
754
|
const body = await response.json();
|
|
@@ -590,10 +762,14 @@ export class HttpJsDb {
|
|
|
590
762
|
};
|
|
591
763
|
}
|
|
592
764
|
}
|
|
593
|
-
// Other errors
|
|
765
|
+
// Other errors, typed by the authority's code.
|
|
594
766
|
if (!response.ok) {
|
|
595
|
-
|
|
596
|
-
|
|
767
|
+
let body = {};
|
|
768
|
+
try {
|
|
769
|
+
body = await response.json();
|
|
770
|
+
}
|
|
771
|
+
catch { /* not JSON */ }
|
|
772
|
+
throw new FeltDBServiceError(body.message || body.error || `HTTP CAS failed with ${response.status}`, body.code || (response.status >= 500 ? FeltDBErrorCode.SERVICE_UNAVAILABLE : FeltDBErrorCode.INVALID_REQUEST), body.request_id, response.status);
|
|
597
773
|
}
|
|
598
774
|
// Success response
|
|
599
775
|
const result = await response.json();
|
|
@@ -613,11 +789,12 @@ export class HttpJsDb {
|
|
|
613
789
|
const response = await fetch(`${this.url}/collections/${encodeURIComponent(collection)}/${encodeURIComponent(id)}`, {
|
|
614
790
|
method: 'DELETE',
|
|
615
791
|
headers: this.headers(),
|
|
792
|
+
signal: AbortSignal.timeout(this.requestTimeoutMs),
|
|
616
793
|
});
|
|
617
794
|
return response.ok ? { success: true, data: key } : this.failure(response);
|
|
618
795
|
}
|
|
619
796
|
catch (error) {
|
|
620
|
-
return
|
|
797
|
+
return failed(error, true, this.context('write'));
|
|
621
798
|
}
|
|
622
799
|
}
|
|
623
800
|
/** Acquire a reference through the server's causal peer fabric. */
|
|
@@ -626,7 +803,7 @@ export class HttpJsDb {
|
|
|
626
803
|
if (response.status === 404)
|
|
627
804
|
return null;
|
|
628
805
|
if (!response.ok)
|
|
629
|
-
throw
|
|
806
|
+
throw await this.refusal(response);
|
|
630
807
|
return (await response.json()).value;
|
|
631
808
|
}
|
|
632
809
|
/** Execute the built-in state-derived search capability remotely. */
|
|
@@ -635,19 +812,19 @@ export class HttpJsDb {
|
|
|
635
812
|
method: 'POST', headers: this.headers(true), body: JSON.stringify({ query, limit }),
|
|
636
813
|
});
|
|
637
814
|
if (!response.ok)
|
|
638
|
-
throw
|
|
815
|
+
throw await this.refusal(response);
|
|
639
816
|
return (await response.json()).map(record => record.value);
|
|
640
817
|
}
|
|
641
818
|
async provenance(collection, id) {
|
|
642
819
|
const response = await fetch(`${this.url}/provenance/${encodeURIComponent(collection)}/${encodeURIComponent(id)}`, { headers: this.headers() });
|
|
643
820
|
if (!response.ok)
|
|
644
|
-
throw
|
|
821
|
+
throw await this.refusal(response);
|
|
645
822
|
return (await response.json()).value;
|
|
646
823
|
}
|
|
647
824
|
async storeContent(content) {
|
|
648
825
|
const response = await fetch(`${this.url}/content`, { method: 'PUT', headers: this.headers(), body: content });
|
|
649
826
|
if (!response.ok)
|
|
650
|
-
throw
|
|
827
|
+
throw await this.refusal(response);
|
|
651
828
|
return response.json();
|
|
652
829
|
}
|
|
653
830
|
async acquireContent(hash) {
|
|
@@ -655,7 +832,7 @@ export class HttpJsDb {
|
|
|
655
832
|
if (response.status === 404)
|
|
656
833
|
return null;
|
|
657
834
|
if (!response.ok)
|
|
658
|
-
throw
|
|
835
|
+
throw await this.refusal(response);
|
|
659
836
|
return new Uint8Array(await response.arrayBuffer());
|
|
660
837
|
}
|
|
661
838
|
async command(path, body) {
|
|
@@ -663,19 +840,28 @@ export class HttpJsDb {
|
|
|
663
840
|
method: 'POST', headers: this.headers(true), body: JSON.stringify(body),
|
|
664
841
|
});
|
|
665
842
|
if (!response.ok)
|
|
666
|
-
throw
|
|
843
|
+
throw await this.refusal(response);
|
|
667
844
|
return response.status === 204 ? undefined : response.json();
|
|
668
845
|
}
|
|
669
|
-
|
|
846
|
+
/**
|
|
847
|
+
* One request to the canonical application service. A request without a
|
|
848
|
+
* body-carrying method is a read; a POST is treated as a mutation unless the
|
|
849
|
+
* caller declares it a read (`{ read: true }`), because only a mutation can
|
|
850
|
+
* leave an uncertain commit behind.
|
|
851
|
+
*/
|
|
852
|
+
async application_request(path, init = {}, options = {}) {
|
|
670
853
|
const separator = path.includes('?') ? '&' : '?';
|
|
671
|
-
const
|
|
854
|
+
const query = this.applicationId
|
|
672
855
|
? `${separator}application_id=${encodeURIComponent(this.applicationId)}&environment=${encodeURIComponent(this.environment)}`
|
|
673
856
|
: '';
|
|
674
857
|
if (this.availabilityState === 'degraded')
|
|
675
858
|
this.availabilityState = 'recovering';
|
|
859
|
+
const mutating = !options.read && !['GET', 'HEAD', 'OPTIONS'].includes((init.method ?? 'GET').toUpperCase());
|
|
860
|
+
// Built only on failure: parsing the request body is not free.
|
|
861
|
+
const context = () => this.context(path.startsWith('/application') ? 'discover' : mutating ? 'write' : 'read', requestCollection(init.body));
|
|
676
862
|
let response;
|
|
677
863
|
try {
|
|
678
|
-
response = await fetch(`${this.url}/v1${path}${
|
|
864
|
+
response = await fetch(`${this.url}/v1${path}${query}`, {
|
|
679
865
|
...init,
|
|
680
866
|
signal: init.signal ?? AbortSignal.timeout(this.requestTimeoutMs),
|
|
681
867
|
headers: { ...this.headers(init.body !== undefined), ...(init.headers || {}) },
|
|
@@ -683,10 +869,17 @@ export class HttpJsDb {
|
|
|
683
869
|
}
|
|
684
870
|
catch (error) {
|
|
685
871
|
this.recordFailure(error);
|
|
686
|
-
|
|
687
|
-
|
|
872
|
+
throw transportFailure(error, mutating, context());
|
|
873
|
+
}
|
|
874
|
+
let text;
|
|
875
|
+
try {
|
|
876
|
+
text = await response.text();
|
|
877
|
+
}
|
|
878
|
+
catch (error) {
|
|
879
|
+
// The answer started and was cut off: for a mutation, still unknown.
|
|
880
|
+
this.recordFailure(error);
|
|
881
|
+
throw transportFailure(error, mutating, context());
|
|
688
882
|
}
|
|
689
|
-
const text = await response.text();
|
|
690
883
|
let body;
|
|
691
884
|
try {
|
|
692
885
|
body = text ? JSON.parse(text) : undefined;
|
|
@@ -695,7 +888,8 @@ export class HttpJsDb {
|
|
|
695
888
|
body = { message: text };
|
|
696
889
|
}
|
|
697
890
|
if (!response.ok) {
|
|
698
|
-
const
|
|
891
|
+
const code = body?.code || body?.error || 'REQUEST_FAILED';
|
|
892
|
+
const error = new FeltDBServiceError(refusalMessage(body?.message || body?.error || `FeltDB service request failed: HTTP ${response.status}`, code, response.status, body?.request_id, context()), code, body?.request_id, response.status, body && typeof body === 'object' && !Array.isArray(body) ? body : undefined);
|
|
699
893
|
// The same structured refusal the direct route carries, so a lost race
|
|
700
894
|
// is typed the same way on every route.
|
|
701
895
|
if (body?.failure)
|
|
@@ -706,6 +900,17 @@ export class HttpJsDb {
|
|
|
706
900
|
this.recordSuccess();
|
|
707
901
|
return body;
|
|
708
902
|
}
|
|
903
|
+
/**
|
|
904
|
+
* A `fetch` bound to this authority: the configured origin and the current credentials and scope headers, read per request so a refreshed
|
|
905
|
+
* token is honoured. `db.workloads` is built on it; a path is resolved against the origin.
|
|
906
|
+
*/
|
|
907
|
+
authority_transport() {
|
|
908
|
+
const fetcher = ((input, init = {}) => fetch(input, {
|
|
909
|
+
...init,
|
|
910
|
+
headers: { ...this.headers(), ...(init.headers || {}) },
|
|
911
|
+
}));
|
|
912
|
+
return { baseUrl: this.url, fetcher };
|
|
913
|
+
}
|
|
709
914
|
/** Canonical HTTP adapter used by the typed public substrate contract. */
|
|
710
915
|
async public_request(path, init = {}) {
|
|
711
916
|
let response;
|
|
@@ -733,7 +938,7 @@ export class HttpJsDb {
|
|
|
733
938
|
const error = new FeltDBServiceError(body?.message || body?.error || `FeltDB request failed: HTTP ${response.status}`, body?.code || (response.status === 401 ? FeltDBErrorCode.UNAUTHORIZED
|
|
734
939
|
: response.status === 403 ? FeltDBErrorCode.FORBIDDEN
|
|
735
940
|
: response.status === 404 ? FeltDBErrorCode.NOT_FOUND
|
|
736
|
-
: 'INVALID_REQUEST'), body?.request_id, response.status);
|
|
941
|
+
: 'INVALID_REQUEST'), body?.request_id, response.status, body && typeof body === 'object' && !Array.isArray(body) ? body : undefined);
|
|
737
942
|
this.recordFailure(error);
|
|
738
943
|
throw error;
|
|
739
944
|
}
|
|
@@ -877,15 +1082,38 @@ export class HttpJsDb {
|
|
|
877
1082
|
this.connectionState = 'closed';
|
|
878
1083
|
this.nextRetryAt = undefined;
|
|
879
1084
|
}
|
|
1085
|
+
/** A refused response as a failed `JsResult` carrying the typed refusal. */
|
|
880
1086
|
async failure(response) {
|
|
881
|
-
|
|
1087
|
+
const cause = await this.refusal(response);
|
|
1088
|
+
return { success: false, error: cause.message, cause };
|
|
1089
|
+
}
|
|
1090
|
+
/**
|
|
1091
|
+
* The authority's refusal, typed: its stable `code` (or one derived from the
|
|
1092
|
+
* status when the body carries none), status, request id and body.
|
|
1093
|
+
*/
|
|
1094
|
+
async refusal(response) {
|
|
1095
|
+
let body;
|
|
882
1096
|
try {
|
|
883
|
-
const
|
|
884
|
-
if (
|
|
885
|
-
|
|
1097
|
+
const parsed = await response.json();
|
|
1098
|
+
if (parsed && typeof parsed === 'object' && !Array.isArray(parsed))
|
|
1099
|
+
body = parsed;
|
|
886
1100
|
}
|
|
887
1101
|
catch { /* response was not JSON */ }
|
|
888
|
-
|
|
1102
|
+
const text = (name) => (typeof body?.[name] === 'string' ? body[name] : undefined);
|
|
1103
|
+
// An `error` field is a code only when it is spelled like one; older
|
|
1104
|
+
// answers put prose there.
|
|
1105
|
+
const errorField = text('error');
|
|
1106
|
+
const errorCode = errorField && /^[A-Z][A-Z0-9_]+$/.test(errorField) ? errorField : undefined;
|
|
1107
|
+
const code = text('code') ?? errorCode ?? (response.status === 401 ? FeltDBErrorCode.AUTHENTICATION_REQUIRED
|
|
1108
|
+
: response.status === 403 ? FeltDBErrorCode.FORBIDDEN
|
|
1109
|
+
: response.status === 404 ? FeltDBErrorCode.NOT_FOUND
|
|
1110
|
+
: response.status === 409 ? FeltDBErrorCode.CONFLICT
|
|
1111
|
+
: response.status >= 500 ? FeltDBErrorCode.SERVICE_UNAVAILABLE
|
|
1112
|
+
: FeltDBErrorCode.INVALID_REQUEST);
|
|
1113
|
+
const message = refusalMessage(text('message') ?? (errorField !== code ? errorField : undefined) ?? `HTTP ${response.status}`, code, response.status, text('request_id'), this.context());
|
|
1114
|
+
const error = new FeltDBServiceError(message, code, text('request_id'), response.status, body);
|
|
1115
|
+
this.recordFailure(error);
|
|
1116
|
+
return error;
|
|
889
1117
|
}
|
|
890
1118
|
get_capability_records(capability) { return this.query(capability); }
|
|
891
1119
|
execute_op() { return { success: false, error: 'Raw operation execution is internal to the server fabric' }; }
|
package/dist/index.js
CHANGED
|
@@ -15,7 +15,7 @@ import { registerDeploymentConfigLoader } from './deployment.js';
|
|
|
15
15
|
import { FileJsDb } from './file-db.js';
|
|
16
16
|
import * as nodeFs from 'fs';
|
|
17
17
|
import * as nodePath from 'path';
|
|
18
|
-
registerRuntime('file', path => new FileJsDb(path));
|
|
18
|
+
registerRuntime('file', (path, options) => new FileJsDb(path, options?.access ? { access: options.access } : {}));
|
|
19
19
|
registerDeploymentConfigLoader(() => {
|
|
20
20
|
const configPath = nodePath.join(process.cwd(), 'feltdb.config.json');
|
|
21
21
|
if (!nodeFs.existsSync(configPath))
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Exclusive ownership of a local state directory.
|
|
3
|
+
*
|
|
4
|
+
* The local file runtime is safe for several processes at the level of a
|
|
5
|
+
* single commit (each mutation takes `state.lock`). An application usually
|
|
6
|
+
* wants more: that no *other* process opens its state at all, because two
|
|
7
|
+
* application processes over one directory each believe they are the only
|
|
8
|
+
* writer of their caches, schedules, and in-process subscriptions.
|
|
9
|
+
*
|
|
10
|
+
* `acquire` creates `owner.lock` in the directory with `O_EXCL` and records
|
|
11
|
+
* the holder (`pid`, `host`, a random token). A second process finds it and is
|
|
12
|
+
* refused with `LocalStateLockedError`. Handles in the same process share the
|
|
13
|
+
* lock and the last `release` removes it; process exit removes it too.
|
|
14
|
+
*
|
|
15
|
+
* A lock whose holder is gone is recoverable: when the recorded host is this
|
|
16
|
+
* host and `process.kill(pid, 0)` reports no such process, the lock is taken
|
|
17
|
+
* over. The takeover renames the old lock aside and checks it is the one that
|
|
18
|
+
* was judged stale before deleting it, so two processes recovering the same
|
|
19
|
+
* stale lock cannot both win. A lock recorded by another host, or one that
|
|
20
|
+
* cannot be read, is never judged stale (it is refused, and the error names
|
|
21
|
+
* the file to remove by hand). A lock records its directory's real path, so a
|
|
22
|
+
* lock copied with the directory (a backup, an upgrade's verification copy)
|
|
23
|
+
* does not hold the copy.
|
|
24
|
+
*
|
|
25
|
+
* This is not authority state: it holds no data, and removing it never
|
|
26
|
+
* changes what the store contains.
|
|
27
|
+
*/
|
|
28
|
+
export declare const LOCAL_STATE_LOCK_FILE = "owner.lock";
|
|
29
|
+
/**
|
|
30
|
+
* Take exclusive ownership of `directory` for this process.
|
|
31
|
+
* Throws `LocalStateLockedError` when another live process holds it.
|
|
32
|
+
*/
|
|
33
|
+
export declare function acquireLocalStateLock(directory: string): () => void;
|
|
34
|
+
//# sourceMappingURL=local-state-lock.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"local-state-lock.d.ts","sourceRoot":"","sources":["../src/local-state-lock.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAOH,eAAO,MAAM,qBAAqB,eAAe,CAAC;AAiHlD;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,IAAI,CAoDnE"}
|