@feltdb/core 0.9.1 → 0.11.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/cli/commands.js +151 -22
- package/dist/cli/development-session.js +16 -0
- package/dist/cli/index.js +1 -1
- package/dist/cli/studio-artifact.js +33 -0
- package/dist/collection.d.ts.map +1 -1
- package/dist/collection.js +6 -4
- package/dist/create/package-versions.js +1 -1
- package/dist/create/server-source/crates/feltdb/src/acceptance_tests.rs +8 -1
- package/dist/create/server-source/crates/feltdb/src/application.rs +16 -0
- package/dist/create/server-source/crates/feltdb/src/bin/feltdb_node.rs +21 -22
- package/dist/create/server-source/crates/feltdb/src/lib.rs +556 -45
- package/dist/create/server-source/crates/feltdb/src/state_contract.rs +48 -3
- package/dist/create/server-source/crates/feltdb/src/state_model.rs +569 -4
- package/dist/create/server-source/crates/feltdb/src/state_trigger.rs +1 -0
- package/dist/create/server-source/crates/feltdb/src/versioned_object_substrate.rs +708 -0
- package/dist/create/server-source/crates/feltdb/src/worker_mesh.rs +1 -0
- package/dist/create/server-source/crates/feltdb/src/workload.rs +480 -44
- package/dist/create/server-source/crates/feltdb/tests/generalized_version_control_evidence.rs +123 -0
- package/dist/create/server-source/crates/feltdb/tests/generalized_version_control_substrate_completion.rs +281 -0
- package/dist/create/server-source/crates/feltdb/tests/local_process_ownership.rs +69 -0
- package/dist/create/server-source/crates/feltdb/tests/production_readiness_contract.rs +15 -3
- package/dist/create/server-source/crates/feltdb/tests/revision_retention_boundary_evidence.rs +49 -24
- package/dist/create/server-source/crates/feltdb/tests/storage_independent_versioned_object_substrate.rs +328 -0
- package/dist/create/server-source/crates/feltdb/tests/workload_envelope_contract.rs +20 -3
- package/dist/create/server-source/crates/feltdb-server/src/app_state.rs +14 -4
- package/dist/create/server-source/crates/feltdb-server/src/backup.rs +0 -4
- package/dist/create/server-source/crates/feltdb-server/src/canonical_principals.rs +1093 -0
- package/dist/create/server-source/crates/feltdb-server/src/causal.rs +905 -144
- package/dist/create/server-source/crates/feltdb-server/src/connections.rs +9 -6
- package/dist/create/server-source/crates/feltdb-server/src/control_plane.rs +90 -0
- package/dist/create/server-source/crates/feltdb-server/src/lib.rs +1 -0
- package/dist/create/server-source/crates/feltdb-server/src/main.rs +418 -82
- package/dist/create/server-source/crates/feltdb-server/src/principals.rs +9 -501
- package/dist/create/server-source/crates/feltdb-server/src/tenancy.rs +22 -0
- package/dist/create/server-source/crates/feltdb-server/tests/principal_tenant_migration.rs +247 -0
- package/dist/db.d.ts +6 -5
- package/dist/db.d.ts.map +1 -1
- package/dist/db.js +18 -24
- package/dist/deployment.d.ts.map +1 -1
- package/dist/deployment.js +4 -2
- package/dist/embedded-transaction.d.ts.map +1 -1
- package/dist/embedded-transaction.js +4 -0
- package/dist/error-codes.d.ts +12 -0
- package/dist/error-codes.d.ts.map +1 -1
- package/dist/error-codes.js +20 -2
- package/dist/feltdb.d.ts +4 -0
- package/dist/feltdb.d.ts.map +1 -1
- package/dist/file-db.d.ts +12 -0
- package/dist/file-db.d.ts.map +1 -1
- package/dist/file-db.js +66 -6
- package/dist/http-db.d.ts +4 -0
- package/dist/http-db.d.ts.map +1 -1
- package/dist/http-db.js +73 -12
- package/dist/index-core.d.ts +1 -0
- package/dist/index-core.d.ts.map +1 -1
- package/dist/index-core.js +1 -0
- package/dist/memory-db.d.ts +1 -0
- package/dist/memory-db.d.ts.map +1 -1
- package/dist/memory-db.js +1 -1
- package/dist/public-contract.d.ts +112 -0
- package/dist/public-contract.d.ts.map +1 -0
- package/dist/public-contract.js +125 -0
- package/dist/studio/app.d.ts +23 -0
- package/dist/studio/app.d.ts.map +1 -1
- package/dist/studio/components/ContractInspector.d.ts +5 -0
- package/dist/studio/components/ContractInspector.d.ts.map +1 -1
- package/dist/studio/components/SettingsPanel.d.ts +4 -1
- package/dist/studio/components/SettingsPanel.d.ts.map +1 -1
- package/dist/studio/components/index.js +4 -4
- package/dist/studio/{components-CehkcX_k.js → components-BHTKlUgT.js} +323 -304
- package/dist/studio/control-plane/Data.d.ts.map +1 -1
- package/dist/studio/control-plane/Instance.d.ts +10 -0
- package/dist/studio/control-plane/Instance.d.ts.map +1 -1
- package/dist/studio/control-plane/Operations.d.ts +3 -0
- package/dist/studio/control-plane/Operations.d.ts.map +1 -1
- package/dist/studio/control-plane/State.d.ts.map +1 -1
- package/dist/studio/control-plane/index.d.ts +4 -2
- package/dist/studio/control-plane/index.d.ts.map +1 -1
- package/dist/studio/control-plane/index.js +2 -2
- package/dist/studio/control-plane-DP8ljTDO.js +3207 -0
- package/dist/studio/index.js +96 -75
- package/dist/studio/utils/api.d.ts +1 -1
- package/dist/studio/utils/api.d.ts.map +1 -1
- package/dist/studio/utils/control-plane-api.d.ts +12 -1
- package/dist/studio/utils/control-plane-api.d.ts.map +1 -1
- package/dist/studio/utils/control-plane-api.js +1 -1
- package/dist/studio/utils/index.js +16 -16
- package/dist/studio/utils/service-api.d.ts +8 -0
- package/dist/studio/utils/service-api.d.ts.map +1 -1
- package/dist/studio/utils/service-api.js +15 -12
- package/dist/studio-app/assets/{feltdb_wasm-DksXDnLa.js → feltdb_wasm-Dlr_kLlk.js} +1 -1
- package/dist/studio-app/assets/feltdb_wasm_bg-CkozAQKb.wasm +0 -0
- package/dist/studio-app/assets/index-H_L5pjur.js +30 -0
- package/dist/studio-app/feltdb-studio.json +6 -0
- package/dist/studio-app/index.html +1 -1
- package/dist/transaction.d.ts +9 -1
- package/dist/transaction.d.ts.map +1 -1
- package/dist/transaction.js +6 -2
- package/dist/wasm/feltdb_wasm_bg.wasm +0 -0
- package/dist/workspace/local-development-authority.d.ts +8 -0
- package/dist/workspace/local-development-authority.d.ts.map +1 -1
- package/dist/workspace/local-development-authority.js +136 -1
- package/package.json +2 -2
- package/dist/studio/control-plane-D6TT2fCQ.js +0 -3064
- package/dist/studio-app/assets/feltdb_wasm_bg-Yy4QRHJT.wasm +0 -0
- package/dist/studio-app/assets/index-CZ55nLgp.js +0 -30
package/dist/cli/commands.js
CHANGED
|
@@ -5,7 +5,7 @@ import fs from 'fs';
|
|
|
5
5
|
import path from 'path';
|
|
6
6
|
import http from 'http';
|
|
7
7
|
import net from 'net';
|
|
8
|
-
import { createRequire } from 'module';
|
|
8
|
+
import { createRequire } from 'node:module';
|
|
9
9
|
import { spawn, spawnSync } from 'child_process';
|
|
10
10
|
import { randomBytes } from 'crypto';
|
|
11
11
|
import { createInterface } from 'node:readline/promises';
|
|
@@ -15,7 +15,7 @@ import { collectRepositoryEvidence } from './repository-evidence.js';
|
|
|
15
15
|
import { discoverWorkspace, ensureWorkspaceGitIgnored, generatePairingToken, persistPairingToken, displayWorkspaceStatus, initializeWorkspace, startPairingDiscoveryServer } from './workspace-integration.js';
|
|
16
16
|
import { applicationUrlCandidate, detectApplication, discoverApplicationUrl } from './application.js';
|
|
17
17
|
import { resolveApplicationLifecycle, startManagedApplication, waitForManagedApplication } from './application-lifecycle.js';
|
|
18
|
-
import { createDevelopmentSession, developmentSessionEnvironment, developmentSessionSummary, transitionDevelopmentSession } from './development-session.js';
|
|
18
|
+
import { createDevelopmentSession, developmentSessionEnvironment, developmentSessionSummary, developmentStudioArguments, transitionDevelopmentSession } from './development-session.js';
|
|
19
19
|
import { openApplication, openStudio } from './browser-opening.js';
|
|
20
20
|
import { publishApplication } from './publish-engine.js';
|
|
21
21
|
import { applyFlowProposalAtomic, assertProposalSourcePlan, describeProposal, loadLocalDevelopmentContract, repositoryContext, reviewFlowProposal, syncGeneratedSource } from './source-sync.js';
|
|
@@ -23,6 +23,8 @@ import { configureAiModel, generateConfiguredFlowProposal, publicAiModelConfigur
|
|
|
23
23
|
import { proposalService } from './proposal-client.js';
|
|
24
24
|
import { resolveManagedAuthorityEnvironment } from './managed-environment.js';
|
|
25
25
|
import { readManagedProjectState } from './managed-project.js';
|
|
26
|
+
import { resolveCanonicalStudioArtifact } from './studio-artifact.js';
|
|
27
|
+
import { configureManagedAccount, finalizeManagedProvisioning } from '../create/index.js';
|
|
26
28
|
function loadProjectEnvironment(file = path.resolve('.env.local')) {
|
|
27
29
|
if (!fs.existsSync(file))
|
|
28
30
|
return;
|
|
@@ -60,6 +62,8 @@ export async function handleCommand(command, args) {
|
|
|
60
62
|
return handleServer(args);
|
|
61
63
|
case 'keys':
|
|
62
64
|
return handleKeys(args);
|
|
65
|
+
case 'init-managed':
|
|
66
|
+
return handleInitManaged(args);
|
|
63
67
|
case 'connect':
|
|
64
68
|
return handleConnect(args);
|
|
65
69
|
case 'status':
|
|
@@ -863,6 +867,123 @@ async function handleConnect(args) {
|
|
|
863
867
|
console.log(`Health: ${health.status || 'healthy'}\n`);
|
|
864
868
|
console.log('Use: feltdb status (to check connection)\n');
|
|
865
869
|
}
|
|
870
|
+
async function promptSecret(message) {
|
|
871
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY)
|
|
872
|
+
return '';
|
|
873
|
+
const input = process.stdin;
|
|
874
|
+
const output = process.stdout;
|
|
875
|
+
const wasRaw = input.isRaw;
|
|
876
|
+
const readline = await import('node:readline');
|
|
877
|
+
readline.emitKeypressEvents(input);
|
|
878
|
+
input.setRawMode(true);
|
|
879
|
+
input.resume();
|
|
880
|
+
output.write(message);
|
|
881
|
+
return new Promise(resolve => {
|
|
882
|
+
let value = '';
|
|
883
|
+
const finish = () => {
|
|
884
|
+
input.off('keypress', onKeypress);
|
|
885
|
+
input.setRawMode(Boolean(wasRaw));
|
|
886
|
+
input.pause();
|
|
887
|
+
output.write('\n');
|
|
888
|
+
resolve(value);
|
|
889
|
+
};
|
|
890
|
+
const onKeypress = (character, key) => {
|
|
891
|
+
if (key.ctrl && key.name === 'c')
|
|
892
|
+
process.exit(130);
|
|
893
|
+
if (key.name === 'return' || key.name === 'enter')
|
|
894
|
+
return finish();
|
|
895
|
+
if (key.name === 'backspace') {
|
|
896
|
+
value = value.slice(0, -1);
|
|
897
|
+
return;
|
|
898
|
+
}
|
|
899
|
+
if (!key.ctrl && !key.meta && character)
|
|
900
|
+
value += character;
|
|
901
|
+
};
|
|
902
|
+
input.on('keypress', onKeypress);
|
|
903
|
+
});
|
|
904
|
+
}
|
|
905
|
+
async function handleInitManaged(args) {
|
|
906
|
+
loadProjectEnvironment();
|
|
907
|
+
const projectDir = process.cwd();
|
|
908
|
+
// Check if already managed
|
|
909
|
+
const managedState = readManagedProjectState(projectDir);
|
|
910
|
+
if (managedState.applicationId) {
|
|
911
|
+
console.log('✅ This project is already configured for managed FeltDB\n');
|
|
912
|
+
console.log(`Application: ${managedState.applicationId}`);
|
|
913
|
+
console.log(`Namespace: ${managedState.namespace}`);
|
|
914
|
+
console.log(`URL: ${managedState.url}`);
|
|
915
|
+
if (managedState.revisionId) {
|
|
916
|
+
console.log(`Revision: ${managedState.revisionId}`);
|
|
917
|
+
}
|
|
918
|
+
console.log('\nTo update credentials, manually edit .env.local and .feltdb/managed.json\n');
|
|
919
|
+
return;
|
|
920
|
+
}
|
|
921
|
+
// Prompt for credentials
|
|
922
|
+
console.log('☁️ Setting up managed FeltDB for this project\n');
|
|
923
|
+
const prompt = await import('node:readline/promises').then(m => m.createInterface({ input: process.stdin, output: process.stdout }));
|
|
924
|
+
const email = args[0] || await prompt.question('Email: ');
|
|
925
|
+
if (!email)
|
|
926
|
+
throw new Error('Email is required');
|
|
927
|
+
const password = args[1] || await promptSecret('Password (8+ characters): ');
|
|
928
|
+
if (password.length < 8)
|
|
929
|
+
throw new Error('Password must be at least 8 characters');
|
|
930
|
+
prompt.close();
|
|
931
|
+
console.log('\nProvisioning managed account...\n');
|
|
932
|
+
try {
|
|
933
|
+
const projectConfig = fs.existsSync(path.join(projectDir, 'feltdb.config.json'))
|
|
934
|
+
? JSON.parse(fs.readFileSync(path.join(projectDir, 'feltdb.config.json'), 'utf8'))
|
|
935
|
+
: {};
|
|
936
|
+
const namespace = projectConfig.namespace || path.basename(projectDir);
|
|
937
|
+
const applicationName = projectConfig.name || path.basename(projectDir);
|
|
938
|
+
const managed = await configureManagedAccount({
|
|
939
|
+
projectDir,
|
|
940
|
+
applicationName,
|
|
941
|
+
namespace,
|
|
942
|
+
email,
|
|
943
|
+
password,
|
|
944
|
+
});
|
|
945
|
+
console.log(`✓ Managed application: ${managed.application.name}`);
|
|
946
|
+
console.log(`✓ Namespace: ${managed.application.namespace || namespace}`);
|
|
947
|
+
console.log(`✓ Environment configured in .env.local\n`);
|
|
948
|
+
// Ask if user wants to publish the initial revision
|
|
949
|
+
const publishPrompt = await import('node:readline/promises').then(m => m.createInterface({ input: process.stdin, output: process.stdout }));
|
|
950
|
+
const shouldPublish = await publishPrompt.question('Publish initial application revision now? [Y/n] ');
|
|
951
|
+
publishPrompt.close();
|
|
952
|
+
if (!/^(n|no)$/i.test(shouldPublish.trim())) {
|
|
953
|
+
console.log('\n🔨 Building and publishing initial revision...\n');
|
|
954
|
+
const npm = process.platform === 'win32' ? 'npm.cmd' : 'npm';
|
|
955
|
+
// Run publish
|
|
956
|
+
await new Promise((resolve, reject) => {
|
|
957
|
+
const child = spawn(npm, ['run', 'feltdb:publish'], {
|
|
958
|
+
cwd: projectDir,
|
|
959
|
+
stdio: 'inherit',
|
|
960
|
+
env: process.env,
|
|
961
|
+
});
|
|
962
|
+
child.once('error', reject);
|
|
963
|
+
child.once('exit', code => code === 0 ? resolve() : reject(new Error(`feltdb publish exited with status ${code ?? 'unknown'}`)));
|
|
964
|
+
});
|
|
965
|
+
console.log('\n✓ Initial revision published\n');
|
|
966
|
+
// Finalize and verify
|
|
967
|
+
try {
|
|
968
|
+
const finalized = await finalizeManagedProvisioning({ projectDir });
|
|
969
|
+
console.log(`✓ Managed runtime verified at revision ${finalized.revisionId}\n`);
|
|
970
|
+
}
|
|
971
|
+
catch (error) {
|
|
972
|
+
console.log('⚠️ Revision published but verification skipped.\n');
|
|
973
|
+
console.log('Your application is ready to use. Run: npm run dev\n');
|
|
974
|
+
}
|
|
975
|
+
}
|
|
976
|
+
else {
|
|
977
|
+
console.log('\n✅ Managed setup complete!\n');
|
|
978
|
+
console.log('Next steps:');
|
|
979
|
+
console.log(' 1. npm run feltdb:publish (to publish your initial revision)');
|
|
980
|
+
console.log(' 2. npm run dev (to start development)\n');
|
|
981
|
+
}
|
|
982
|
+
}
|
|
983
|
+
catch (error) {
|
|
984
|
+
throw error;
|
|
985
|
+
}
|
|
986
|
+
}
|
|
866
987
|
async function handleExplain(args) {
|
|
867
988
|
const flowRef = args[0];
|
|
868
989
|
if (!flowRef || !flowRef.startsWith('flow://')) {
|
|
@@ -1127,6 +1248,14 @@ async function handleDev(args) {
|
|
|
1127
1248
|
port: authorityPort,
|
|
1128
1249
|
proposalContext: () => loadLocalDevelopmentContract(projectDir),
|
|
1129
1250
|
intentObservations: () => collectRepositoryEvidence(projectDir),
|
|
1251
|
+
runtimeContext: {
|
|
1252
|
+
namespace: runtimeNamespace || config.namespace,
|
|
1253
|
+
runtime: config.runtime,
|
|
1254
|
+
storage: config.storage,
|
|
1255
|
+
distributed: Boolean(config.distributed),
|
|
1256
|
+
applicationLifecycle,
|
|
1257
|
+
applicationUrl: appUrl,
|
|
1258
|
+
},
|
|
1130
1259
|
});
|
|
1131
1260
|
process.env.VITE_FELTDB_URL = localAuthority.endpoint;
|
|
1132
1261
|
const connectionsPath = path.join(projectDir, '.feltdb', 'connections.json');
|
|
@@ -1277,20 +1406,7 @@ async function handleDev(args) {
|
|
|
1277
1406
|
}
|
|
1278
1407
|
const open = !args.includes('--no-open');
|
|
1279
1408
|
try {
|
|
1280
|
-
const studio = handleStudio(
|
|
1281
|
-
'--port', studioPort,
|
|
1282
|
-
'--namespace', developmentSession.namespace,
|
|
1283
|
-
'--runtime', developmentSession.runtime,
|
|
1284
|
-
'--workspace-id', developmentSession.workspaceId,
|
|
1285
|
-
'--session-id', developmentSession.sessionId,
|
|
1286
|
-
'--project-id', developmentSession.projectId,
|
|
1287
|
-
'--authority-url', developmentSession.authorityUrl,
|
|
1288
|
-
'--pairing-url', developmentSession.pairingUrl,
|
|
1289
|
-
'--lifecycle', developmentSession.lifecycle,
|
|
1290
|
-
...(developmentSession.applicationUrl ? ['--app-url', developmentSession.applicationUrl] : []),
|
|
1291
|
-
...((developmentSession.runtime === 'self-hosted' || developmentSession.runtime === 'managed') ? ['--connect', developmentSession.authorityUrl] : []),
|
|
1292
|
-
'--no-open',
|
|
1293
|
-
], studioUrl => {
|
|
1409
|
+
const studio = handleStudio(developmentStudioArguments(developmentSession, studioPort), studioUrl => {
|
|
1294
1410
|
developmentSession.studioUrl = studioUrl;
|
|
1295
1411
|
transitionDevelopmentSession(developmentSession, 'READY');
|
|
1296
1412
|
console.log(`\n${developmentSessionSummary(developmentSession)}\n`);
|
|
@@ -1640,13 +1756,15 @@ async function handleStudio(args, onReady) {
|
|
|
1640
1756
|
else {
|
|
1641
1757
|
console.log(`Studio Server: http://localhost:${port}\n`);
|
|
1642
1758
|
}
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1759
|
+
let studioArtifact;
|
|
1760
|
+
try {
|
|
1761
|
+
studioArtifact = resolveCanonicalStudioArtifact();
|
|
1762
|
+
}
|
|
1763
|
+
catch (error) {
|
|
1764
|
+
console.log(`⚠️ ${error instanceof Error ? error.message : String(error)}`);
|
|
1648
1765
|
return;
|
|
1649
1766
|
}
|
|
1767
|
+
const root = studioArtifact.root;
|
|
1650
1768
|
const canonicalFlow = path.resolve(process.cwd(), 'feltdb.flow');
|
|
1651
1769
|
const projectFlow = fs.existsSync(canonicalFlow) ? canonicalFlow : undefined;
|
|
1652
1770
|
const studioMutationToken = randomBytes(24).toString('hex');
|
|
@@ -2019,6 +2137,7 @@ Commands:
|
|
|
2019
2137
|
diff [file] Diff FlowSpec against the last deployed model
|
|
2020
2138
|
deploy [file] Validate, version, and deploy FlowSpec
|
|
2021
2139
|
publish [file] Build and publish/update a managed application
|
|
2140
|
+
init-managed Set up managed FeltDB for an existing project
|
|
2022
2141
|
ai Open the canonical interactive local-AI workflow
|
|
2023
2142
|
ai --dry-run Open an ephemeral preview-only AI session
|
|
2024
2143
|
ai model show Show the selected local or user-provided model
|
|
@@ -2068,7 +2187,15 @@ API Key Commands:
|
|
|
2068
2187
|
feltdb keys revoke <key-id>
|
|
2069
2188
|
|
|
2070
2189
|
Connection:
|
|
2071
|
-
feltdb connect <url>
|
|
2190
|
+
feltdb connect <url> Connect to a running FeltDB server
|
|
2191
|
+
|
|
2192
|
+
Managed FeltDB Setup (Existing Projects):
|
|
2193
|
+
feltdb init-managed Set up managed FeltDB for an existing project
|
|
2194
|
+
Prompts for email and password, provisions resources,
|
|
2195
|
+
and optionally publishes the initial revision
|
|
2196
|
+
feltdb init-managed <email> <password> Non-interactive setup with provided credentials
|
|
2197
|
+
|
|
2198
|
+
Development:
|
|
2072
2199
|
dev Start development server
|
|
2073
2200
|
build Build FeltDB application
|
|
2074
2201
|
check Validate configuration
|
|
@@ -2102,6 +2229,8 @@ Examples:
|
|
|
2102
2229
|
feltdb studio --connect http://localhost:7700
|
|
2103
2230
|
feltdb server --port 7700 --data ./data
|
|
2104
2231
|
feltdb keys create --name development
|
|
2232
|
+
feltdb init-managed (interactive - prompts for email/password)
|
|
2233
|
+
feltdb init-managed user@example.com secret123
|
|
2105
2234
|
feltdb connect http://localhost:7700
|
|
2106
2235
|
feltdb status
|
|
2107
2236
|
feltdb dev
|
|
@@ -41,6 +41,22 @@ export function developmentSessionEnvironment(session) {
|
|
|
41
41
|
VITE_FELTDB_NAMESPACE: session.namespace,
|
|
42
42
|
};
|
|
43
43
|
}
|
|
44
|
+
export function developmentStudioArguments(session, port) {
|
|
45
|
+
return [
|
|
46
|
+
'--port', port,
|
|
47
|
+
'--namespace', session.namespace,
|
|
48
|
+
'--runtime', session.runtime,
|
|
49
|
+
'--workspace-id', session.workspaceId,
|
|
50
|
+
'--session-id', session.sessionId,
|
|
51
|
+
'--project-id', session.projectId,
|
|
52
|
+
'--authority-url', session.authorityUrl,
|
|
53
|
+
'--pairing-url', session.pairingUrl,
|
|
54
|
+
'--lifecycle', session.lifecycle,
|
|
55
|
+
...(session.applicationUrl ? ['--app-url', session.applicationUrl] : []),
|
|
56
|
+
'--connect', session.authorityUrl,
|
|
57
|
+
'--no-open',
|
|
58
|
+
];
|
|
59
|
+
}
|
|
44
60
|
export function developmentSessionSummary(session) {
|
|
45
61
|
const rows = [
|
|
46
62
|
['Session', session.sessionId], ['Workspace', session.workspaceId], ['Project', session.projectId], ['Namespace', session.namespace],
|
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.9.0';
|
|
27
27
|
function prompt(question) {
|
|
28
28
|
const rl = readline.createInterface({
|
|
29
29
|
input: process.stdin,
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { createRequire } from 'node:module';
|
|
4
|
+
export const CANONICAL_STUDIO_ARTIFACT_CONTRACT = 'feltdb.studio.app.v1';
|
|
5
|
+
// Kept non-contiguous so Core assembly's package-import rewrite cannot mutate
|
|
6
|
+
// this artifact identity from its source package into an import subpath.
|
|
7
|
+
export const CANONICAL_STUDIO_ARTIFACT_SOURCE = '@feltdb/' + 'studio';
|
|
8
|
+
/** Resolve the one Studio application shipped by the installed Core package. */
|
|
9
|
+
export function resolveCanonicalStudioArtifact(options = {}) {
|
|
10
|
+
const require = createRequire(import.meta.url);
|
|
11
|
+
const resolvePackage = options.resolvePackage || (specifier => require.resolve(specifier));
|
|
12
|
+
const packageFile = resolvePackage('@feltdb/core/package.json');
|
|
13
|
+
const root = path.join(path.dirname(packageFile), 'dist', 'studio-app');
|
|
14
|
+
const index = path.join(root, 'index.html');
|
|
15
|
+
const identityFile = path.join(root, 'feltdb-studio.json');
|
|
16
|
+
if (!fs.existsSync(index) || !fs.existsSync(identityFile)) {
|
|
17
|
+
throw new Error(`The installed @feltdb/core package does not contain the canonical FeltDB Studio artifact (${root})`);
|
|
18
|
+
}
|
|
19
|
+
let identity;
|
|
20
|
+
try {
|
|
21
|
+
identity = JSON.parse(fs.readFileSync(identityFile, 'utf8'));
|
|
22
|
+
}
|
|
23
|
+
catch (error) {
|
|
24
|
+
throw new Error(`The canonical FeltDB Studio artifact identity is unreadable (${identityFile}): ${error instanceof Error ? error.message : String(error)}`);
|
|
25
|
+
}
|
|
26
|
+
if (identity.contract !== CANONICAL_STUDIO_ARTIFACT_CONTRACT
|
|
27
|
+
|| identity.source !== CANONICAL_STUDIO_ARTIFACT_SOURCE
|
|
28
|
+
|| typeof identity.version !== 'string' || !identity.version
|
|
29
|
+
|| identity.entrypoint !== 'src/main.tsx') {
|
|
30
|
+
throw new Error(`The installed Studio artifact is not the canonical @feltdb/core/studio application (${identityFile})`);
|
|
31
|
+
}
|
|
32
|
+
return { root, index, identity };
|
|
33
|
+
}
|
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
|
@@ -484,10 +484,12 @@ export class Collection {
|
|
|
484
484
|
conflictCode: casResult.conflictCode,
|
|
485
485
|
};
|
|
486
486
|
}
|
|
487
|
-
// Use the item returned by cas()
|
|
488
|
-
|
|
489
|
-
//
|
|
490
|
-
finalItem
|
|
487
|
+
// Use the item returned by cas() if available (file-db, memory-db), otherwise use local copy
|
|
488
|
+
// When cas() returns the item, it's the authoritative post-commit state from the backend.
|
|
489
|
+
// When it doesn't, we reconstruct from our local updated object and the returned version.
|
|
490
|
+
const finalItem = casResult.item
|
|
491
|
+
? casResult.item
|
|
492
|
+
: { ...updated, __version: casResult.currentVersion };
|
|
491
493
|
// Update indexes
|
|
492
494
|
this.indexBackend.updateRecord(String(id), current, finalItem);
|
|
493
495
|
// Emit change through reactive dependency graph
|
|
@@ -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.11.0';
|
|
4
4
|
export const feltdbPackageRange = `^${FELTDB_PACKAGE_VERSION}`;
|
|
@@ -1460,9 +1460,12 @@ mod convergence_acceptance_tests {
|
|
|
1460
1460
|
use crate::FeltDb;
|
|
1461
1461
|
use serde::{Deserialize, Serialize};
|
|
1462
1462
|
use std::path::PathBuf;
|
|
1463
|
+
use std::sync::atomic::{AtomicU64, Ordering};
|
|
1463
1464
|
use std::time::SystemTime;
|
|
1464
1465
|
use std::time::UNIX_EPOCH;
|
|
1465
1466
|
|
|
1467
|
+
static TEMP_FILE_SEQUENCE: AtomicU64 = AtomicU64::new(0);
|
|
1468
|
+
|
|
1466
1469
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
|
1467
1470
|
struct Task {
|
|
1468
1471
|
title: String,
|
|
@@ -1475,7 +1478,11 @@ mod convergence_acceptance_tests {
|
|
|
1475
1478
|
.duration_since(UNIX_EPOCH)
|
|
1476
1479
|
.expect("time")
|
|
1477
1480
|
.as_nanos();
|
|
1478
|
-
|
|
1481
|
+
let sequence = TEMP_FILE_SEQUENCE.fetch_add(1, Ordering::Relaxed);
|
|
1482
|
+
std::env::temp_dir().join(format!(
|
|
1483
|
+
"feltdb-convergence-test-{}-{nanos}-{sequence}.data",
|
|
1484
|
+
std::process::id()
|
|
1485
|
+
))
|
|
1479
1486
|
}
|
|
1480
1487
|
|
|
1481
1488
|
/// Test convergence: Two instances make concurrent mutations on same key
|
|
@@ -1203,6 +1203,22 @@ impl ApplicationStore {
|
|
|
1203
1203
|
records: Arc::new(RwLock::new(records)),
|
|
1204
1204
|
})
|
|
1205
1205
|
}
|
|
1206
|
+
|
|
1207
|
+
/// Return every tenant scope asserted by canonical application definition
|
|
1208
|
+
/// records for one stable application identity. Callers must reject zero
|
|
1209
|
+
/// or multiple distinct tenants; ordering is never ownership evidence.
|
|
1210
|
+
pub fn tenant_ownership_candidates(&self, application_id: &str) -> Vec<String> {
|
|
1211
|
+
let Ok(records) = self.records.read() else {
|
|
1212
|
+
return Vec::new();
|
|
1213
|
+
};
|
|
1214
|
+
records
|
|
1215
|
+
.revisions
|
|
1216
|
+
.iter()
|
|
1217
|
+
.map(|value| (&value.application_id, &value.tenant_id))
|
|
1218
|
+
.filter(|(candidate, _)| candidate.as_str() == application_id)
|
|
1219
|
+
.map(|(_, tenant)| tenant.clone())
|
|
1220
|
+
.collect()
|
|
1221
|
+
}
|
|
1206
1222
|
fn persist(&self, r: &RevisionRecords) -> Result<(), String> {
|
|
1207
1223
|
if let Some(parent) = self.path.parent() {
|
|
1208
1224
|
fs::create_dir_all(parent).map_err(|e| e.to_string())?;
|
|
@@ -744,11 +744,12 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|
|
744
744
|
link.peer.clone(),
|
|
745
745
|
envelope.envelope_id.sequence,
|
|
746
746
|
);
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
747
|
+
// Forwarding, like re-sending, is an explicit
|
|
748
|
+
// recovery operation. Prefer a fresh connection so
|
|
749
|
+
// a peer restart cannot leave this write accepted
|
|
750
|
+
// by a stale local socket and delivered nowhere.
|
|
751
|
+
let _ = connect_with_retry(link, 3).await;
|
|
752
|
+
if link.io.send(message).await.is_ok() {
|
|
752
753
|
delivered.push(link.peer.clone());
|
|
753
754
|
}
|
|
754
755
|
}
|
|
@@ -793,19 +794,16 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|
|
793
794
|
link.peer.clone(),
|
|
794
795
|
envelope.envelope_id.sequence,
|
|
795
796
|
);
|
|
796
|
-
//
|
|
797
|
-
//
|
|
798
|
-
//
|
|
799
|
-
//
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
delivered.push(link.peer.clone());
|
|
807
|
-
}
|
|
808
|
-
}
|
|
797
|
+
// Re-sending is the recovery primitive used after a
|
|
798
|
+
// peer restart. A write to the old TCP socket can
|
|
799
|
+
// still return success after the peer has exited,
|
|
800
|
+
// because that only confirms acceptance by the
|
|
801
|
+
// local kernel. Try to establish a fresh connection
|
|
802
|
+
// first. If the peer never restarted, the connect
|
|
803
|
+
// attempt leaves the established stream untouched.
|
|
804
|
+
let _ = connect_with_retry(link, 3).await;
|
|
805
|
+
if link.io.send(message).await.is_ok() {
|
|
806
|
+
delivered.push(link.peer.clone());
|
|
809
807
|
}
|
|
810
808
|
}
|
|
811
809
|
println!(
|
|
@@ -961,12 +959,13 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|
|
961
959
|
|
|
962
960
|
let mut delivered = Vec::new();
|
|
963
961
|
for link in &send_links {
|
|
962
|
+
// Acknowledgements are durable recovery evidence and are
|
|
963
|
+
// commonly rebroadcast after a peer restart. Refresh the
|
|
964
|
+
// connection first for the same reason as explicit
|
|
965
|
+
// envelope replay.
|
|
966
|
+
let _ = connect_with_retry(link, 3).await;
|
|
964
967
|
if link.io.send_acknowledgement(ack.clone()).await.is_ok() {
|
|
965
968
|
delivered.push(link.peer.clone());
|
|
966
|
-
} else if connect_with_retry(link, 3).await
|
|
967
|
-
&& link.io.send_acknowledgement(ack.clone()).await.is_ok()
|
|
968
|
-
{
|
|
969
|
-
delivered.push(link.peer.clone());
|
|
970
969
|
}
|
|
971
970
|
}
|
|
972
971
|
println!(
|