@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.
Files changed (127) hide show
  1. package/README.md +25 -0
  2. package/dist/application-development.d.ts +10 -3
  3. package/dist/application-development.d.ts.map +1 -1
  4. package/dist/application-development.js +11 -1
  5. package/dist/application-manifest.d.ts +2 -0
  6. package/dist/application-manifest.d.ts.map +1 -1
  7. package/dist/application-manifest.js +1 -1
  8. package/dist/application-understanding.d.ts +76 -0
  9. package/dist/application-understanding.d.ts.map +1 -0
  10. package/dist/application-understanding.js +282 -0
  11. package/dist/canonical-application.d.ts +1 -0
  12. package/dist/canonical-application.d.ts.map +1 -1
  13. package/dist/canonical-application.js +3 -2
  14. package/dist/cli/ai-model-config.js +3 -3
  15. package/dist/cli/cli.js +1 -1
  16. package/dist/cli/commands.js +321 -45
  17. package/dist/cli/index.js +1 -1
  18. package/dist/cli/managed-environment.js +6 -0
  19. package/dist/cli/managed-project.js +6 -0
  20. package/dist/cli/proposal-client.js +16 -3
  21. package/dist/cli/publish-engine.js +8 -5
  22. package/dist/cli/repository-evidence.js +55 -0
  23. package/dist/cli/source-sync.js +220 -61
  24. package/dist/convergence.d.ts +55 -0
  25. package/dist/convergence.d.ts.map +1 -0
  26. package/dist/convergence.js +63 -0
  27. package/dist/create/cli.js +10 -1
  28. package/dist/create/create.js +10 -12
  29. package/dist/create/managed-account.js +72 -7
  30. package/dist/create/package-versions.js +1 -1
  31. package/dist/create/server-source/crates/feltdb/src/application.rs +14 -0
  32. package/dist/create/server-source/crates/feltdb/src/lib.rs +20 -2
  33. package/dist/create/server-source/crates/feltdb-server/src/app_state.rs +12 -24
  34. package/dist/create/server-source/crates/feltdb-server/src/application_contract.rs +11 -3
  35. package/dist/create/server-source/crates/feltdb-server/src/authenticated_principal.rs +0 -1
  36. package/dist/create/server-source/crates/feltdb-server/src/certification_harness.rs +23 -22
  37. package/dist/create/server-source/crates/feltdb-server/src/connections.rs +14 -0
  38. package/dist/create/server-source/crates/feltdb-server/src/delegation_token.rs +5 -15
  39. package/dist/create/server-source/crates/feltdb-server/src/durable_operations.rs +13 -28
  40. package/dist/create/server-source/crates/feltdb-server/src/identity.rs +79 -14
  41. package/dist/create/server-source/crates/feltdb-server/src/key_management.rs +28 -7
  42. package/dist/create/server-source/crates/feltdb-server/src/lib.rs +5 -5
  43. package/dist/create/server-source/crates/feltdb-server/src/main.rs +1929 -261
  44. package/dist/create/server-source/crates/feltdb-server/src/managed_diagnostics.rs +10 -30
  45. package/dist/create/server-source/crates/feltdb-server/src/membership_policy.rs +12 -4
  46. package/dist/create/server-source/crates/feltdb-server/src/request_telemetry.rs +8 -6
  47. package/dist/create/server-source/crates/feltdb-server/src/snapshot_cursor.rs +10 -14
  48. package/dist/create/server-source/crates/feltdb-server/src/tenancy.rs +191 -22
  49. package/dist/create/server-source/crates/feltdb-server/src/tenant_policies.rs +9 -21
  50. package/dist/create/server-source/crates/feltdb-server/src/transaction_idempotency.rs +5 -3
  51. package/dist/create/server-source/crates/feltdb-server/src/transaction_recovery.rs +9 -8
  52. package/dist/create/server-source/crates/feltdb-server/tests/revision_recovery_integration_test.rs +1 -4
  53. package/dist/create/template/default-project/feltdb.flow +5 -0
  54. package/dist/create/template/default-project/src/context/AuthContext.tsx +9 -1
  55. package/dist/create/template/default-project/src/pages/SignUp.tsx +1 -1
  56. package/dist/flowspec.d.ts +1 -0
  57. package/dist/flowspec.d.ts.map +1 -1
  58. package/dist/flowspec.js +51 -12
  59. package/dist/http-client.d.ts +166 -14
  60. package/dist/http-client.d.ts.map +1 -1
  61. package/dist/http-client.js +91 -31
  62. package/dist/http-db.d.ts +3 -0
  63. package/dist/http-db.d.ts.map +1 -1
  64. package/dist/http-db.js +28 -3
  65. package/dist/index-core.d.ts +5 -0
  66. package/dist/index-core.d.ts.map +1 -1
  67. package/dist/index-core.js +5 -0
  68. package/dist/intent.d.ts +98 -0
  69. package/dist/intent.d.ts.map +1 -0
  70. package/dist/intent.js +343 -0
  71. package/dist/module-intent.d.ts +51 -0
  72. package/dist/module-intent.d.ts.map +1 -0
  73. package/dist/module-intent.js +230 -0
  74. package/dist/module.d.ts +105 -0
  75. package/dist/module.d.ts.map +1 -0
  76. package/dist/module.js +566 -0
  77. package/dist/proposal.d.ts +92 -4
  78. package/dist/proposal.d.ts.map +1 -1
  79. package/dist/proposal.js +150 -6
  80. package/dist/studio/app.d.ts +3 -1
  81. package/dist/studio/app.d.ts.map +1 -1
  82. package/dist/studio/components/AskFeltDB.d.ts +2 -1
  83. package/dist/studio/components/AskFeltDB.d.ts.map +1 -1
  84. package/dist/studio/components/ContractInspector.d.ts +7 -0
  85. package/dist/studio/components/ContractInspector.d.ts.map +1 -0
  86. package/dist/studio/components/ManagedInstancePanel.d.ts.map +1 -1
  87. package/dist/studio/components/ManagedInstancePanel.js +1 -5
  88. package/dist/studio/components/ModuleExplorer.d.ts +6 -0
  89. package/dist/studio/components/ModuleExplorer.d.ts.map +1 -0
  90. package/dist/studio/components/ProposalBrowser.d.ts +10 -1
  91. package/dist/studio/components/ProposalBrowser.d.ts.map +1 -1
  92. package/dist/studio/components/StateExplorer.d.ts +6 -7
  93. package/dist/studio/components/StateExplorer.d.ts.map +1 -1
  94. package/dist/studio/components/index.d.ts +2 -0
  95. package/dist/studio/components/index.d.ts.map +1 -1
  96. package/dist/studio/components/index.js +4 -4
  97. package/dist/studio/components-Duq2xQWt.js +2845 -0
  98. package/dist/studio/index.js +193 -147
  99. package/dist/studio/studio.css +1 -1
  100. package/dist/studio/utils/index.d.ts +2 -0
  101. package/dist/studio/utils/index.d.ts.map +1 -1
  102. package/dist/studio/utils/index.js +14 -12
  103. package/dist/studio/utils/managed-instance.d.ts +0 -4
  104. package/dist/studio/utils/managed-instance.d.ts.map +1 -1
  105. package/dist/studio/utils/managed-instance.js +0 -4
  106. package/dist/studio/utils/proposal-api.d.ts +56 -0
  107. package/dist/studio/utils/proposal-api.d.ts.map +1 -0
  108. package/dist/studio/utils/proposal-api.js +94 -0
  109. package/dist/studio/utils/service-api.d.ts +81 -0
  110. package/dist/studio/utils/service-api.d.ts.map +1 -0
  111. package/dist/studio/utils/service-api.js +51 -0
  112. package/dist/studio-app/assets/{feltdb_wasm-B-Ccgccb.js → feltdb_wasm-bIqcRzAr.js} +1 -1
  113. package/dist/studio-app/assets/feltdb_wasm_bg-BE79okwX.wasm +0 -0
  114. package/dist/studio-app/assets/{index-BRrmvaKw.css → index-Cd_NPw14.css} +1 -1
  115. package/dist/studio-app/assets/index-XGYdlElN.js +29 -0
  116. package/dist/studio-app/index.html +2 -2
  117. package/dist/wasm/feltdb_wasm_bg.wasm +0 -0
  118. package/dist/workspace/local-development-authority.d.ts +2 -0
  119. package/dist/workspace/local-development-authority.d.ts.map +1 -1
  120. package/dist/workspace/local-development-authority.js +128 -4
  121. package/package.json +1 -1
  122. package/dist/studio/components-Bm4sjhcX.js +0 -2162
  123. package/dist/studio-app/assets/dist-DA1MYSzp.js +0 -1
  124. package/dist/studio-app/assets/feltdb_wasm_bg-wH3enJIA.wasm +0 -0
  125. package/dist/studio-app/assets/index-CEIqowCh.js +0 -42
  126. package/dist/studio-app/assets/lib-B2_7fcn7.js +0 -69
  127. package/dist/studio-app/assets/worker-CARZ5g7K.js +0 -69
@@ -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
@@ -1,6 +1,11 @@
1
1
  flow_version 1
2
2
 
3
3
  app FeltDBStarter {
4
+ // Optional billing semantics. Uncomment to include the Stripe-compatible module:
5
+ // module stripe {
6
+ // provider stripe-compatible
7
+ // version 1.0.0
8
+ // }
4
9
  collection User {
5
10
  email: text
6
11
  name: text
@@ -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 => { await db.auth.signUp({ email: input.email, password: input.password, display_name: input.name }); establish(await createAccount(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
  }
@@ -38,6 +38,7 @@ export interface FlowSpec {
38
38
  triggers: FlowTrigger[];
39
39
  policies: FlowBlock[];
40
40
  schedules: FlowBlock[];
41
+ modules?: import('./module.js').FlowModuleDeclaration[];
41
42
  }
42
43
  export interface FlowDiagnostic {
43
44
  severity: 'error' | 'warning';
@@ -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;CACxB;AAED,MAAM,WAAW,cAAc;IAAG,QAAQ,EAAE,OAAO,GAAG,SAAS,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE;AACjG,MAAM,WAAW,YAAY;IAAG,KAAK,EAAE,MAAM,EAAE,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAA;CAAE;AACvF,MAAM,WAAW,sBAAsB;IAAG,IAAI,EAAE,KAAK,GAAG,QAAQ,GAAG,QAAQ,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,GAAG,oBAAoB,GAAG,aAAa,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE;AAEpK,eAAO,MAAM,oBAAoB,yLAA0L,CAAC;AAC5N,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAQ1D;AAED,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AAErE,sHAAsH;AACtH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,mBAAmB,CAY/G;AAED,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,mBAAmB,GAAG,QAAQ,CAG1E;AAqGD,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,QAAQ,CAA4C;AAEnG,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,QAAQ,GAAG,cAAc,EAAE,CAkBjE;AAGD,wBAAgB,cAAc,CAAC,IAAI,EAAE,QAAQ,GAAG,MAAM,CAUrD;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,GAAG,YAAY,CAQ5E;AAED,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,GAAG,sBAAsB,EAAE,CAsBjG;AAED,wBAAgB,aAAa,CAAC,GAAG,SAAU,GAAG,QAAQ,CAAyI"}
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_-]*$/.test(value))
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 capabilities = spec.capabilities.map(value => ({
18
- name: value.name,
19
- visibility: (value.statements.find(statement => statement.startsWith('visibility '))?.slice('visibility '.length) || 'public'),
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
- 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: index.name, collection: collection.name, fields: [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, 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' } };
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 ') && !collections.has(field.type.slice(4)))
253
- diagnostics.push({ severity: 'error', path: `${collection.name}.${field.name}`, message: `Unknown referenced collection ${field.type.slice(4)}` });
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: [] }; }
@@ -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 RemoteRecord<T> {
12
- id: string;
13
- key: string;
14
- value: T;
15
- version_ms: number;
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
- runtime(): Promise<Record<string, unknown>>;
24
- list<T>(collection: string): Promise<Array<RemoteRecord<T>>>;
25
- get<T>(collection: string, id: string | number): Promise<RemoteRecord<T>>;
26
- insert<T extends object>(collection: string, value: T, id?: string | number): Promise<RemoteRecord<T>>;
27
- update<T extends object>(collection: string, id: string | number, changes: Partial<T>): Promise<RemoteRecord<T>>;
28
- delete(collection: string, id: string | number): Promise<void>;
29
- getInfo(): {
30
- runtime: 'remote';
31
- url: string;
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,WAAW,aAAa;IAC5B,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,YAAY,CAAC,CAAC;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,CAAC,CAAC;IACT,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAS;IAC7B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;gBAEnB,OAAO,EAAE,aAAa;IAMlC,OAAO,CAAC,OAAO;YAQD,OAAO;IAgBrB,OAAO,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAI3C,IAAI,CAAC,CAAC,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC;IAI5D,GAAG,CAAC,CAAC,EAAE,UAAU,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;IAIzE,MAAM,CAAC,CAAC,SAAS,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;IAOtG,MAAM,CAAC,CAAC,SAAS,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;IAOhH,MAAM,CAAC,UAAU,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAM9D,OAAO,IAAI;QAAE,OAAO,EAAE,QAAQ,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE;CAG9C;AAED,wBAAgB,YAAY,CAAC,OAAO,EAAE,aAAa,GAAG,YAAY,CAEjE"}
1
+ {"version":3,"file":"http-client.d.ts","sourceRoot":"","sources":["../src/http-client.ts"],"names":[],"mappings":"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"}
@@ -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 fetch(`${this.url}${path}`, {
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
- const body = await response.json();
32
- if (body.error)
33
- message = body.error;
51
+ body = await response.json();
34
52
  }
35
53
  catch { /* non-JSON error */ }
36
- throw new Error(message);
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
- runtime() {
41
- return this.request('/runtime');
42
- }
43
- list(collection) {
44
- return this.request(`/collections/${encodeURIComponent(collection)}`);
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
- get(collection, id) {
47
- return this.request(`/collections/${encodeURIComponent(collection)}/${encodeURIComponent(String(id))}`);
48
- }
49
- insert(collection, value, id) {
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
- getInfo() {
67
- return { runtime: 'remote', url: this.url };
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>;