Docyrus

DSQL Editor

A two-pane workbench for writing and running DSQL (security-scoped SQL) over Docyrus data sources, with a result grid and an embedded AI agent.

Client Only

DSQL (Docyrus Structured Query Language) is a security-scoped SQL surface restricted to the tenant's data sources. The DsqlEditor is a two-pane workbench: a top DSQL Query editor (SQL syntax highlighting, Format + Run) and a bottom Result panel that renders the returned rows in a paginated data grid. It carries the same embedded AI agent panel as the JSONata / Handlebars / HTML Template editors.

Queries reference tables as appSlug.dataSourceSlug (e.g. base.contact), not raw schemas — the backend rewrites them. Statements are SELECT-only / read-only and run through PUT /v1/dsql/query.

This is a Docyrus-connection component. It requires an authenticated RestApiClient to run queries, so the preview above is informational only — wire it against your tenant (or the playground) to see it live.

Installation

pnpm dlx @docyrus/cli add @docyrus/ui-dsql-editor
Required Packages(3 packages)
pnpm add sql-formatter @uiw/react-codemirror @uiw/codemirror-extensions-langs

Usage

import { useState } from 'react';
import { useDocyrusAuth } from '@docyrus/signin';
import { DsqlEditor, useApplyDsql } from '@docyrus/ui/components/dsql-editor';
import { EditorAgentDrawer } from '@/components/editor-agent-drawer';

const DSQL_AGENT_ID = '019ecf41-2c43-7b0e-8d2d-71f218952e14';

function MyDsqlWorkbench() {
  const { client } = useDocyrusAuth();
  const dsql = useApplyDsql('select id, email from base.contact limit 100');
  const [aiOpen, setAiOpen] = useState(false);

  if (!client) return null;

  return (
    <DsqlEditor
      client={client}
      query={dsql.query}
      onQueryChange={dsql.setQuery}
      height="100%"
      aiAssistantOpen={aiOpen}
      onAiAssistantOpenChange={setAiOpen}
      renderAiAssistant={({ open, onClose, query }) => (
        <EditorAgentDrawer
          agentId={DSQL_AGENT_ID}
          open={open}
          onClose={onClose}
          clientTools={dsql.tools}
          editorContext={() => `Current DSQL query:\n${query || '(empty)'}`}
        />
      )}
    />
  );
}

For tests or mock data, supply onRun instead of client:

<DsqlEditor onRun={async (query) => ({
  rows: [{ id: 1, email: 'a@b.com' }],
  columns: [{ name: 'id', type: 'number' }, { name: 'email', type: 'string' }],
  rowCount: 1
})} />

Schema-aware autocomplete

Pass a schema namespace to get table + column completion: from base. completes data-source names and base.contact. completes columns. The useDsqlSchema hook fetches it from GET /v1/dsql/schema/apps/:appSlug (returns compact create table appSlug.dataSourceSlug ( … ) DDL per data source), parses it, and synthesizes the referenced tenant.user / tenant.enum system tables. The same fetched DDL is ideal to feed into the agent's editorContext so it authors valid DSQL.

import { DsqlEditor, useApplyDsql, useDsqlSchema } from '@docyrus/ui/components/dsql-editor';

const dsql = useApplyDsql();
const { schema, tables } = useDsqlSchema(client, ['base']);

<DsqlEditor client={client} query={dsql.query} onQueryChange={dsql.setQuery} schema={schema} />;

Building blocks are exported for custom wiring: fetchDsqlAppSchema(client, appSlug), fetchDsqlSchemaNamespace(client, apps), dsqlSchemasToNamespace(tables), and parseDsqlColumns(ddl). You can also pass a hand-built SQLNamespace directly to schema (or to the standalone DsqlCodeEditor / sqlExtensions({ schema })).

Auto-run when the agent writes a query

useApplyDsql takes an onApply(query) callback that fires right after the agent's applyDsqlQuery tool writes a (formatted) query into the editor. Combine it with the imperative ref to close the agent panel and run the query in one step:

const editorRef = useRef<DsqlEditorHandle>(null);
const dsql = useApplyDsql('', {
  onApply: (query) => {
    setAiOpen(false);             // close the agent panel
    editorRef.current?.run(query); // run immediately (pass query to avoid stale state)
  }
});

<DsqlEditor ref={editorRef} client={client} query={dsql.query} onQueryChange={dsql.setQuery} />;

The DsqlEditorHandle exposes run(query?) (runs the current text, or an explicit query) and getQuery().

Row caps

The backend returns a single page — there is no server-side pagination. The effective row limit is min(yourLimit ?? 100, maxLimit), where maxLimit is 1000 for delegated user sessions and 100 for API / client-credentials tokens. The editor surfaces the cap next to the result count so a truncated result isn't mistaken for the full set. Add LIMIT / filters to narrow large queries.

API Reference

PropTypeDefault
clientDsqlEditorClient | null—
onRun(query: string) => Promise<DsqlRunResult>—
querystring—
defaultQuerystring—
onQueryChange(next: string) => void—
onResult(result: DsqlRunResult) => void—
onError(message: string) => void—
schemaSQLNamespace—
resultPageSizenumber25
heightnumber | string'100%'
classNamestring—
aiAssistantOpenboolean—
onAiAssistantOpenChange(open: boolean) => void—
renderAiAssistant(ctx: DsqlAiAssistantRenderProps) => ReactNode—

DsqlCodeEditor

The standalone SQL editor pane (embed it in form fields or automation-node configs).

PropTypeDefault
valuestring—
onChange(value: string) => void—
readOnlybooleanfalse
placeholderstring—
autoFocusbooleanfalse
minHeightstring'2.5rem'
maxHeightstring'12rem'
heightstring—
classNamestring—
lineNumbersbooleantrue
schemaSQLNamespace—
basicSetupBasicSetupOptions—
extensionsExtension[]—

Components

ComponentDescription
DsqlEditorTwo-pane query + result workbench
DsqlCodeEditorStandalone SQL CodeMirror pane
useApplyDsqlOwns query state + the applyDsqlQuery agent client tool
useDsqlSchemaFetches + builds the autocomplete SQLNamespace for given apps
runDsqlQueryPUT /v1/dsql/query caller
normalizeDsqlResponseParses the API envelope into a DsqlRunResult
extractDsqlErrorPulls a human message out of a rejected request
fetchDsqlAppSchema / fetchDsqlSchemaNamespaceFetch DSQL schema from the schema endpoints
dsqlSchemasToNamespace / parseDsqlColumnsBuild a SQLNamespace from DDL schema text
formatSqlPostgreSQL-dialect formatter (never throws)
sqlExtensionsCodeMirror SQL highlight + completion extensions factory

Type Exports

TypeDescription
DsqlEditorPropsProps for DsqlEditor
DsqlCodeEditorPropsProps for DsqlCodeEditor
DsqlEditorClientMinimal { put } client contract
DsqlRunResult{ rows, columns, rowCount, durationMs? }
DsqlRunStateDiscriminated result-pane state
DsqlColumnMeta{ name, type?, label? } resolved column
DsqlTableSchemaRaw schema row from the schema endpoints ({ appSlug, slug, schema, … })
DsqlAiAssistantRenderPropsContext passed to renderAiAssistant

Type Reference

DsqlRunResult

FieldTypeDescription
rowsArray<Record<string, unknown>>Returned rows
columnsDsqlColumnMeta[]Columns inferred from row keys (SELECT order)
rowCountnumberRow count from the server (meta.count)
durationMsnumberClient-measured round-trip duration

DsqlColumnMeta

FieldTypeDescription
namestringColumn key
type'number' | 'boolean' | 'date' | 'datetime' | 'uuid' | 'string'Coarse inferred type (drives the cell variant)
labelstringOptional human label (defaults to name)

On this page