// common/proto/table_script.proto syntax = "proto3"; package komp_ac.table_script; // Manages column-computation scripts for user-defined tables and supplies the // dependency data used by the client-side Steel runtime. // Each script belongs to a single table (table_definition_id) and populates // exactly one target column in that table. The server: // - Validates script syntax (non-empty, balanced parentheses, starts with '(') // - Validates the target column (exists, not a system column, allowed type) // - Validates column/type usage inside math expressions // - Validates referenced tables/columns against the schema // - Enforces link constraints for structured access (see notes below) // - Analyzes dependencies and prevents cycles across the schema // - Transforms the script to decimal-safe math (steel_decimal) // - Upserts into table_scripts and records dependencies in script_dependencies // - Hydrates external column and aggregate inputs requested by the client // // The client fetches stored scripts and their declared dependencies, builds a // restricted Steel context, and executes scripts for immediate form feedback. // Current-table values come from the client's active row snapshot. External // values must come from HydrateScriptDependencies; the client must not query // arbitrary database data from inside the Steel VM. // // Server-side persistence remains authoritative and recalculates affected rows. // Script creation and update are transactional. service TableScript { // Create or update a script for a specific table and target column. // // Behavior: // - Fetches the table by table_definition_id (must exist) // - Validates "script" (syntax), "target_column" (exists and type rules), // and all referenced tables/columns (must exist in same schema) // - Validates math operations: prohibits using certain data types in math // - Enforces link constraints for structured table access: // • Allowed always: self-references (same table) // • Structured access via steel_get_column // requires an explicit link in table_definition_links // - Rejects raw SQL access; steel_query_sql is not part of the supported DSL // - Detects and rejects circular dependencies across all scripts in the schema // (self-references are allowed and not treated as cycles) // - Transforms the script to decimal-safe operations (steel_decimal) // - UPSERTS into table_scripts on (table_definitions_id, target_column) // and saves a normalized dependency list into script_dependencies rpc PostTableScript(PostTableScriptRequest) returns (TableScriptResponse); // Fetch all stored scripts for a specific table. // // Behavior: // - Resolves the table from (profile_name, table_name) // - Returns the stored, transformed script from table_scripts // - Includes normalized dependency metadata from script_dependencies // - Returns an empty scripts list when the table has no scripts // // Client use: // - Registers each client-evaluable script with the computed-field runtime // - Uses dependencies as the allowlist for values exposed to the Steel VM // - Uses target_column_type to validate and convert the script result rpc GetTableScripts(GetTableScriptsRequest) returns (GetTableScriptsResponse); // Build the external data snapshot needed to execute a table's scripts in // the client-side Steel runtime. // // The server derives the required inputs from stored script_dependencies; // callers do not choose arbitrary tables or columns. Direct related-column // reads and related aggregates are evaluated through SQLx in one read-only // PostgreSQL snapshot. Returned values include logical type and currency // metadata so the client can create correctly typed ScriptValues. // // Current-table column values are intentionally not returned. The client // supplies those directly from row_data so unsaved edits participate in // immediate calculations. The response replaces the client's previous // script dependency cache as one complete hydration snapshot. rpc HydrateScriptDependencies(HydrateScriptDependenciesRequest) returns (HydrateScriptDependenciesResponse); } // Request to create or update a script bound to a specific table and column. message PostTableScriptRequest { // Required. The metadata ID from table_definitions.id that identifies the // table this script belongs to. The table must exist; its schema determines // where referenced tables/columns are validated and where dependencies are stored. int64 table_definition_id = 1; // Required. The target column in the target table that this script computes. // Must be an existing user-defined column in that table (not a system column). // System columns are reserved: "id", "deleted", "created_at", "row_revision". // The column's data type must NOT be one of the prohibited target types: // BIGINT, DATE, TIMESTAMPTZ // Note: BOOLEAN targets are allowed (values are converted to Steel #true/#false). string target_column = 2; // Required. The script in the Steel DSL (S-expression style). // Syntax requirements: // - Non-empty, must start with '(' // - Balanced parentheses // // Referencing data: // - Structured table/column access (enforces link constraints): // (steel_get_column "table_name" "column_name") // • current-table references are read directly from the active row // • other tables require an explicit link from the source table // (table_definition_links) or the request fails // - Related collections use allowlisted aggregate shorthand: // @sum(table.column), @min(table.column), @max(table.column), // @count(table.column), @count_distinct(table.column), // @any(table.boolean), @all(table.boolean), // @count_rows(table via anchor), @exists(table via anchor) // Every related aggregate requires the `via anchor` clause. The related // table must be reachable through that anchor by an unambiguous FK path; // the path may span multiple FK hops. // - Raw SQL access is not supported; steel_query_sql is rejected // // Math operations: // - The script is transformed by steel_decimal; supported math forms include: // +, -, *, /, ^, **, pow, sqrt, >, <, =, >=, <=, min, max, abs, round, // ln, log, log10, exp, sin, cos, tan // - Columns of the following types CANNOT be used inside math expressions: // BIGINT, TEXT, BOOLEAN, DATE, TIMESTAMPTZ // // Dependency tracking and cycles: // - Dependencies are extracted from steel_get_column calls and stored // in script_dependencies with context // - Cycles across tables are rejected (self-dependency is allowed) string script = 3; // Optional. Free-text description stored alongside the script (no functional effect). string description = 4; } // Response after creating or updating a script. message TableScriptResponse { // The ID of the script record in table_scripts (new or existing on upsert). int64 id = 1; // Human-readable warnings concatenated into a single string. Possible messages: // - Warning if the script references itself (may affect first population) // - Info about number of structured linked-table accesses // - Warning if many dependencies may affect performance string warnings = 2; } message GetTableScriptsRequest { // Required. Profile (schema) name. string profile_name = 1; // Required. Table name within the profile. string table_name = 2; } message GetTableScriptsResponse { // Scripts and dependency allowlists used to configure the client Steel runtime. repeated StoredTableScript scripts = 1; } message StoredTableScript { // Persistent script identifier. int64 id = 1; // Display-name key of the current-table field populated by this script. string target_column = 2; // Logical type used by the client to validate and convert the result. string target_column_type = 3; // Validated and transformed Steel expression executed by the client for // immediate feedback and by the server for authoritative persistence. string script = 4; string description = 5; // Complete allowlist of data inputs that may be exposed to this script. repeated ScriptDependency dependencies = 6; } message ScriptDependency { // Logical table name referenced by the script. string target_table = 1; // Normalized dependency kind, such as column_access or related_aggregate. string dependency_type = 2; // Logical column name. Empty for aggregates that operate on rows only. string column = 3; // Aggregate operation name, such as sum, count_rows, or exists; empty for // column_access dependencies. string operation = 4; // Relationship table used to match the owner row to the related collection. string via_table = 5; } // Identifies the active form row whose external Steel inputs must be hydrated. message HydrateScriptDependenciesRequest { // Required profile/database schema containing the scripted table. string profile_name = 1; // Required logical name of the table owning the scripts. string table_name = 2; // Persisted owner-row ID, or zero for a new unsaved row. int64 row_id = 3; // Complete current client form snapshot keyed by logical column name. // It contains unsaved current-table values and related foreign keys such as // "customer_id". The server uses it to resolve related rows and new-row // aggregate semantics; it is not an arbitrary dependency request. map row_data = 4; } // One declared cross-table column input for the client Steel context. message HydratedColumnValue { // Logical related-table name used by steel_get_column. string target_table = 1; // Related row from which the value was loaded. int64 row_id = 2; // Logical column name used by steel_get_column. string column = 3; // String-encoded database value. An empty string represents NULL/empty input. string value = 4; // Logical database type used to create a typed client ScriptValue. string field_type = 5; // Related table's base currency for MONEY values; otherwise empty. string base_currency = 6; } // One declared related-collection aggregate input for the client Steel context. message HydratedAggregateValue { // Normalized aggregate operation: sum, min, max, count, count_distinct, // any, all, count_rows, or exists. string operation = 1; // Logical table whose related rows were aggregated. string target_table = 2; // Logical aggregated column; empty for count_rows and exists. string column = 3; // Relationship anchor used to distinguish aggregate dependency paths. string via_table = 4; // String-encoded aggregate result. string value = 5; // Logical source-column type; empty for row-only aggregates. string field_type = 6; // Aggregate table's base currency for MONEY values; otherwise empty. string base_currency = 7; } // Complete external dependency snapshot for client-side Steel execution. message HydrateScriptDependenciesResponse { // Exact related-column inputs declared by stored scripts. repeated HydratedColumnValue columns = 1; // Exact related aggregate inputs declared by stored scripts. repeated HydratedAggregateValue aggregates = 2; }