@feltdb/core 0.7.4 → 0.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +25 -0
- package/dist/application-development.d.ts +10 -3
- package/dist/application-development.d.ts.map +1 -1
- package/dist/application-development.js +11 -1
- package/dist/application-manifest.d.ts +2 -0
- package/dist/application-manifest.d.ts.map +1 -1
- package/dist/application-manifest.js +1 -1
- package/dist/application-understanding.d.ts +76 -0
- package/dist/application-understanding.d.ts.map +1 -0
- package/dist/application-understanding.js +282 -0
- package/dist/canonical-application.d.ts +1 -0
- package/dist/canonical-application.d.ts.map +1 -1
- package/dist/canonical-application.js +3 -2
- package/dist/cli/ai-model-config.js +3 -3
- package/dist/cli/cli.js +1 -1
- package/dist/cli/commands.js +321 -45
- package/dist/cli/index.js +1 -1
- package/dist/cli/managed-environment.js +6 -0
- package/dist/cli/managed-project.js +6 -0
- package/dist/cli/proposal-client.js +16 -3
- package/dist/cli/publish-engine.js +8 -5
- package/dist/cli/repository-evidence.js +55 -0
- package/dist/cli/source-sync.js +220 -61
- package/dist/convergence.d.ts +55 -0
- package/dist/convergence.d.ts.map +1 -0
- package/dist/convergence.js +63 -0
- package/dist/create/cli.js +10 -1
- package/dist/create/create.js +10 -12
- package/dist/create/managed-account.js +72 -7
- package/dist/create/package-versions.js +1 -1
- package/dist/create/server-source/crates/feltdb/src/application.rs +14 -0
- package/dist/create/server-source/crates/feltdb/src/lib.rs +20 -2
- package/dist/create/server-source/crates/feltdb-server/src/app_state.rs +12 -24
- package/dist/create/server-source/crates/feltdb-server/src/application_contract.rs +11 -3
- package/dist/create/server-source/crates/feltdb-server/src/authenticated_principal.rs +0 -1
- package/dist/create/server-source/crates/feltdb-server/src/certification_harness.rs +23 -22
- package/dist/create/server-source/crates/feltdb-server/src/connections.rs +14 -0
- package/dist/create/server-source/crates/feltdb-server/src/delegation_token.rs +5 -15
- package/dist/create/server-source/crates/feltdb-server/src/durable_operations.rs +13 -28
- package/dist/create/server-source/crates/feltdb-server/src/identity.rs +79 -14
- package/dist/create/server-source/crates/feltdb-server/src/key_management.rs +28 -7
- package/dist/create/server-source/crates/feltdb-server/src/lib.rs +5 -5
- package/dist/create/server-source/crates/feltdb-server/src/main.rs +1929 -261
- package/dist/create/server-source/crates/feltdb-server/src/managed_diagnostics.rs +10 -30
- package/dist/create/server-source/crates/feltdb-server/src/membership_policy.rs +12 -4
- package/dist/create/server-source/crates/feltdb-server/src/request_telemetry.rs +8 -6
- package/dist/create/server-source/crates/feltdb-server/src/snapshot_cursor.rs +10 -14
- package/dist/create/server-source/crates/feltdb-server/src/tenancy.rs +191 -22
- package/dist/create/server-source/crates/feltdb-server/src/tenant_policies.rs +9 -21
- package/dist/create/server-source/crates/feltdb-server/src/transaction_idempotency.rs +5 -3
- package/dist/create/server-source/crates/feltdb-server/src/transaction_recovery.rs +9 -8
- package/dist/create/server-source/crates/feltdb-server/tests/revision_recovery_integration_test.rs +1 -4
- package/dist/create/template/default-project/feltdb.flow +5 -0
- package/dist/create/template/default-project/src/context/AuthContext.tsx +9 -1
- package/dist/create/template/default-project/src/pages/SignUp.tsx +1 -1
- package/dist/flowspec.d.ts +1 -0
- package/dist/flowspec.d.ts.map +1 -1
- package/dist/flowspec.js +51 -12
- package/dist/http-client.d.ts +166 -14
- package/dist/http-client.d.ts.map +1 -1
- package/dist/http-client.js +91 -31
- package/dist/http-db.d.ts +3 -0
- package/dist/http-db.d.ts.map +1 -1
- package/dist/http-db.js +28 -3
- package/dist/index-core.d.ts +5 -0
- package/dist/index-core.d.ts.map +1 -1
- package/dist/index-core.js +5 -0
- package/dist/intent.d.ts +98 -0
- package/dist/intent.d.ts.map +1 -0
- package/dist/intent.js +343 -0
- package/dist/module-intent.d.ts +51 -0
- package/dist/module-intent.d.ts.map +1 -0
- package/dist/module-intent.js +230 -0
- package/dist/module.d.ts +105 -0
- package/dist/module.d.ts.map +1 -0
- package/dist/module.js +566 -0
- package/dist/proposal.d.ts +92 -4
- package/dist/proposal.d.ts.map +1 -1
- package/dist/proposal.js +150 -6
- package/dist/studio/app.d.ts +3 -1
- package/dist/studio/app.d.ts.map +1 -1
- package/dist/studio/components/AskFeltDB.d.ts +2 -1
- package/dist/studio/components/AskFeltDB.d.ts.map +1 -1
- package/dist/studio/components/ContractInspector.d.ts +7 -0
- package/dist/studio/components/ContractInspector.d.ts.map +1 -0
- package/dist/studio/components/ManagedInstancePanel.d.ts.map +1 -1
- package/dist/studio/components/ManagedInstancePanel.js +1 -5
- package/dist/studio/components/ModuleExplorer.d.ts +6 -0
- package/dist/studio/components/ModuleExplorer.d.ts.map +1 -0
- package/dist/studio/components/ProposalBrowser.d.ts +10 -1
- package/dist/studio/components/ProposalBrowser.d.ts.map +1 -1
- package/dist/studio/components/StateExplorer.d.ts +6 -7
- package/dist/studio/components/StateExplorer.d.ts.map +1 -1
- package/dist/studio/components/index.d.ts +2 -0
- package/dist/studio/components/index.d.ts.map +1 -1
- package/dist/studio/components/index.js +4 -4
- package/dist/studio/components-Duq2xQWt.js +2845 -0
- package/dist/studio/index.js +193 -147
- package/dist/studio/studio.css +1 -1
- package/dist/studio/utils/index.d.ts +2 -0
- package/dist/studio/utils/index.d.ts.map +1 -1
- package/dist/studio/utils/index.js +14 -12
- package/dist/studio/utils/managed-instance.d.ts +0 -4
- package/dist/studio/utils/managed-instance.d.ts.map +1 -1
- package/dist/studio/utils/managed-instance.js +0 -4
- package/dist/studio/utils/proposal-api.d.ts +56 -0
- package/dist/studio/utils/proposal-api.d.ts.map +1 -0
- package/dist/studio/utils/proposal-api.js +94 -0
- package/dist/studio/utils/service-api.d.ts +81 -0
- package/dist/studio/utils/service-api.d.ts.map +1 -0
- package/dist/studio/utils/service-api.js +51 -0
- package/dist/studio-app/assets/{feltdb_wasm-B-Ccgccb.js → feltdb_wasm-bIqcRzAr.js} +1 -1
- package/dist/studio-app/assets/feltdb_wasm_bg-BE79okwX.wasm +0 -0
- package/dist/studio-app/assets/{index-BRrmvaKw.css → index-Cd_NPw14.css} +1 -1
- package/dist/studio-app/assets/index-XGYdlElN.js +29 -0
- package/dist/studio-app/index.html +2 -2
- package/dist/wasm/feltdb_wasm_bg.wasm +0 -0
- package/dist/workspace/local-development-authority.d.ts +2 -0
- package/dist/workspace/local-development-authority.d.ts.map +1 -1
- package/dist/workspace/local-development-authority.js +128 -4
- package/package.json +1 -1
- package/dist/studio/components-Bm4sjhcX.js +0 -2162
- package/dist/studio-app/assets/dist-DA1MYSzp.js +0 -1
- package/dist/studio-app/assets/feltdb_wasm_bg-wH3enJIA.wasm +0 -0
- package/dist/studio-app/assets/index-CEIqowCh.js +0 -42
- package/dist/studio-app/assets/lib-B2_7fcn7.js +0 -69
- package/dist/studio-app/assets/worker-CARZ5g7K.js +0 -69
package/dist/create/server-source/crates/feltdb-server/tests/revision_recovery_integration_test.rs
CHANGED
|
@@ -124,10 +124,7 @@ mod revision_recovery_tests {
|
|
|
124
124
|
});
|
|
125
125
|
|
|
126
126
|
// Both requests should have the same recovery_id
|
|
127
|
-
assert_eq!(
|
|
128
|
-
request1.get("recoveryId"),
|
|
129
|
-
request2.get("recoveryId")
|
|
130
|
-
);
|
|
127
|
+
assert_eq!(request1.get("recoveryId"), request2.get("recoveryId"));
|
|
131
128
|
}
|
|
132
129
|
|
|
133
130
|
/// Test environment parameter handling
|
|
@@ -17,7 +17,15 @@ export function AuthProvider({ children }: { children: ReactNode }) {
|
|
|
17
17
|
const loading = sessionRecords.loading || userRecords.loading || membershipRecords.loading || organizationRecords.loading;
|
|
18
18
|
const establish = (next: Session) => { sessionStorage.setItem(SESSION_KEY, next.id); setSessionId(next.id); };
|
|
19
19
|
const value = useMemo<AuthValue>(() => ({ loading, authenticated: Boolean(session && user && membership && organization), session, user, membership, organization,
|
|
20
|
-
signUp: async input => {
|
|
20
|
+
signUp: async input => {
|
|
21
|
+
try {
|
|
22
|
+
await db.auth.signUp({ email: input.email, password: input.password, display_name: input.name });
|
|
23
|
+
} catch (error) {
|
|
24
|
+
if ((error as { code?: string }).code !== 'CONFLICT') throw error;
|
|
25
|
+
await db.auth.signIn({ email: input.email, password: input.password });
|
|
26
|
+
}
|
|
27
|
+
establish(await createAccount(input));
|
|
28
|
+
},
|
|
21
29
|
signIn: async (email, password) => { await db.auth.signIn({ email, password }); establish(await createSession(email)); },
|
|
22
30
|
signOut: async () => { if (session) await sessions.delete(session.id); await db.auth.signOut(); sessionStorage.removeItem(SESSION_KEY); setSessionId(null); },
|
|
23
31
|
}), [loading, session, user, membership, organization]);
|
|
@@ -3,6 +3,6 @@ import { useAuth } from '../context/AuthContext';
|
|
|
3
3
|
|
|
4
4
|
export function SignUp() {
|
|
5
5
|
const { signUp } = useAuth(); const [error, setError] = useState(''); const [busy, setBusy] = useState(false);
|
|
6
|
-
async function submit(event: FormEvent<HTMLFormElement>) { event.preventDefault(); setBusy(true); setError(''); const form = new FormData(event.currentTarget); try { await signUp({ name: String(form.get('name')), email: String(form.get('email')), password: String(form.get('password')), organization: String(form.get('organization')) }); } catch { setError('We could not create your account. Check the details and try again.'); setBusy(false); } }
|
|
6
|
+
async function submit(event: FormEvent<HTMLFormElement>) { event.preventDefault(); setBusy(true); setError(''); const form = new FormData(event.currentTarget); try { await signUp({ name: String(form.get('name')), email: String(form.get('email')), password: String(form.get('password')), organization: String(form.get('organization')) }); } catch (error) { setError(error instanceof Error ? error.message : 'We could not create your account. Check the details and try again.'); setBusy(false); } }
|
|
7
7
|
return <><span className="eyebrow">Create your workspace</span><h2>Start with durable state.</h2><p className="muted">Your actor, account, and organization are established at the FeltDB boundary.</p><form onSubmit={submit}><label>Name<input name="name" autoComplete="name" required placeholder="Ada Lovelace" /></label><label>Work email<input name="email" type="email" autoComplete="email" required placeholder="ada@example.com" /></label><label>Password<input name="password" type="password" autoComplete="new-password" minLength={8} required /></label><label>Organization<input name="organization" required placeholder="Analytical Engines" /></label>{error && <p className="error">{error}</p>}<button disabled={busy}>{busy ? 'Creating…' : 'Create account'}</button></form><p className="switch">Already have an account? <a href="?view=signin">Sign in</a></p></>;
|
|
8
8
|
}
|
package/dist/flowspec.d.ts
CHANGED
package/dist/flowspec.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"flowspec.d.ts","sourceRoot":"","sources":["../src/flowspec.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,SAAS;IAAG,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,OAAO,CAAA;CAAE;AAC5E,MAAM,WAAW,SAAS;IAAG,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE;AAC7E,MAAM,WAAW,cAAc;IAAG,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,SAAS,EAAE,CAAC;IAAC,OAAO,EAAE,SAAS,EAAE,CAAA;CAAE;AAC3F,MAAM,WAAW,SAAS;IAAG,IAAI,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,EAAE,CAAA;CAAE;AACjE,MAAM,WAAW,QAAS,SAAQ,SAAS;CAAG;AAC9C,MAAM,WAAW,YAAY;IAAG,IAAI,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,QAAQ,EAAE,CAAA;CAAE;AACrF,MAAM,WAAW,WAAW;IAAG,KAAK,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,EAAE,CAAA;CAAE;AACpE,MAAM,WAAW,QAAQ;IACvB,OAAO,EAAE,CAAC,CAAC;IACX,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW,EAAE,cAAc,EAAE,CAAC;IAC9B,YAAY,EAAE,SAAS,EAAE,CAAC;IAC1B,MAAM,EAAE,SAAS,EAAE,CAAC;IACpB,SAAS,EAAE,YAAY,EAAE,CAAC;IAC1B,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,QAAQ,EAAE,SAAS,EAAE,CAAC;IACtB,SAAS,EAAE,SAAS,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"flowspec.d.ts","sourceRoot":"","sources":["../src/flowspec.ts"],"names":[],"mappings":"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"}
|
package/dist/flowspec.js
CHANGED
|
@@ -3,7 +3,7 @@ export function isValidFlowFieldType(type) {
|
|
|
3
3
|
const value = type.trim();
|
|
4
4
|
if (FLOW_PRIMITIVE_TYPES.includes(value))
|
|
5
5
|
return true;
|
|
6
|
-
if (/^ref [A-Za-z_][A-Za-z0-9_-]
|
|
6
|
+
if (/^ref [A-Za-z_][A-Za-z0-9_-]*(?:\.[A-Za-z_][A-Za-z0-9_-]*)?$/.test(value))
|
|
7
7
|
return true;
|
|
8
8
|
if (/^enum\([^)]+\)$/.test(value))
|
|
9
9
|
return value.slice(5, -1).split(',').every(item => item.trim().length > 0);
|
|
@@ -12,23 +12,30 @@ export function isValidFlowFieldType(type) {
|
|
|
12
12
|
const container = /^(array|map)<(.+)>$/.exec(value);
|
|
13
13
|
return Boolean(container && isValidFlowFieldType(container[2]));
|
|
14
14
|
}
|
|
15
|
+
import { resolveFeltDBModules } from './module.js';
|
|
15
16
|
/** FlowSpec is a portable source format; the returned Rust manifest is authoritative only after server validation. */
|
|
16
17
|
export function flowSpecToManifest(spec, tenantId, applicationId) {
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
const modules = resolveFeltDBModules(spec.modules);
|
|
19
|
+
const capabilities = [...spec.capabilities.map(value => ({
|
|
20
|
+
name: value.name,
|
|
21
|
+
visibility: (value.statements.find(statement => statement.startsWith('visibility '))?.slice('visibility '.length) || 'public'),
|
|
22
|
+
})), ...modules.flatMap(module => module.capabilities.map(name => ({ name, provider: module.id, visibility: 'internal' })))];
|
|
21
23
|
const policySubject = (statements, operation) => statements.map(value => new RegExp(`^${operation}\\s*:\\s*(.+)$`).exec(value)?.[1]).find(Boolean);
|
|
22
24
|
const manifestField = (field) => {
|
|
23
25
|
const reference = field.type.startsWith('ref ') ? field.type.slice(4) : undefined;
|
|
24
26
|
const enumValues = field.type.startsWith('enum(') && field.type.endsWith(')') ? field.type.slice(5, -1).split(',').map(value => value.trim()) : undefined;
|
|
25
27
|
return { name: field.name, type: reference ? 'reference' : enumValues ? 'enum' : field.type, required: !field.optional, ...(reference ? { reference } : {}), ...(enumValues ? { enum_values: enumValues } : {}) };
|
|
26
28
|
};
|
|
27
|
-
|
|
29
|
+
const indexFields = (expression) => {
|
|
30
|
+
const trimmed = expression.trim();
|
|
31
|
+
const match = trimmed.match(/^(?:[A-Za-z_][A-Za-z0-9_]*)?\((.*)\)$/);
|
|
32
|
+
return (match?.[1] ?? trimmed).split(',').map(value => value.trim()).filter(Boolean);
|
|
33
|
+
};
|
|
34
|
+
return { application_id: applicationId, tenant_id: tenantId, schema: 1, metadata: { name: spec.app, labels: {} }, collections: spec.collections.map(value => ({ name: value.name, fields: value.fields.map(manifestField) })), indexes: spec.collections.flatMap(collection => collection.indexes.map(index => ({ name: `${collection.name}.${index.name}`, collection: collection.name, fields: indexFields(index.expression), unique: index.kind === 'unique' }))), references: [], queries: [], actions: [], workflows: spec.workflows.map(value => ({ name: value.name, steps: value.steps.map(step => ({ name: step.name, capability: step.statements.find(statement => statement.startsWith('capability '))?.slice('capability '.length) })) })), triggers: spec.triggers.map((value, index) => ({ name: `trigger-${index + 1}`, event: value.event, workflow: spec.workflows[0]?.name || '' })), schedules: spec.schedules.map(value => ({ name: value.name, expression: value.statements[0] || '', workflow: spec.workflows[0]?.name || '' })), agents: spec.agents.map(value => ({ name: value.name, capabilities: capabilities.map(capability => capability.name).filter(name => value.statements.some(statement => statement.includes(name))) })), capabilities, modules: resolveFeltDBModules(spec.modules), connections: [], policies: spec.policies.map(value => ({ name: value.name, resource: value.name, read: policySubject(value.statements, 'read'), write: policySubject(value.statements, 'write'), capabilities: [] })), ui: { routes: [], bindings: [], forms: [], dashboards: [] }, environments: ['development', 'preview', 'staging', 'production'].map(name => ({ name, configuration: {} })), artifacts: [], deployment: { environments: ['development', 'preview', 'staging', 'production'] }, runtime: { contract: 1, minimum_engine: '0.2.0', maximum_engine_exclusive: '2.0.0' } };
|
|
28
35
|
}
|
|
29
36
|
export function manifestToFlowSpec(manifest) {
|
|
30
37
|
const fieldType = (field) => field.reference ? `ref ${field.reference}` : field.enum_values?.length ? `enum(${field.enum_values.join(',')})` : field.type;
|
|
31
|
-
return { version: 1, app: manifest.metadata.name, collections: manifest.collections.map(value => ({ name: value.name, fields: value.fields.map(field => ({ name: field.name, type: fieldType(field), optional: !field.required })), indexes: manifest.indexes.filter(index => index.collection === value.name).map(index => ({ name: index.name, kind: index.unique ? 'unique' : 'index', expression: index.fields.join(', ') })) })), capabilities: manifest.capabilities.map(value => ({ name: value.name, statements: [...(value.provider ? [`provider ${value.provider}`] : []), `visibility ${value.visibility || 'public'}`] })), agents: manifest.agents.map(value => ({ name: value.name, statements: value.capabilities.map(capability => `capability ${capability}`) })), workflows: manifest.workflows.map(value => ({ name: value.name, parameters: '', steps: value.steps.map(step => ({ name: step.name, statements: step.capability ? [`capability ${step.capability}`] : [] })) })), triggers: manifest.triggers.map(value => ({ event: value.event, statements: [`run ${value.workflow}`] })), policies: manifest.policies.map(value => ({ name: value.name, statements: [...(value.read ? [`read: ${value.read}`] : []), ...(value.write ? [`write: ${value.write}`] : []), ...value.capabilities] })), schedules: manifest.schedules.map(value => ({ name: value.name, statements: [value.expression, `run ${value.workflow}`] })) };
|
|
38
|
+
return { version: 1, app: manifest.metadata.name, collections: manifest.collections.map(value => ({ name: value.name, fields: value.fields.map(field => ({ name: field.name, type: fieldType(field), optional: !field.required })), indexes: manifest.indexes.filter(index => index.collection === value.name).map(index => ({ name: index.name, kind: index.unique ? 'unique' : 'index', expression: index.fields.join(', ') })) })), capabilities: manifest.capabilities.map(value => ({ name: value.name, statements: [...(value.provider ? [`provider ${value.provider}`] : []), `visibility ${value.visibility || 'public'}`] })), agents: manifest.agents.map(value => ({ name: value.name, statements: value.capabilities.map(capability => `capability ${capability}`) })), workflows: manifest.workflows.map(value => ({ name: value.name, parameters: '', steps: value.steps.map(step => ({ name: step.name, statements: step.capability ? [`capability ${step.capability}`] : [] })) })), triggers: manifest.triggers.map(value => ({ event: value.event, statements: [`run ${value.workflow}`] })), policies: manifest.policies.map(value => ({ name: value.name, statements: [...(value.read ? [`read: ${value.read}`] : []), ...(value.write ? [`write: ${value.write}`] : []), ...value.capabilities] })), schedules: manifest.schedules.map(value => ({ name: value.name, statements: [value.expression, `run ${value.workflow}`] })), modules: (manifest.modules ?? []).map(value => ({ name: value.id, provider: value.provider, version: value.version })) };
|
|
32
39
|
}
|
|
33
40
|
function lex(source) {
|
|
34
41
|
const tokens = [];
|
|
@@ -135,11 +142,20 @@ class Parser {
|
|
|
135
142
|
const app = this.identifier();
|
|
136
143
|
this.take('{');
|
|
137
144
|
this.newlines();
|
|
138
|
-
const spec = { version, app, collections: [], capabilities: [], agents: [], workflows: [], triggers: [], policies: [], schedules: [] };
|
|
145
|
+
const spec = { version, app, collections: [], capabilities: [], agents: [], workflows: [], triggers: [], policies: [], schedules: [], modules: [] };
|
|
139
146
|
while (!this.peek('}')) {
|
|
140
147
|
const kind = this.take().value;
|
|
141
148
|
if (kind === 'collection')
|
|
142
149
|
spec.collections.push(this.collection());
|
|
150
|
+
else if (kind === 'module') {
|
|
151
|
+
const name = this.identifier();
|
|
152
|
+
const statements = this.statements();
|
|
153
|
+
const provider = statements.find(value => value.startsWith('provider '))?.slice(9);
|
|
154
|
+
const version = statements.find(value => value.startsWith('version '))?.slice(8);
|
|
155
|
+
if (!provider)
|
|
156
|
+
throw new Error(`Module ${name} requires provider`);
|
|
157
|
+
spec.modules.push({ name, provider: provider.replace(/^"|"$/g, ''), version: version?.replace(/^"|"$/g, '') });
|
|
158
|
+
}
|
|
143
159
|
else if (kind === 'workflow')
|
|
144
160
|
spec.workflows.push(this.workflow());
|
|
145
161
|
else if (kind === 'trigger')
|
|
@@ -238,6 +254,14 @@ export function validateFlowSpec(spec) {
|
|
|
238
254
|
duplicate('capability', spec.capabilities.map(value => value.name));
|
|
239
255
|
duplicate('agent', spec.agents.map(value => value.name));
|
|
240
256
|
duplicate('workflow', spec.workflows.map(value => value.name));
|
|
257
|
+
duplicate('module', (spec.modules ?? []).map(value => value.name));
|
|
258
|
+
let resolvedModules = [];
|
|
259
|
+
try {
|
|
260
|
+
resolvedModules = resolveFeltDBModules(spec.modules);
|
|
261
|
+
}
|
|
262
|
+
catch (error) {
|
|
263
|
+
diagnostics.push({ severity: 'error', path: 'modules', message: error instanceof Error ? error.message : String(error) });
|
|
264
|
+
}
|
|
241
265
|
for (const capability of spec.capabilities) {
|
|
242
266
|
const visibility = capability.statements.find(statement => statement.startsWith('visibility '))?.slice('visibility '.length);
|
|
243
267
|
if (visibility && !['public', 'agent', 'workflow', 'internal'].includes(visibility))
|
|
@@ -249,8 +273,13 @@ export function validateFlowSpec(spec) {
|
|
|
249
273
|
for (const field of collection.fields) {
|
|
250
274
|
if (!isValidFlowFieldType(field.type))
|
|
251
275
|
diagnostics.push({ severity: 'error', path: `${collection.name}.${field.name}`, message: `Unsupported field type ${field.type}` });
|
|
252
|
-
if (field.type.startsWith('ref ')
|
|
253
|
-
|
|
276
|
+
if (field.type.startsWith('ref ')) {
|
|
277
|
+
const target = field.type.slice(4);
|
|
278
|
+
const [moduleId, model] = target.split('.');
|
|
279
|
+
const moduleReference = model && resolvedModules.some(module => module.id === moduleId && module.models.some(value => value.name === model));
|
|
280
|
+
if (!collections.has(target) && !moduleReference)
|
|
281
|
+
diagnostics.push({ severity: 'error', path: `${collection.name}.${field.name}`, message: `Unknown referenced collection or module model ${target}` });
|
|
282
|
+
}
|
|
254
283
|
}
|
|
255
284
|
}
|
|
256
285
|
for (const workflow of spec.workflows) {
|
|
@@ -258,6 +287,14 @@ export function validateFlowSpec(spec) {
|
|
|
258
287
|
if (!workflow.steps.length)
|
|
259
288
|
diagnostics.push({ severity: 'error', message: `Workflow ${workflow.name} has no steps` });
|
|
260
289
|
}
|
|
290
|
+
for (const trigger of spec.triggers) {
|
|
291
|
+
const moduleId = trigger.event.split('.')[0];
|
|
292
|
+
const module = resolvedModules.find(value => value.id === moduleId);
|
|
293
|
+
if (moduleId && trigger.event.split('.').length >= 3 && !module)
|
|
294
|
+
diagnostics.push({ severity: 'error', path: `trigger ${trigger.event}`, message: `Module event ${trigger.event} requires module ${moduleId}` });
|
|
295
|
+
else if (module && !module.events.includes(trigger.event))
|
|
296
|
+
diagnostics.push({ severity: 'error', path: `trigger ${trigger.event}`, message: `Unknown ${moduleId} module event ${trigger.event}` });
|
|
297
|
+
}
|
|
261
298
|
return diagnostics;
|
|
262
299
|
}
|
|
263
300
|
function block(kind, value, indent = ' ') { return `${indent}${kind} ${value.name} {\n${value.statements.map(line => `${indent} ${line}`).join('\n')}\n${indent}}`; }
|
|
@@ -277,13 +314,15 @@ export function formatFlowSpec(spec) {
|
|
|
277
314
|
declarations.push(block('policy', value));
|
|
278
315
|
for (const value of spec.schedules)
|
|
279
316
|
declarations.push(block('schedule', value));
|
|
317
|
+
for (const value of spec.modules ?? [])
|
|
318
|
+
declarations.push(` module ${value.name} {\n provider ${value.provider}\n${value.version ? ` version ${value.version}\n` : ''} }`);
|
|
280
319
|
return `flow_version ${spec.version}\n\napp ${spec.app} {\n${declarations.join('\n\n')}\n}\n`;
|
|
281
320
|
}
|
|
282
321
|
export function diffFlowSpec(before, after) {
|
|
283
322
|
const flatten = (spec) => new Map([
|
|
284
323
|
...spec.collections.map(value => [`collection ${value.name}`, JSON.stringify(value)]), ...spec.capabilities.map(value => [`capability ${value.name}`, JSON.stringify(value)]),
|
|
285
324
|
...spec.agents.map(value => [`agent ${value.name}`, JSON.stringify(value)]), ...spec.workflows.map(value => [`workflow ${value.name}`, JSON.stringify(value)]),
|
|
286
|
-
...spec.triggers.map(value => [`trigger ${value.event}`, JSON.stringify(value)]), ...spec.policies.map(value => [`policy ${value.name}`, JSON.stringify(value)]), ...spec.schedules.map(value => [`schedule ${value.name}`, JSON.stringify(value)]),
|
|
325
|
+
...spec.triggers.map(value => [`trigger ${value.event}`, JSON.stringify(value)]), ...spec.policies.map(value => [`policy ${value.name}`, JSON.stringify(value)]), ...spec.schedules.map(value => [`schedule ${value.name}`, JSON.stringify(value)]), ...(spec.modules ?? []).map(value => [`module ${value.name}`, JSON.stringify(value)]),
|
|
287
326
|
]);
|
|
288
327
|
const left = flatten(before), right = flatten(after);
|
|
289
328
|
return { added: [...right.keys()].filter(key => !left.has(key)), removed: [...left.keys()].filter(key => !right.has(key)), changed: [...right.keys()].filter(key => left.has(key) && left.get(key) !== right.get(key)) };
|
|
@@ -323,4 +362,4 @@ export function planFlowSpecMigration(before, after) {
|
|
|
323
362
|
operations.push({ kind: 'remove', target, safety: 'destructive', detail: 'Application primitive is no longer declared' });
|
|
324
363
|
return operations;
|
|
325
364
|
}
|
|
326
|
-
export function emptyFlowSpec(app = 'MyApp') { return { version: 1, app, collections: [], capabilities: [], agents: [], workflows: [], triggers: [], policies: [], schedules: [] }; }
|
|
365
|
+
export function emptyFlowSpec(app = 'MyApp') { return { version: 1, app, collections: [], capabilities: [], agents: [], workflows: [], triggers: [], policies: [], schedules: [], modules: [] }; }
|
package/dist/http-client.d.ts
CHANGED
|
@@ -4,31 +4,183 @@
|
|
|
4
4
|
* Provides a simple interface for connecting to and querying remote FeltDB instances.
|
|
5
5
|
* For applications that only need HTTP access without the full core framework.
|
|
6
6
|
*/
|
|
7
|
+
import type { FeltDBProposalEvent, FeltDBProposalPreviewArtifact, FeltDBProposalRecord, ProposalReadiness, ProposalStatus } from './proposal.js';
|
|
8
|
+
import type { ApplicationUnderstanding } from './application-understanding.js';
|
|
9
|
+
import type { ConvergenceResult } from './convergence.js';
|
|
10
|
+
import type { ImplementationPlan, Intent, IntentEvent, IntentExecutionResult } from './intent.js';
|
|
11
|
+
import type { CanonicalQuery, QueryResult, TransactionResult } from './state-contract.js';
|
|
7
12
|
export interface ClientOptions {
|
|
8
13
|
url: string;
|
|
9
14
|
token: string;
|
|
15
|
+
applicationId?: string;
|
|
16
|
+
environment?: string;
|
|
17
|
+
revisionId?: string;
|
|
18
|
+
fetch?: typeof globalThis.fetch;
|
|
10
19
|
}
|
|
11
|
-
export interface
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
20
|
+
export interface ServiceApiErrorBody {
|
|
21
|
+
code?: string;
|
|
22
|
+
error?: string;
|
|
23
|
+
message?: string;
|
|
24
|
+
request_id?: string;
|
|
25
|
+
}
|
|
26
|
+
export declare class FeltDBServiceApiError extends Error {
|
|
27
|
+
readonly code: string;
|
|
28
|
+
readonly status: number;
|
|
29
|
+
readonly requestId?: string | undefined;
|
|
30
|
+
constructor(code: string, message: string, status: number, requestId?: string | undefined);
|
|
31
|
+
}
|
|
32
|
+
export interface ApplicationInspection {
|
|
33
|
+
application_id: string;
|
|
34
|
+
application_name?: string;
|
|
35
|
+
environment: string;
|
|
36
|
+
version: number;
|
|
37
|
+
revision_id: string;
|
|
38
|
+
contract_hash: string;
|
|
39
|
+
flow_hash?: string;
|
|
40
|
+
snapshot: Record<string, unknown>;
|
|
41
|
+
}
|
|
42
|
+
export interface ApplicationSchemaInspection {
|
|
43
|
+
application: {
|
|
44
|
+
application_id: string;
|
|
45
|
+
environment: string;
|
|
46
|
+
};
|
|
47
|
+
contract_version: number;
|
|
48
|
+
collections: Array<{
|
|
49
|
+
collection: string;
|
|
50
|
+
fields: Array<{
|
|
51
|
+
name: string;
|
|
52
|
+
type: string;
|
|
53
|
+
required?: boolean;
|
|
54
|
+
reference?: string;
|
|
55
|
+
}>;
|
|
56
|
+
relationships: unknown[];
|
|
57
|
+
}>;
|
|
58
|
+
}
|
|
59
|
+
export interface InspectionPage<T> {
|
|
60
|
+
collection: string;
|
|
61
|
+
records: T[];
|
|
62
|
+
pagination: {
|
|
63
|
+
limit: number;
|
|
64
|
+
nextCursor: string | null;
|
|
65
|
+
};
|
|
66
|
+
state_version: number;
|
|
67
|
+
}
|
|
68
|
+
export interface InspectionRecord<T> {
|
|
69
|
+
collection: string;
|
|
70
|
+
record: T;
|
|
71
|
+
relationships: unknown[];
|
|
16
72
|
}
|
|
17
73
|
export declare class FeltDBClient {
|
|
18
74
|
private readonly url;
|
|
19
75
|
private readonly token;
|
|
76
|
+
private readonly applicationId?;
|
|
77
|
+
private readonly environment;
|
|
78
|
+
private readonly fetcher;
|
|
79
|
+
private revisionId?;
|
|
20
80
|
constructor(options: ClientOptions);
|
|
21
81
|
private headers;
|
|
22
82
|
private request;
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
83
|
+
private scoped;
|
|
84
|
+
private scopeBody;
|
|
85
|
+
private activeRevision;
|
|
86
|
+
/** Canonical application Contract Snapshot and schema inspection. */
|
|
87
|
+
readonly application: {
|
|
88
|
+
get: () => Promise<ApplicationInspection>;
|
|
89
|
+
schema: () => Promise<ApplicationSchemaInspection>;
|
|
90
|
+
};
|
|
91
|
+
/** Bounded, authorized application-state inspection. Cursors remain opaque. */
|
|
92
|
+
readonly data: {
|
|
93
|
+
list: <T = Record<string, unknown>>(collection: string, options?: {
|
|
94
|
+
limit?: number;
|
|
95
|
+
cursor?: string;
|
|
96
|
+
filters?: Record<string, string>;
|
|
97
|
+
}) => Promise<InspectionPage<T>>;
|
|
98
|
+
get: <T = Record<string, unknown>>(collection: string, id: string) => Promise<InspectionRecord<T>>;
|
|
99
|
+
};
|
|
100
|
+
/** Canonical application queries and atomic transactions against the active revision. */
|
|
101
|
+
readonly state: {
|
|
102
|
+
query: <T = Record<string, unknown>>(query: CanonicalQuery) => Promise<QueryResult<T>>;
|
|
103
|
+
transaction: (transaction: Record<string, unknown>) => Promise<TransactionResult>;
|
|
104
|
+
version: () => Promise<{
|
|
105
|
+
state_version: number;
|
|
106
|
+
}>;
|
|
107
|
+
};
|
|
108
|
+
/** Public contract-declared application capability invocation. */
|
|
109
|
+
readonly capabilities: {
|
|
110
|
+
run: (name: string, input: unknown) => Promise<unknown>;
|
|
111
|
+
};
|
|
112
|
+
/** Canonical intent execution and its durable semantic artifacts. */
|
|
113
|
+
readonly intents: {
|
|
114
|
+
execute: (rawRequest: string) => Promise<IntentExecutionResult>;
|
|
115
|
+
list: () => Promise<{
|
|
116
|
+
intents: Intent[];
|
|
117
|
+
}>;
|
|
118
|
+
get: (id: string) => Promise<Intent>;
|
|
119
|
+
understanding: (id: string) => Promise<ApplicationUnderstanding>;
|
|
120
|
+
plan: (id: string) => Promise<ImplementationPlan>;
|
|
121
|
+
proposal: (id: string) => Promise<FeltDBProposalRecord>;
|
|
122
|
+
history: (id: string) => Promise<{
|
|
123
|
+
events: IntentEvent[];
|
|
124
|
+
}>;
|
|
125
|
+
};
|
|
126
|
+
/** Canonical Proposal lifecycle. Repository application is intentionally absent. */
|
|
127
|
+
readonly proposals: {
|
|
128
|
+
create: (proposal: {
|
|
129
|
+
base_contract_hash: string;
|
|
130
|
+
base_flow_hash: string;
|
|
131
|
+
proposed_flow: string;
|
|
132
|
+
summary: string;
|
|
133
|
+
warnings?: string[];
|
|
134
|
+
metadata?: Record<string, string>;
|
|
135
|
+
parent_proposal_id?: string;
|
|
136
|
+
expires_at?: string | number;
|
|
137
|
+
contract_diff?: string[];
|
|
138
|
+
source_plan?: {
|
|
139
|
+
files: Array<{
|
|
140
|
+
path: string;
|
|
141
|
+
action: string;
|
|
142
|
+
}>;
|
|
143
|
+
};
|
|
144
|
+
understanding?: ApplicationUnderstanding;
|
|
145
|
+
evidence_hash?: string;
|
|
146
|
+
understanding_hash?: string;
|
|
147
|
+
}) => Promise<FeltDBProposalRecord>;
|
|
148
|
+
list: (options?: {
|
|
149
|
+
status?: ProposalStatus;
|
|
150
|
+
limit?: number;
|
|
151
|
+
cursor?: string;
|
|
152
|
+
}) => Promise<{
|
|
153
|
+
proposals: FeltDBProposalRecord[];
|
|
154
|
+
pagination: {
|
|
155
|
+
limit: number;
|
|
156
|
+
nextCursor: string | null;
|
|
157
|
+
};
|
|
158
|
+
}>;
|
|
159
|
+
get: (id: string) => Promise<FeltDBProposalRecord>;
|
|
160
|
+
history: (id: string) => Promise<{
|
|
161
|
+
events: FeltDBProposalEvent[];
|
|
162
|
+
}>;
|
|
163
|
+
status: (id: string, currentEvidenceHash?: string) => Promise<ProposalReadiness>;
|
|
164
|
+
validate: (id: string, proposedManifest?: unknown) => Promise<FeltDBProposalRecord>;
|
|
165
|
+
createPreview: (id: string) => Promise<FeltDBProposalPreviewArtifact>;
|
|
166
|
+
preview: (previewId: string) => Promise<FeltDBProposalPreviewArtifact>;
|
|
167
|
+
previewData: <T = Record<string, unknown>>(previewId: string, collection: string, options?: {
|
|
168
|
+
limit?: number;
|
|
169
|
+
cursor?: string;
|
|
170
|
+
}) => Promise<InspectionPage<T>>;
|
|
171
|
+
simulatePreview: (previewId: string, event: string) => Promise<{
|
|
172
|
+
preview_id: string;
|
|
173
|
+
event: string;
|
|
174
|
+
status: "simulated";
|
|
175
|
+
external_side_effects: "none";
|
|
176
|
+
trace: string[];
|
|
177
|
+
}>;
|
|
178
|
+
approve: (id: string, approveAuthorization?: boolean) => Promise<FeltDBProposalRecord>;
|
|
179
|
+
reject: (id: string, reason?: string) => Promise<FeltDBProposalRecord>;
|
|
180
|
+
convergence: (id: string) => Promise<{
|
|
181
|
+
convergence: ConvergenceResult[];
|
|
182
|
+
}>;
|
|
183
|
+
recordConvergence: (id: string, result: ConvergenceResult) => Promise<ConvergenceResult>;
|
|
32
184
|
};
|
|
33
185
|
}
|
|
34
186
|
export declare function createClient(options: ClientOptions): FeltDBClient;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"http-client.d.ts","sourceRoot":"","sources":["../src/http-client.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,
|
|
1
|
+
{"version":3,"file":"http-client.d.ts","sourceRoot":"","sources":["../src/http-client.ts"],"names":[],"mappings":"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"}
|
package/dist/http-client.js
CHANGED
|
@@ -4,14 +4,33 @@
|
|
|
4
4
|
* Provides a simple interface for connecting to and querying remote FeltDB instances.
|
|
5
5
|
* For applications that only need HTTP access without the full core framework.
|
|
6
6
|
*/
|
|
7
|
+
export class FeltDBServiceApiError extends Error {
|
|
8
|
+
code;
|
|
9
|
+
status;
|
|
10
|
+
requestId;
|
|
11
|
+
constructor(code, message, status, requestId) {
|
|
12
|
+
super(message);
|
|
13
|
+
this.code = code;
|
|
14
|
+
this.status = status;
|
|
15
|
+
this.requestId = requestId;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
7
18
|
export class FeltDBClient {
|
|
8
19
|
url;
|
|
9
20
|
token;
|
|
21
|
+
applicationId;
|
|
22
|
+
environment;
|
|
23
|
+
fetcher;
|
|
24
|
+
revisionId;
|
|
10
25
|
constructor(options) {
|
|
11
26
|
if (!options.url || !options.token)
|
|
12
27
|
throw new Error('FeltDBClient requires url and token');
|
|
13
28
|
this.url = options.url.replace(/\/$/, '');
|
|
14
29
|
this.token = options.token;
|
|
30
|
+
this.applicationId = options.applicationId;
|
|
31
|
+
this.environment = options.environment || 'production';
|
|
32
|
+
this.fetcher = options.fetch ?? globalThis.fetch.bind(globalThis);
|
|
33
|
+
this.revisionId = options.revisionId;
|
|
15
34
|
}
|
|
16
35
|
headers(json = false) {
|
|
17
36
|
return {
|
|
@@ -21,51 +40,92 @@ export class FeltDBClient {
|
|
|
21
40
|
};
|
|
22
41
|
}
|
|
23
42
|
async request(path, init = {}) {
|
|
24
|
-
const response = await
|
|
43
|
+
const response = await this.fetcher(`${this.url}${path}`, {
|
|
25
44
|
...init,
|
|
26
45
|
headers: { ...this.headers(init.body !== undefined), ...init.headers },
|
|
27
46
|
});
|
|
28
47
|
if (!response.ok) {
|
|
29
48
|
let message = `FeltDB request failed: HTTP ${response.status}`;
|
|
49
|
+
let body = {};
|
|
30
50
|
try {
|
|
31
|
-
|
|
32
|
-
if (body.error)
|
|
33
|
-
message = body.error;
|
|
51
|
+
body = await response.json();
|
|
34
52
|
}
|
|
35
53
|
catch { /* non-JSON error */ }
|
|
36
|
-
|
|
54
|
+
if (body.message || body.error)
|
|
55
|
+
message = body.message || body.error || message;
|
|
56
|
+
throw new FeltDBServiceApiError(body.code || body.error || 'REQUEST_FAILED', message, response.status, body.request_id);
|
|
37
57
|
}
|
|
38
58
|
return (response.status === 204 ? undefined : response.json());
|
|
39
59
|
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
60
|
+
scoped(path, query = {}) {
|
|
61
|
+
if (!this.applicationId)
|
|
62
|
+
throw new Error('This Service API operation requires createClient({ applicationId })');
|
|
63
|
+
const separator = path.includes('?') ? '&' : '?';
|
|
64
|
+
const parameters = new URLSearchParams({ application_id: this.applicationId, environment: this.environment });
|
|
65
|
+
for (const [name, value] of Object.entries(query))
|
|
66
|
+
if (value !== undefined && value !== '')
|
|
67
|
+
parameters.set(name, String(value));
|
|
68
|
+
return `${path}${separator}${parameters}`;
|
|
45
69
|
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
return this.request(`/collections/${encodeURIComponent(collection)}`, {
|
|
51
|
-
method: 'POST',
|
|
52
|
-
body: JSON.stringify(id === undefined ? value : { ...value, id }),
|
|
53
|
-
});
|
|
54
|
-
}
|
|
55
|
-
update(collection, id, changes) {
|
|
56
|
-
return this.request(`/collections/${encodeURIComponent(collection)}/${encodeURIComponent(String(id))}`, {
|
|
57
|
-
method: 'PATCH',
|
|
58
|
-
body: JSON.stringify(changes),
|
|
59
|
-
});
|
|
60
|
-
}
|
|
61
|
-
delete(collection, id) {
|
|
62
|
-
return this.request(`/collections/${encodeURIComponent(collection)}/${encodeURIComponent(String(id))}`, {
|
|
63
|
-
method: 'DELETE',
|
|
64
|
-
});
|
|
70
|
+
scopeBody(value = {}) {
|
|
71
|
+
if (!this.applicationId)
|
|
72
|
+
throw new Error('This Service API operation requires createClient({ applicationId })');
|
|
73
|
+
return { application_id: this.applicationId, environment: this.environment, ...value };
|
|
65
74
|
}
|
|
66
|
-
|
|
67
|
-
|
|
75
|
+
async activeRevision() {
|
|
76
|
+
if (this.revisionId)
|
|
77
|
+
return this.revisionId;
|
|
78
|
+
const application = await this.application.get();
|
|
79
|
+
this.revisionId = application.revision_id;
|
|
80
|
+
return application.revision_id;
|
|
68
81
|
}
|
|
82
|
+
/** Canonical application Contract Snapshot and schema inspection. */
|
|
83
|
+
application = {
|
|
84
|
+
get: () => this.request(this.scoped('/v1/application')),
|
|
85
|
+
schema: () => this.request(this.scoped('/v1/application/schema')),
|
|
86
|
+
};
|
|
87
|
+
/** Bounded, authorized application-state inspection. Cursors remain opaque. */
|
|
88
|
+
data = {
|
|
89
|
+
list: (collection, options = {}) => this.request(this.scoped(`/v1/data/${encodeURIComponent(collection)}`, { limit: options.limit, cursor: options.cursor, ...options.filters })),
|
|
90
|
+
get: (collection, id) => this.request(this.scoped(`/v1/data/${encodeURIComponent(collection)}/${encodeURIComponent(id)}`)),
|
|
91
|
+
};
|
|
92
|
+
/** Canonical application queries and atomic transactions against the active revision. */
|
|
93
|
+
state = {
|
|
94
|
+
query: async (query) => this.request('/v1/query', { method: 'POST', body: JSON.stringify({ ...this.scopeBody(), revision_id: await this.activeRevision(), query }) }),
|
|
95
|
+
transaction: async (transaction) => this.request('/v1/transactions', { method: 'POST', body: JSON.stringify({ ...this.scopeBody(), revision_id: await this.activeRevision(), transaction }) }),
|
|
96
|
+
version: async () => this.request(this.scoped('/v1/state/version', { revision_id: await this.activeRevision() })),
|
|
97
|
+
};
|
|
98
|
+
/** Public contract-declared application capability invocation. */
|
|
99
|
+
capabilities = {
|
|
100
|
+
run: (name, input) => this.request(this.scoped(`/v1/capabilities/${encodeURIComponent(name)}/run`), { method: 'POST', body: JSON.stringify(input) }),
|
|
101
|
+
};
|
|
102
|
+
/** Canonical intent execution and its durable semantic artifacts. */
|
|
103
|
+
intents = {
|
|
104
|
+
execute: (rawRequest) => this.request('/v1/intents', { method: 'POST', body: JSON.stringify(this.scopeBody({ raw_request: rawRequest })) }),
|
|
105
|
+
list: () => this.request(this.scoped('/v1/intents')),
|
|
106
|
+
get: (id) => this.request(this.scoped(`/v1/intents/${encodeURIComponent(id)}`)),
|
|
107
|
+
understanding: (id) => this.request(this.scoped(`/v1/intents/${encodeURIComponent(id)}/understanding`)),
|
|
108
|
+
plan: (id) => this.request(this.scoped(`/v1/intents/${encodeURIComponent(id)}/plan`)),
|
|
109
|
+
proposal: (id) => this.request(this.scoped(`/v1/intents/${encodeURIComponent(id)}/proposal`)),
|
|
110
|
+
history: (id) => this.request(this.scoped(`/v1/intents/${encodeURIComponent(id)}/history`)),
|
|
111
|
+
};
|
|
112
|
+
/** Canonical Proposal lifecycle. Repository application is intentionally absent. */
|
|
113
|
+
proposals = {
|
|
114
|
+
create: (proposal) => this.request('/v1/proposals', { method: 'POST', body: JSON.stringify(this.scopeBody({ proposal_version: 1, contract_diff: [], source_plan: { files: [] }, ...proposal })) }),
|
|
115
|
+
list: (options = {}) => this.request(this.scoped('/v1/proposals', options)),
|
|
116
|
+
get: (id) => this.request(this.scoped(`/v1/proposals/${encodeURIComponent(id)}`)),
|
|
117
|
+
history: (id) => this.request(this.scoped(`/v1/proposals/${encodeURIComponent(id)}/history`)),
|
|
118
|
+
status: (id, currentEvidenceHash) => this.request(this.scoped(`/v1/proposals/${encodeURIComponent(id)}/status`, { current_evidence_hash: currentEvidenceHash })),
|
|
119
|
+
validate: (id, proposedManifest) => this.request(`/v1/proposals/${encodeURIComponent(id)}/validate`, { method: 'POST', body: JSON.stringify(this.scopeBody({ proposed_manifest: proposedManifest })) }),
|
|
120
|
+
createPreview: (id) => this.request(`/v1/proposals/${encodeURIComponent(id)}/preview`, { method: 'POST', body: JSON.stringify(this.scopeBody()) }),
|
|
121
|
+
preview: (previewId) => this.request(this.scoped(`/v1/previews/${encodeURIComponent(previewId)}`)),
|
|
122
|
+
previewData: (previewId, collection, options = {}) => this.request(this.scoped(`/v1/previews/${encodeURIComponent(previewId)}/data/${encodeURIComponent(collection)}`, options)),
|
|
123
|
+
simulatePreview: (previewId, event) => this.request(this.scoped(`/v1/previews/${encodeURIComponent(previewId)}/simulate`), { method: 'POST', body: JSON.stringify({ event }) }),
|
|
124
|
+
approve: (id, approveAuthorization = false) => this.request(`/v1/proposals/${encodeURIComponent(id)}/approve`, { method: 'POST', body: JSON.stringify(this.scopeBody({ approve_authorization: approveAuthorization })) }),
|
|
125
|
+
reject: (id, reason) => this.request(`/v1/proposals/${encodeURIComponent(id)}/reject`, { method: 'POST', body: JSON.stringify(this.scopeBody({ reason })) }),
|
|
126
|
+
convergence: (id) => this.request(this.scoped(`/v1/proposals/${encodeURIComponent(id)}/convergence`)),
|
|
127
|
+
recordConvergence: (id, result) => this.request(`/v1/proposals/${encodeURIComponent(id)}/convergence`, { method: 'POST', body: JSON.stringify(this.scopeBody(result)) }),
|
|
128
|
+
};
|
|
69
129
|
}
|
|
70
130
|
export function createClient(options) {
|
|
71
131
|
return new FeltDBClient(options);
|
package/dist/http-db.d.ts
CHANGED
|
@@ -41,8 +41,11 @@ export declare class HttpJsDb implements JsDb {
|
|
|
41
41
|
private readonly applicationId?;
|
|
42
42
|
private readonly environment;
|
|
43
43
|
private activeRevision?;
|
|
44
|
+
private readonly authWaiters;
|
|
44
45
|
constructor(options: HttpRuntimeOptions);
|
|
45
46
|
private headers;
|
|
47
|
+
private updateToken;
|
|
48
|
+
private waitForAuthChange;
|
|
46
49
|
auth_request<T extends {
|
|
47
50
|
access_token?: string;
|
|
48
51
|
}>(operation: 'signup' | 'signin' | 'signout' | 'session', body?: unknown): Promise<T>;
|