mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-09-09 00:04:10 +00:00
feat: local type references parsing support for main function args (#5995)
* add base struct * feat resolve interface and type declarion in entrypoint param's function * nits * fix reset dependencies * update package * fix handle infinite recursion * add depth level and handle enum for referenced type * nits * nits * nits * perf * fix * done * fix schema form cache inconsistency * fix default type and nits * remove * update Object typ for parser * one level ref from from parent when resolving types and use format for resource * update cli and use resource type * nits * update parsers * fix: use specific parser versions --------- Co-authored-by: HugoCasa <hugo@casademont.ch>
This commit is contained in:
+1
-1
@@ -28,7 +28,7 @@ members = [
|
||||
"./parsers/windmill-parser-py",
|
||||
"./parsers/windmill-parser-py-imports",
|
||||
"./parsers/windmill-sql-datatype-parser-wasm",
|
||||
"./parsers/windmill-parser-yaml", "windmill-macros", "parsers/windmill-parser-nu",
|
||||
"./parsers/windmill-parser-yaml", "windmill-macros", "parsers/windmill-parser-nu"
|
||||
]
|
||||
|
||||
[workspace.package]
|
||||
|
||||
@@ -7,7 +7,7 @@ use anyhow::anyhow;
|
||||
use tree_sitter::Node;
|
||||
use windmill_parser::Arg;
|
||||
use windmill_parser::MainArgSignature;
|
||||
use windmill_parser::Typ;
|
||||
use windmill_parser::{ObjectType, Typ};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct CsharpMainSigMeta {
|
||||
@@ -112,7 +112,7 @@ fn find_typ<'a>(typ_node: Node<'a>, code: &str) -> anyhow::Result<Typ> {
|
||||
Ok("double") | Ok("System.Double") => Ok(Typ::Float),
|
||||
Ok("bool") | Ok("System.Boolean") => Ok(Typ::Bool),
|
||||
Ok("decimal") | Ok("System.Decimal") => Ok(Typ::Float),
|
||||
Ok("object") => Ok(Typ::Object(vec![])), // TODO: Complete the object type
|
||||
Ok("object") => Ok(Typ::Object(ObjectType::new(None, Some(vec![])))), // TODO: Complete the object type
|
||||
Ok(s) => Err(anyhow!("Unknown type `{s}`")),
|
||||
Err(e) => Err(anyhow!("Error getting type name: {}", e)),
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ use gosyn::{
|
||||
use itertools::Itertools;
|
||||
|
||||
use regex::Regex;
|
||||
use windmill_parser::{Arg, MainArgSignature, ObjectProperty, Typ};
|
||||
use windmill_parser::{Arg, MainArgSignature, ObjectProperty, ObjectType, Typ};
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
pub static ref REQUIRE_PARSE: Regex = Regex::new(r"//require (.*)\n").unwrap();
|
||||
@@ -142,13 +142,13 @@ fn parse_go_typ(typ: &Expression) -> (Option<String>, Typ) {
|
||||
"struct {{ {} }}",
|
||||
otyps.iter().join("; ").to_string()
|
||||
)),
|
||||
Typ::Object(typs),
|
||||
Typ::Object(ObjectType::new(None, Some(typs))),
|
||||
)
|
||||
}
|
||||
Expression::TypeInterface(_) => (Some("interface{}".to_string()), Typ::Object(vec![])),
|
||||
Expression::TypeInterface(_) => (Some("interface{}".to_string()), Typ::Object(ObjectType::new(None, Some(vec![])))),
|
||||
Expression::TypeMap(_) => (
|
||||
Some("map[string]interface{}".to_string()),
|
||||
Typ::Object(vec![]),
|
||||
Typ::Object(ObjectType::new(None, Some(vec![]))),
|
||||
),
|
||||
_ => (None, Typ::Unknown),
|
||||
}
|
||||
@@ -218,10 +218,10 @@ func main(x int, y string, z bool, l []string, o struct { Name string `json:"nam
|
||||
Arg {
|
||||
otyp: Some("struct { Name string `json:\"name\"` }".to_string()),
|
||||
name: "o".to_string(),
|
||||
typ: Typ::Object(vec![ObjectProperty {
|
||||
typ: Typ::Object(ObjectType::new(None, Some(vec![ObjectProperty {
|
||||
key: "name".to_string(),
|
||||
typ: Box::new(Typ::Str(None))
|
||||
},]),
|
||||
},]))),
|
||||
default: None,
|
||||
has_default: false,
|
||||
oidx: None
|
||||
@@ -229,7 +229,7 @@ func main(x int, y string, z bool, l []string, o struct { Name string `json:"nam
|
||||
Arg {
|
||||
otyp: Some("interface{}".to_string()),
|
||||
name: "n".to_string(),
|
||||
typ: Typ::Object(vec![]),
|
||||
typ: Typ::Object(ObjectType::new(None, Some(vec![]))),
|
||||
default: None,
|
||||
has_default: false,
|
||||
oidx: None
|
||||
@@ -237,7 +237,7 @@ func main(x int, y string, z bool, l []string, o struct { Name string `json:"nam
|
||||
Arg {
|
||||
otyp: Some("map[string]interface{}".to_string()),
|
||||
name: "m".to_string(),
|
||||
typ: Typ::Object(vec![]),
|
||||
typ: Typ::Object(ObjectType::new(None, Some(vec![]))),
|
||||
default: None,
|
||||
has_default: false,
|
||||
oidx: None
|
||||
|
||||
@@ -9,7 +9,7 @@ use regex_lite::Regex;
|
||||
|
||||
use serde_json::json;
|
||||
|
||||
use windmill_parser::{Arg, MainArgSignature, Typ};
|
||||
use windmill_parser::{Arg, MainArgSignature, ObjectType, Typ};
|
||||
|
||||
pub fn parse_graphql_sig(code: &str) -> anyhow::Result<MainArgSignature> {
|
||||
let parsed = parse_graphql_file(&code)?;
|
||||
@@ -75,7 +75,7 @@ pub fn parse_graphql_typ(typ: &str) -> Typ {
|
||||
"Int" => Typ::Int,
|
||||
"Boolean" => Typ::Bool,
|
||||
"Float" => Typ::Float,
|
||||
_ => Typ::Object(vec![]),
|
||||
_ => Typ::Object(ObjectType::new(None, Some(vec![]))),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ use serde_json::Value;
|
||||
use tree_sitter::Node;
|
||||
use windmill_parser::Arg;
|
||||
use windmill_parser::MainArgSignature;
|
||||
use windmill_parser::Typ;
|
||||
use windmill_parser::{ObjectType, Typ};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct JavaMainSigMeta {
|
||||
@@ -101,7 +101,7 @@ fn find_typ<'a>(typ_node: Node<'a>, code: &str) -> anyhow::Result<(Typ, Option<V
|
||||
Ok("Double") => (Typ::Float, null),
|
||||
Ok("Boolean") => (Typ::Bool, null),
|
||||
Ok("Character") => (Typ::Str(None), null),
|
||||
Ok("Object") => (Typ::Object(vec![]),null), // TODO: Complete the object type
|
||||
Ok("Object") => (Typ::Object(ObjectType::new(None, Some(vec![]))),null), // TODO: Complete the object type
|
||||
Ok(s) => bail!("Unknown type `{s}`"),
|
||||
Err(e) => bail!("Error getting type name: {}", e),
|
||||
}
|
||||
@@ -394,7 +394,7 @@ class Main {
|
||||
Arg {
|
||||
name: "i".into(),
|
||||
otyp: Some("Object".into()),
|
||||
typ: Typ::Object(vec![]),
|
||||
typ: Typ::Object(ObjectType::new(None, Some(vec![]))),
|
||||
default: Some(json!(null)),
|
||||
has_default: true,
|
||||
oidx: None
|
||||
@@ -427,7 +427,7 @@ class Main {
|
||||
Arg {
|
||||
name: "b".into(),
|
||||
otyp: Some("Object[]".into()),
|
||||
typ: Typ::List(Box::new(Typ::Object(vec![]))),
|
||||
typ: Typ::List(Box::new(Typ::Object(ObjectType::new(None, Some(vec![]))))),
|
||||
default: Some(json!(null)),
|
||||
has_default: true,
|
||||
oidx: None
|
||||
|
||||
@@ -4,7 +4,7 @@ use anyhow::{anyhow, bail};
|
||||
use nu_parser::lex;
|
||||
|
||||
use serde_json::{json, Value};
|
||||
use windmill_parser::{Arg, MainArgSignature, Typ};
|
||||
use windmill_parser::{Arg, MainArgSignature, ObjectType, Typ};
|
||||
|
||||
pub fn parse_nu_signature(code: &str) -> anyhow::Result<MainArgSignature> {
|
||||
let (tokens, ..) = lex(code.as_bytes(), 0, &[], &[], true);
|
||||
@@ -162,8 +162,8 @@ pub fn parse_nu_signature(code: &str) -> anyhow::Result<MainArgSignature> {
|
||||
"int" => Typ::Int,
|
||||
"float" => Typ::Float,
|
||||
"number" => Typ::Float,
|
||||
"record" => Typ::Object(vec![]),
|
||||
"table" => Typ::List(Box::new(Typ::Object(vec![]))),
|
||||
"record" => Typ::Object(ObjectType::new(None, Some(vec![]))),
|
||||
"table" => Typ::List(Box::new(Typ::Object(ObjectType::new(None, Some(vec![]))))),
|
||||
"nothing" => Typ::Unknown,
|
||||
// TODO: needs additional work on literal parsing
|
||||
// "binary" => Typ::Bytes,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use serde_json::json;
|
||||
use windmill_parser::{Arg, MainArgSignature, Typ};
|
||||
use windmill_parser::{Arg, MainArgSignature, ObjectType, Typ};
|
||||
use windmill_parser_nu::parse_nu_signature;
|
||||
|
||||
#[test]
|
||||
@@ -200,7 +200,7 @@ mod test {
|
||||
Arg {
|
||||
name: "a7".into(),
|
||||
otyp: None,
|
||||
typ: Typ::Object(vec![]),
|
||||
typ: Typ::Object(ObjectType::new(None, Some(vec![]))),
|
||||
default: None,
|
||||
has_default: false,
|
||||
oidx: None
|
||||
@@ -216,7 +216,7 @@ mod test {
|
||||
Arg {
|
||||
name: "a9".into(),
|
||||
otyp: None,
|
||||
typ: Typ::List(Box::new(Typ::Object(vec![]))),
|
||||
typ: Typ::List(Box::new(Typ::Object(ObjectType::new(None, Some(vec![]))))),
|
||||
default: None,
|
||||
has_default: false,
|
||||
oidx: None
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use serde_json::Value;
|
||||
use windmill_parser::{to_snake_case, Arg, MainArgSignature, Typ};
|
||||
use windmill_parser::{to_snake_case, Arg, MainArgSignature, ObjectType, Typ};
|
||||
|
||||
use php_parser_rs::parser::{
|
||||
self,
|
||||
@@ -18,7 +18,7 @@ fn parse_php_type(e: Type) -> Typ {
|
||||
Type::Integer(_) => Typ::Int,
|
||||
Type::String(_) => Typ::Str(None),
|
||||
Type::Array(_) => Typ::List(Box::new(Typ::Str(None))),
|
||||
Type::Object(_) => Typ::Object(vec![]),
|
||||
Type::Object(_) => Typ::Object(ObjectType::new(None, Some(vec![]))),
|
||||
Type::Named(_, name) => Typ::Resource(to_snake_case(name.to_string().as_ref())),
|
||||
_ => Typ::Unknown,
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ use std::collections::HashMap;
|
||||
use itertools::Itertools;
|
||||
|
||||
use serde_json::json;
|
||||
use windmill_parser::{json_to_typ, Arg, MainArgSignature, Typ};
|
||||
use windmill_parser::{json_to_typ, Arg, MainArgSignature, ObjectType, Typ};
|
||||
|
||||
use rustpython_parser::{
|
||||
ast::{
|
||||
@@ -226,7 +226,7 @@ fn parse_typ(id: &str) -> Typ {
|
||||
"float" => Typ::Float,
|
||||
"int" => Typ::Int,
|
||||
"bool" => Typ::Bool,
|
||||
"dict" => Typ::Object(vec![]),
|
||||
"dict" => Typ::Object(ObjectType::new(None, Some(vec![]))),
|
||||
"list" => Typ::List(Box::new(Typ::Unknown)),
|
||||
"bytes" => Typ::Bytes,
|
||||
"datetime" => Typ::Datetime,
|
||||
|
||||
@@ -14,7 +14,7 @@ use std::{
|
||||
iter::Peekable,
|
||||
str::CharIndices,
|
||||
};
|
||||
pub use windmill_parser::{Arg, MainArgSignature, Typ};
|
||||
pub use windmill_parser::{Arg, MainArgSignature, ObjectType, Typ};
|
||||
|
||||
pub const SANITIZED_ENUM_STR: &str = "__sanitized_enum__";
|
||||
pub const SANITIZED_RAW_STRING_STR: &str = "__sanitized_raw_string__";
|
||||
@@ -737,7 +737,7 @@ pub fn parse_pg_typ(typ: &str) -> Typ {
|
||||
"bigint" => Typ::Int,
|
||||
"bool" | "boolean" => Typ::Bool,
|
||||
"char" | "character" => Typ::Str(None),
|
||||
"json" | "jsonb" => Typ::Object(vec![]),
|
||||
"json" | "jsonb" => Typ::Object(ObjectType::new(None, Some(vec![]))),
|
||||
"smallint" | "int2" => Typ::Int,
|
||||
"smallserial" | "serial2" => Typ::Int,
|
||||
"serial" | "serial4" => Typ::Int,
|
||||
@@ -769,7 +769,7 @@ pub fn parse_bigquery_typ(typ: &str) -> Typ {
|
||||
match typ {
|
||||
"string" => Typ::Str(None),
|
||||
"bytes" => Typ::Bytes,
|
||||
"json" => Typ::Object(vec![]),
|
||||
"json" => Typ::Object(ObjectType::new(None, Some(vec![]))),
|
||||
"timestamp" | "date" | "time" | "datetime" => Typ::Datetime,
|
||||
"integer" | "int64" => Typ::Int,
|
||||
"float" | "float64" | "numeric" | "bignumeric" => Typ::Float,
|
||||
|
||||
@@ -7,19 +7,20 @@
|
||||
*/
|
||||
// use deno_core::{serde_v8, v8, JsRuntime, RuntimeOptions};
|
||||
use serde_json::Value;
|
||||
use std::collections::HashSet;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use swc_ecma_visit::{noop_visit_type, Visit, VisitWith};
|
||||
use windmill_parser::{
|
||||
json_to_typ, to_snake_case, Arg, MainArgSignature, ObjectProperty, OneOfVariant, Typ,
|
||||
json_to_typ, to_snake_case, Arg, MainArgSignature, ObjectProperty, ObjectType, OneOfVariant,
|
||||
Typ,
|
||||
};
|
||||
|
||||
use swc_common::{sync::Lrc, FileName, SourceMap, SourceMapper, Span, Spanned};
|
||||
use swc_ecma_ast::{
|
||||
ArrayLit, AssignPat, BigInt, BindingIdent, Bool, Decl, ExportDecl, Expr, FnDecl, Ident,
|
||||
IdentName, Lit, MemberExpr, MemberProp, ModuleDecl, ModuleItem, Number, ObjectLit, ObjectPat,
|
||||
Param, Pat, Str, TsArrayType, TsEntityName, TsKeywordType, TsKeywordTypeKind, TsLit, TsLitType,
|
||||
TsOptionalType, TsParenthesizedType, TsPropertySignature, TsType, TsTypeAnn, TsTypeElement,
|
||||
TsTypeLit, TsTypeRef, TsUnionOrIntersectionType, TsUnionType,
|
||||
ArrayLit, AssignPat, BigInt, BindingIdent, Bool, Decl, ExportDecl, Expr, Ident, IdentName, Lit,
|
||||
MemberExpr, MemberProp, ModuleDecl, ModuleItem, Number, ObjectLit, ObjectPat, Param, Pat, Stmt,
|
||||
Str, TsArrayType, TsEntityName, TsInterfaceDecl, TsKeywordType, TsKeywordTypeKind, TsLit,
|
||||
TsLitType, TsOptionalType, TsParenthesizedType, TsPropertySignature, TsType, TsTypeAliasDecl,
|
||||
TsTypeAnn, TsTypeElement, TsTypeLit, TsTypeRef, TsUnionOrIntersectionType, TsUnionType,
|
||||
};
|
||||
use swc_ecma_parser::{lexer::Lexer, EsSyntax, Parser, StringInput, Syntax, TsSyntax};
|
||||
|
||||
@@ -205,6 +206,11 @@ pub fn parse_expr_for_ids(code: &str) -> anyhow::Result<Vec<(String, String)>> {
|
||||
Ok(visitor.idents.into_iter().collect())
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum TypeDecl {
|
||||
Interface(TsInterfaceDecl),
|
||||
Alias(TsTypeAliasDecl),
|
||||
}
|
||||
pub mod asset_parser;
|
||||
pub use asset_parser::parse_assets;
|
||||
|
||||
@@ -214,7 +220,7 @@ pub fn parse_deno_signature(
|
||||
code: &str,
|
||||
skip_dflt: bool,
|
||||
skip_params: bool,
|
||||
main_override: Option<String>,
|
||||
entrypoint_override: Option<String>,
|
||||
) -> anyhow::Result<MainArgSignature> {
|
||||
let cm: Lrc<SourceMap> = Default::default();
|
||||
let fm = cm.new_source_file(FileName::Custom("main.ts".into()).into(), code.into());
|
||||
@@ -234,6 +240,9 @@ pub fn parse_deno_signature(
|
||||
err_s += &e.into_kind().msg().to_string();
|
||||
}
|
||||
|
||||
let mut has_preprocessor = false;
|
||||
let mut entrypoint_params = None;
|
||||
|
||||
let ast = parser
|
||||
.parse_module()
|
||||
.map_err(|e| {
|
||||
@@ -241,35 +250,71 @@ pub fn parse_deno_signature(
|
||||
})?
|
||||
.body;
|
||||
|
||||
let has_preprocessor = ast.iter().any(|x| match x {
|
||||
ModuleItem::ModuleDecl(ModuleDecl::ExportDecl(ExportDecl {
|
||||
decl: Decl::Fn(FnDecl { ident: Ident { sym, .. }, .. }),
|
||||
..
|
||||
})) => &sym.to_string() == "preprocessor",
|
||||
_ => false,
|
||||
});
|
||||
let entrypoint_function = entrypoint_override.as_deref().unwrap_or("main");
|
||||
|
||||
let main_name = main_override.unwrap_or("main".to_string());
|
||||
let params = ast.into_iter().find_map(|x| match x {
|
||||
ModuleItem::ModuleDecl(ModuleDecl::ExportDecl(ExportDecl {
|
||||
decl: Decl::Fn(FnDecl { ident: Ident { sym, .. }, function, .. }),
|
||||
..
|
||||
})) if &sym.to_string() == &main_name => Some(function.params),
|
||||
_ => None,
|
||||
});
|
||||
let mut symbol_table: HashMap<String, TypeDecl> = HashMap::new();
|
||||
|
||||
for item in ast {
|
||||
if let ModuleItem::ModuleDecl(ModuleDecl::ExportDecl(ExportDecl { decl, .. }))
|
||||
| ModuleItem::Stmt(Stmt::Decl(decl)) = item
|
||||
{
|
||||
match decl {
|
||||
Decl::TsInterface(mut iface) => match symbol_table.get_mut(iface.id.sym.as_str()) {
|
||||
Some(TypeDecl::Interface(interface)) => {
|
||||
interface.body.body.append(&mut iface.body.body);
|
||||
}
|
||||
None => {
|
||||
symbol_table.insert(
|
||||
to_snake_case(iface.id.sym.as_str()),
|
||||
TypeDecl::Interface(*iface),
|
||||
);
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
Decl::TsTypeAlias(alias) => {
|
||||
symbol_table.insert(
|
||||
to_snake_case(alias.id.sym.as_str()),
|
||||
TypeDecl::Alias(*alias),
|
||||
);
|
||||
}
|
||||
Decl::Fn(fn_decl) => {
|
||||
let name = fn_decl.ident.sym.to_string();
|
||||
if name == "preprocessor" {
|
||||
has_preprocessor = true;
|
||||
}
|
||||
if name == entrypoint_function {
|
||||
entrypoint_params = Some(fn_decl.function.params.clone());
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut c: u16 = 0;
|
||||
let no_main_func = params.is_none();
|
||||
|
||||
let no_main_func = entrypoint_params.is_none();
|
||||
let mut type_resolver = HashMap::new();
|
||||
let r = MainArgSignature {
|
||||
star_args: false,
|
||||
star_kwargs: false,
|
||||
args: if skip_params {
|
||||
vec![]
|
||||
} else {
|
||||
params
|
||||
.map(|x| {
|
||||
x.into_iter()
|
||||
.map(|x| parse_param(x, &cm, skip_dflt, &mut c))
|
||||
entrypoint_params
|
||||
.map(|param| {
|
||||
param
|
||||
.into_iter()
|
||||
.map(|param| {
|
||||
parse_param(
|
||||
&symbol_table,
|
||||
&mut type_resolver,
|
||||
param,
|
||||
&cm,
|
||||
skip_dflt,
|
||||
&mut c,
|
||||
)
|
||||
})
|
||||
.collect::<anyhow::Result<Vec<Arg>>>()
|
||||
})
|
||||
.transpose()?
|
||||
@@ -282,14 +327,16 @@ pub fn parse_deno_signature(
|
||||
}
|
||||
|
||||
fn parse_param(
|
||||
x: Param,
|
||||
symbol_table: &HashMap<String, TypeDecl>,
|
||||
type_resolver: &mut HashMap<String, (Typ, bool)>,
|
||||
param: Param,
|
||||
cm: &Lrc<SourceMap>,
|
||||
skip_dflt: bool,
|
||||
counter: &mut u16,
|
||||
) -> anyhow::Result<Arg> {
|
||||
let r = match x.pat {
|
||||
let r = match param.pat {
|
||||
Pat::Ident(ident) => {
|
||||
let (name, typ, nullable) = binding_ident_to_arg(&ident);
|
||||
let (name, typ, nullable) = binding_ident_to_arg(symbol_table, type_resolver, &ident);
|
||||
Ok(Arg {
|
||||
otyp: None,
|
||||
name,
|
||||
@@ -302,9 +349,9 @@ fn parse_param(
|
||||
// Pat::Object(ObjectPat { ... }) = todo!()
|
||||
Pat::Assign(AssignPat { left, right, .. }) => {
|
||||
let (name, mut typ, _nullable) = match *left {
|
||||
Pat::Ident(ident) => binding_ident_to_arg(&ident),
|
||||
Pat::Ident(ident) => binding_ident_to_arg(symbol_table, type_resolver, &ident),
|
||||
Pat::Object(ObjectPat { type_ann, .. }) => {
|
||||
let (typ, nullable) = eval_type_ann(&type_ann);
|
||||
let (typ, nullable) = eval_type_ann(symbol_table, type_resolver, &type_ann);
|
||||
*counter += 1;
|
||||
let name = format!("anon{}", counter);
|
||||
(name, typ, nullable)
|
||||
@@ -346,16 +393,16 @@ fn parse_param(
|
||||
Ok(Arg { otyp: None, name, typ, default: dflt, has_default: true, oidx: None })
|
||||
}
|
||||
Pat::Object(ObjectPat { type_ann, .. }) => {
|
||||
let (typ, nullable) = eval_type_ann(&type_ann);
|
||||
let (typ, nullable) = eval_type_ann(symbol_table, type_resolver, &type_ann);
|
||||
*counter += 1;
|
||||
let name = format!("anon{}", counter);
|
||||
Ok(Arg { otyp: None, name, typ, default: None, has_default: nullable, oidx: None })
|
||||
}
|
||||
_ => Err(anyhow::anyhow!(
|
||||
"parameter syntax unsupported: `{}`: {:#?}",
|
||||
cm.span_to_snippet(x.span())
|
||||
.unwrap_or_else(|_| cm.span_to_string(x.span())),
|
||||
x.pat
|
||||
cm.span_to_snippet(param.span())
|
||||
.unwrap_or_else(|_| cm.span_to_string(param.span())),
|
||||
param.pat
|
||||
)),
|
||||
};
|
||||
r
|
||||
@@ -374,14 +421,22 @@ fn eval_span(span: Span, cm: &Lrc<SourceMap>) -> Option<Value> {
|
||||
}
|
||||
}
|
||||
|
||||
fn eval_type_ann(type_ann: &Option<Box<TsTypeAnn>>) -> (Typ, bool) {
|
||||
fn eval_type_ann(
|
||||
symbol_table: &HashMap<String, TypeDecl>,
|
||||
type_resolver: &mut HashMap<String, (Typ, bool)>,
|
||||
type_ann: &Option<Box<TsTypeAnn>>,
|
||||
) -> (Typ, bool) {
|
||||
return type_ann
|
||||
.as_ref()
|
||||
.map(|x| tstype_to_typ(&*x.type_ann))
|
||||
.map(|x| tstype_to_typ(symbol_table, type_resolver, &*x.type_ann, true))
|
||||
.unwrap_or((Typ::Unknown, false));
|
||||
}
|
||||
fn binding_ident_to_arg(BindingIdent { id, type_ann }: &BindingIdent) -> (String, Typ, bool) {
|
||||
let (typ, nullable) = eval_type_ann(type_ann);
|
||||
fn binding_ident_to_arg(
|
||||
symbol_table: &HashMap<String, TypeDecl>,
|
||||
type_resolver: &mut HashMap<String, (Typ, bool)>,
|
||||
BindingIdent { id, type_ann }: &BindingIdent,
|
||||
) -> (String, Typ, bool) {
|
||||
let (typ, nullable) = eval_type_ann(symbol_table, type_resolver, type_ann);
|
||||
(id.sym.to_string(), typ, nullable)
|
||||
}
|
||||
|
||||
@@ -411,11 +466,158 @@ pub fn remove_pinned_imports(code: &str) -> anyhow::Result<String> {
|
||||
Ok(content)
|
||||
}
|
||||
|
||||
fn tstype_to_typ(ts_type: &TsType) -> (Typ, bool) {
|
||||
fn resolve_type_ref(type_resolver: &HashMap<String, (Typ, bool)>, typ: &mut Typ) {
|
||||
let mut visited = std::collections::HashSet::new();
|
||||
resolve_type_ref_with_visited(type_resolver, typ, &mut visited);
|
||||
}
|
||||
|
||||
fn resolve_type_ref_with_visited(
|
||||
type_resolver: &HashMap<String, (Typ, bool)>,
|
||||
typ: &mut Typ,
|
||||
visited: &mut std::collections::HashSet<String>,
|
||||
) {
|
||||
match typ {
|
||||
Typ::Object(ObjectType { props: Some(obj), .. }) => {
|
||||
for property in obj.iter_mut() {
|
||||
resolve_type_ref_with_visited(type_resolver, &mut property.typ, visited);
|
||||
}
|
||||
}
|
||||
Typ::List(list) => resolve_type_ref_with_visited(type_resolver, list, visited),
|
||||
Typ::Object(ObjectType { name: Some(name), props: None }) => {
|
||||
if visited.contains(name) {
|
||||
return;
|
||||
}
|
||||
let maybe_resolved_type = type_resolver
|
||||
.get(name)
|
||||
.map(|rs_typ| {
|
||||
let mut typ = rs_typ.0.clone();
|
||||
visited.insert(name.clone());
|
||||
resolve_type_ref_with_visited(type_resolver, &mut typ, visited);
|
||||
visited.remove(name);
|
||||
typ
|
||||
})
|
||||
.unwrap_or(Typ::Object(ObjectType::new(Some(name.to_owned()), None)));
|
||||
|
||||
*typ = maybe_resolved_type;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn resolve_interface(
|
||||
iface: &TsInterfaceDecl,
|
||||
symbol_table: &HashMap<String, TypeDecl>,
|
||||
type_resolver: &mut HashMap<String, (Typ, bool)>,
|
||||
) -> Vec<ObjectProperty> {
|
||||
let mut properties = vec![];
|
||||
|
||||
for ext in &iface.extends {
|
||||
// If the current interface extends other interfaces,
|
||||
// retrieve their properties first and add them to the current interface's object definition.
|
||||
if let Expr::Ident(Ident { sym, .. }) = &*ext.expr {
|
||||
if let Some(TypeDecl::Interface(parent_iface)) =
|
||||
symbol_table.get(&to_snake_case(sym.as_str()))
|
||||
{
|
||||
properties.extend(resolve_interface(parent_iface, symbol_table, type_resolver));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for member in &iface.body.body {
|
||||
if let TsTypeElement::TsPropertySignature(sig) = member {
|
||||
if let Expr::Ident(Ident { sym, .. }) = &*sig.key {
|
||||
let typ = sig
|
||||
.type_ann
|
||||
.as_ref()
|
||||
.map(|ta| {
|
||||
Box::new(tstype_to_typ(symbol_table, type_resolver, &ta.type_ann, false).0)
|
||||
})
|
||||
.unwrap_or(Box::new(Typ::Unknown));
|
||||
|
||||
properties.push(ObjectProperty { key: sym.to_string(), typ });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
properties
|
||||
}
|
||||
|
||||
fn resolve_type_alias(
|
||||
alias: &TsTypeAliasDecl,
|
||||
symbol_table: &HashMap<String, TypeDecl>,
|
||||
type_resolver: &mut HashMap<String, (Typ, bool)>,
|
||||
top_level_call: bool,
|
||||
) -> (Typ, bool) {
|
||||
tstype_to_typ(symbol_table, type_resolver, &alias.type_ann, top_level_call)
|
||||
}
|
||||
|
||||
fn resolve_ts_interface_and_type_alias(
|
||||
type_name: &str,
|
||||
symbol_table: &HashMap<String, TypeDecl>,
|
||||
type_resolver: &mut HashMap<String, (Typ, bool)>,
|
||||
top_level_call: bool,
|
||||
) -> Option<(Typ, bool)> {
|
||||
let Some(type_declaration) = symbol_table.get(type_name) else {
|
||||
return None;
|
||||
};
|
||||
|
||||
if let Some(resolved_type) = type_resolver.get_mut(type_name) {
|
||||
return Some(resolved_type.to_owned());
|
||||
}
|
||||
|
||||
type_resolver.insert(
|
||||
type_name.to_owned(),
|
||||
(
|
||||
Typ::Object(ObjectType::new(Some(type_name.to_owned()), None)),
|
||||
false,
|
||||
),
|
||||
);
|
||||
|
||||
let mut resolved_type = match type_declaration {
|
||||
TypeDecl::Alias(alias) => {
|
||||
resolve_type_alias(alias, symbol_table, type_resolver, top_level_call)
|
||||
}
|
||||
TypeDecl::Interface(iface) => (
|
||||
Typ::Object(ObjectType::new(
|
||||
Some(to_snake_case(&type_name)),
|
||||
Some(resolve_interface(iface, symbol_table, type_resolver)),
|
||||
)),
|
||||
false,
|
||||
),
|
||||
};
|
||||
|
||||
if let Typ::Object(obj) = &mut resolved_type.0 {
|
||||
if obj.name.is_none() {
|
||||
obj.name = Some(type_name.to_owned());
|
||||
}
|
||||
}
|
||||
|
||||
type_resolver.insert(type_name.to_owned(), resolved_type.clone());
|
||||
|
||||
// `top_level_call` indicates whether the current invocation of the function
|
||||
// is at the topmost level (e.g., the immediate parameters of the main function).
|
||||
// When true:
|
||||
// - Type references within object properties (e.g., nested interfaces) are recursively resolved
|
||||
// up to a default depth to inline and fully materialize their structure.
|
||||
if top_level_call {
|
||||
resolve_type_ref(type_resolver, &mut resolved_type.0);
|
||||
}
|
||||
|
||||
Some(resolved_type)
|
||||
}
|
||||
|
||||
fn tstype_to_typ(
|
||||
symbol_table: &HashMap<String, TypeDecl>,
|
||||
type_resolver: &mut HashMap<String, (Typ, bool)>,
|
||||
ts_type: &TsType,
|
||||
top_level_call: bool,
|
||||
) -> (Typ, bool) {
|
||||
match ts_type {
|
||||
TsType::TsKeywordType(t) => (
|
||||
match t.kind {
|
||||
TsKeywordTypeKind::TsObjectKeyword => Typ::Object(vec![]),
|
||||
TsKeywordTypeKind::TsObjectKeyword => {
|
||||
Typ::Object(ObjectType::new(None, Some(vec![])))
|
||||
}
|
||||
TsKeywordTypeKind::TsBooleanKeyword => Typ::Bool,
|
||||
TsKeywordTypeKind::TsBigIntKeyword => Typ::Int,
|
||||
TsKeywordTypeKind::TsNumberKeyword => Typ::Float,
|
||||
@@ -437,7 +639,17 @@ fn tstype_to_typ(ts_type: &TsType) -> (Typ, bool) {
|
||||
key: sym.to_string(),
|
||||
typ: type_ann
|
||||
.as_ref()
|
||||
.map(|typ| Box::new(tstype_to_typ(&*typ.type_ann).0))
|
||||
.map(|typ| {
|
||||
Box::new(
|
||||
tstype_to_typ(
|
||||
symbol_table,
|
||||
type_resolver,
|
||||
&*typ.type_ann,
|
||||
top_level_call,
|
||||
)
|
||||
.0,
|
||||
)
|
||||
})
|
||||
.unwrap_or(Box::new(Typ::Unknown)),
|
||||
}),
|
||||
_ => None,
|
||||
@@ -445,21 +657,25 @@ fn tstype_to_typ(ts_type: &TsType) -> (Typ, bool) {
|
||||
_ => None,
|
||||
})
|
||||
.collect();
|
||||
(Typ::Object(properties), false)
|
||||
(Typ::Object(ObjectType::new(None, Some(properties))), false)
|
||||
}
|
||||
TsType::TsParenthesizedType(TsParenthesizedType { type_ann, .. }) => {
|
||||
tstype_to_typ(type_ann)
|
||||
tstype_to_typ(symbol_table, type_resolver, type_ann, top_level_call)
|
||||
}
|
||||
// TODO: we can do better here and extract the inner type of array
|
||||
TsType::TsArrayType(TsArrayType { elem_type, .. }) => {
|
||||
(Typ::List(Box::new(tstype_to_typ(&**elem_type).0)), false)
|
||||
}
|
||||
TsType::TsArrayType(TsArrayType { elem_type, .. }) => (
|
||||
Typ::List(Box::new(
|
||||
tstype_to_typ(symbol_table, type_resolver, &**elem_type, top_level_call).0,
|
||||
)),
|
||||
false,
|
||||
),
|
||||
TsType::TsLitType(TsLitType { lit: TsLit::Str(Str { value, .. }), .. }) => {
|
||||
(Typ::Str(Some(vec![value.to_string()])), false)
|
||||
}
|
||||
TsType::TsOptionalType(TsOptionalType { type_ann, .. }) => {
|
||||
(tstype_to_typ(type_ann).0, true)
|
||||
}
|
||||
TsType::TsOptionalType(TsOptionalType { type_ann, .. }) => (
|
||||
tstype_to_typ(symbol_table, type_resolver, type_ann, top_level_call).0,
|
||||
true,
|
||||
),
|
||||
TsType::TsUnionOrIntersectionType(TsUnionOrIntersectionType::TsUnionType(
|
||||
TsUnionType { types, .. },
|
||||
)) => {
|
||||
@@ -484,11 +700,18 @@ fn tstype_to_typ(ts_type: &TsType) -> (Typ, bool) {
|
||||
} else {
|
||||
0
|
||||
};
|
||||
(tstype_to_typ(&types[other_p]).0, true)
|
||||
(
|
||||
tstype_to_typ(symbol_table, type_resolver, &types[other_p], top_level_call).0,
|
||||
true,
|
||||
)
|
||||
} else {
|
||||
if types.len() > 1 {
|
||||
let one_of_values: Vec<OneOfVariant> =
|
||||
types.into_iter().map_while(parse_one_of_type).collect();
|
||||
let one_of_values: Vec<OneOfVariant> = types
|
||||
.into_iter()
|
||||
.map_while(|t| {
|
||||
parse_one_of_type(symbol_table, type_resolver, t, top_level_call)
|
||||
})
|
||||
.collect();
|
||||
|
||||
if one_of_values.len() == types.len() {
|
||||
return (Typ::OneOf(one_of_values), false);
|
||||
@@ -527,6 +750,7 @@ fn tstype_to_typ(ts_type: &TsType) -> (Typ, bool) {
|
||||
TsEntityName::Ident(Ident { sym, .. }) => sym,
|
||||
TsEntityName::TsQualifiedName(p) => &*p.right.sym,
|
||||
};
|
||||
|
||||
match sym.to_string().as_str() {
|
||||
"Resource" => (
|
||||
Typ::Resource(
|
||||
@@ -547,24 +771,69 @@ fn tstype_to_typ(ts_type: &TsType) -> (Typ, bool) {
|
||||
"Base64" => (Typ::Bytes, false),
|
||||
"Email" => (Typ::Email, false),
|
||||
"Sql" => (Typ::Sql, false),
|
||||
x @ _ if x.starts_with("DynSelect_") => (
|
||||
Typ::DynSelect(x.strip_prefix("DynSelect_").unwrap().to_string()),
|
||||
symbol @ _ if symbol.starts_with("DynSelect_") => (
|
||||
Typ::DynSelect(symbol.strip_prefix("DynSelect_").unwrap().to_string()),
|
||||
false,
|
||||
),
|
||||
x @ _ => (Typ::Resource(to_snake_case(x)), false),
|
||||
symbol @ _ => {
|
||||
let symbol = to_snake_case(symbol);
|
||||
|
||||
resolve_ts_interface_and_type_alias(
|
||||
&symbol,
|
||||
symbol_table,
|
||||
type_resolver,
|
||||
top_level_call,
|
||||
)
|
||||
.unwrap_or_else(|| (Typ::Resource(symbol), false))
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => (Typ::Unknown, false),
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_one_of_type(x: &Box<TsType>) -> Option<OneOfVariant> {
|
||||
fn parse_one_of_type(
|
||||
symbol_table: &HashMap<String, TypeDecl>,
|
||||
type_resolver: &mut HashMap<String, (Typ, bool)>,
|
||||
x: &Box<TsType>,
|
||||
top_level_call: bool,
|
||||
) -> Option<OneOfVariant> {
|
||||
match &**x {
|
||||
TsType::TsTypeLit(TsTypeLit { members, .. }) => {
|
||||
let label = one_of_label(members)?;
|
||||
let properties = one_of_properties(members);
|
||||
let properties =
|
||||
one_of_properties(symbol_table, type_resolver, members, top_level_call);
|
||||
Some(OneOfVariant { label, properties })
|
||||
}
|
||||
TsType::TsTypeRef(TsTypeRef { type_name, .. }) => {
|
||||
let label = type_name.as_ident()?.sym.to_string();
|
||||
match label.as_str() {
|
||||
symbol
|
||||
if ["Resource", "Date", "Base64", "Email", "Sql"]
|
||||
.iter()
|
||||
.any(|s| *s == symbol)
|
||||
|| symbol.starts_with("DynSelect_") =>
|
||||
{
|
||||
return None
|
||||
}
|
||||
symbol @ _ => {
|
||||
let Typ::Object(ObjectType { props: Some(properties), .. }) =
|
||||
resolve_ts_interface_and_type_alias(
|
||||
symbol,
|
||||
symbol_table,
|
||||
type_resolver,
|
||||
top_level_call,
|
||||
)
|
||||
.unwrap_or_else(|| (Typ::Resource(to_snake_case(symbol)), false))
|
||||
.0
|
||||
else {
|
||||
return None;
|
||||
};
|
||||
|
||||
Some(OneOfVariant { label, properties })
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
@@ -596,7 +865,12 @@ fn one_of_label(members: &Vec<TsTypeElement>) -> Option<String> {
|
||||
})
|
||||
}
|
||||
|
||||
fn one_of_properties(members: &Vec<TsTypeElement>) -> Vec<ObjectProperty> {
|
||||
fn one_of_properties(
|
||||
symbol_table: &HashMap<String, TypeDecl>,
|
||||
type_resolver: &mut HashMap<String, (Typ, bool)>,
|
||||
members: &Vec<TsTypeElement>,
|
||||
top_level_call: bool,
|
||||
) -> Vec<ObjectProperty> {
|
||||
members
|
||||
.iter()
|
||||
.filter_map(|x| {
|
||||
@@ -610,10 +884,12 @@ fn one_of_properties(members: &Vec<TsTypeElement>) -> Vec<ObjectProperty> {
|
||||
};
|
||||
let typ = type_ann
|
||||
.as_ref()
|
||||
.map(|typ| Box::new(tstype_to_typ(&*typ.type_ann).0))
|
||||
.unwrap_or(Box::new(Typ::Unknown));
|
||||
.map(|typ| {
|
||||
tstype_to_typ(symbol_table, type_resolver, &*typ.type_ann, top_level_call).0
|
||||
})
|
||||
.unwrap_or(Typ::Unknown);
|
||||
|
||||
Some(ObjectProperty { key: sym.to_string(), typ })
|
||||
Some(ObjectProperty { key: sym.to_string(), typ: Box::new(typ) })
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
@@ -115,7 +115,7 @@ def main [
|
||||
wasm-pack build ($profile) --target ($tar) --out-dir $env.OUT_DIR --features ($t.features) -Z build-std=panic_abort,std -Z build-std-features=panic_immediate_abort
|
||||
},
|
||||
"tree-sitter" => {
|
||||
$env.CFLAGS_wasm32_unknown_unknown = "-I$(pwd)/wasm-sysroot -Wbad-function-cast -Wcast-function-type -fno-builtin"
|
||||
$env.CFLAGS_wasm32_unknown_unknown = $"-I(pwd)/wasm-sysroot -Wbad-function-cast -Wcast-function-type -fno-builtin"
|
||||
$env.RUSTFLAGS = "-Zwasm-c-abi=spec"
|
||||
wasm-pack build ($profile) --target ($tar) --out-dir $env.OUT_DIR --features $t.features
|
||||
},
|
||||
@@ -123,9 +123,9 @@ def main [
|
||||
}
|
||||
|
||||
if ($cli) {
|
||||
rm ($env.OUT_DIR)/.gitignore
|
||||
rm $"($env.OUT_DIR)/.gitignore"
|
||||
} else {
|
||||
let p = ($env.OUT_DIR)/package.json
|
||||
let p = $"($env.OUT_DIR)/package.json"
|
||||
open $p | update name $"windmill-parser-wasm-($t.ident)" | save -f $p
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,3 +30,6 @@ popd
|
||||
|
||||
pushd "pkg-nu" && npm publish ${args}
|
||||
popd
|
||||
|
||||
pushd "pkg-java" && npm publish ${args}
|
||||
popd
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use serde_json::json;
|
||||
use wasm_bindgen_test::wasm_bindgen_test;
|
||||
use windmill_parser::{Arg, MainArgSignature, ObjectProperty, Typ};
|
||||
use windmill_parser::{Arg, MainArgSignature, ObjectProperty, ObjectType, Typ};
|
||||
use windmill_parser_bash::parse_powershell_sig;
|
||||
use windmill_parser_ts::{parse_deno_signature, parse_expr_for_ids, parse_expr_for_imports};
|
||||
|
||||
@@ -115,10 +115,10 @@ export function main(test1?: string, test2: string = \"burkina\",
|
||||
Arg {
|
||||
otyp: None,
|
||||
name: "min_object".to_string(),
|
||||
typ: Typ::Object(vec![
|
||||
typ: Typ::Object(ObjectType::new(None, Some(vec![
|
||||
ObjectProperty { key: "a".to_string(), typ: Box::new(Typ::Str(None)) },
|
||||
ObjectProperty { key: "b".to_string(), typ: Box::new(Typ::Float) }
|
||||
]),
|
||||
]))),
|
||||
default: None,
|
||||
has_default: false,
|
||||
oidx: None
|
||||
@@ -210,10 +210,10 @@ export function main(test2 = \"burkina\",
|
||||
Arg {
|
||||
otyp: None,
|
||||
name: "min_object".to_string(),
|
||||
typ: Typ::Object(vec![
|
||||
typ: Typ::Object(ObjectType::new(None, Some(vec![
|
||||
ObjectProperty { key: "a".to_string(), typ: Box::new(Typ::Str(None)) },
|
||||
ObjectProperty { key: "b".to_string(), typ: Box::new(Typ::Int) }
|
||||
]),
|
||||
]))),
|
||||
default: Some(json!({"a": "test", "b": 42})),
|
||||
has_default: true,
|
||||
oidx: None
|
||||
|
||||
@@ -2,7 +2,7 @@ use std::collections::HashMap;
|
||||
|
||||
use anyhow::anyhow;
|
||||
use serde_json::json;
|
||||
use windmill_parser::{Arg, MainArgSignature, ObjectProperty, Typ};
|
||||
use windmill_parser::{Arg, MainArgSignature, ObjectProperty, ObjectType, Typ};
|
||||
use yaml_rust::{Yaml, YamlEmitter, YamlLoader};
|
||||
|
||||
pub fn parse_ansible_sig(inner_content: &str) -> anyhow::Result<MainArgSignature> {
|
||||
@@ -147,9 +147,9 @@ fn parse_ansible_typ(arg: &Yaml) -> Typ {
|
||||
})
|
||||
}
|
||||
}
|
||||
Typ::Object(prop_vec)
|
||||
Typ::Object(ObjectType::new(None, Some(prop_vec)))
|
||||
} else {
|
||||
Typ::Object(vec![])
|
||||
Typ::Object(ObjectType::new(None, Some(vec![])))
|
||||
}
|
||||
}
|
||||
"array" => {
|
||||
|
||||
@@ -28,6 +28,24 @@ pub struct ObjectProperty {
|
||||
pub typ: Box<Typ>,
|
||||
}
|
||||
|
||||
impl ObjectProperty {
|
||||
pub fn new(key: String, typ: Box<Typ>) -> ObjectProperty {
|
||||
ObjectProperty { key, typ }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Clone, Debug, PartialEq)]
|
||||
pub struct ObjectType {
|
||||
pub name: Option<String>,
|
||||
pub props: Option<Vec<ObjectProperty>>,
|
||||
}
|
||||
|
||||
impl ObjectType {
|
||||
pub fn new(name: Option<String>, props: Option<Vec<ObjectProperty>>) -> ObjectType {
|
||||
ObjectType { name, props }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Clone, Debug, PartialEq)]
|
||||
#[serde(rename_all(serialize = "lowercase"))]
|
||||
pub struct OneOfVariant {
|
||||
@@ -49,7 +67,7 @@ pub enum Typ {
|
||||
Email,
|
||||
Sql,
|
||||
DynSelect(String),
|
||||
Object(Vec<ObjectProperty>),
|
||||
Object(ObjectType),
|
||||
OneOf(Vec<OneOfVariant>),
|
||||
Unknown,
|
||||
}
|
||||
@@ -70,11 +88,14 @@ pub fn json_to_typ(js: &Value) -> Typ {
|
||||
Value::Number(n) if n.is_i64() => Typ::Int,
|
||||
Value::Number(_) => Typ::Float,
|
||||
Value::Bool(_) => Typ::Bool,
|
||||
Value::Object(o) => Typ::Object(
|
||||
o.iter()
|
||||
.map(|(k, v)| ObjectProperty { key: k.to_string(), typ: Box::new(json_to_typ(v)) })
|
||||
.collect(),
|
||||
),
|
||||
Value::Object(o) => Typ::Object(ObjectType::new(
|
||||
None,
|
||||
Some(
|
||||
o.iter()
|
||||
.map(|(k, v)| ObjectProperty { key: k.to_string(), typ: Box::new(json_to_typ(v)) })
|
||||
.collect(),
|
||||
),
|
||||
)),
|
||||
Value::Array(a) => Typ::List(Box::new(a.first().map(json_to_typ).unwrap_or(Typ::Unknown))),
|
||||
_ => Typ::Unknown,
|
||||
}
|
||||
|
||||
@@ -14641,36 +14641,41 @@ components:
|
||||
- type: object
|
||||
properties:
|
||||
object:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
key:
|
||||
type: string
|
||||
typ:
|
||||
oneOf:
|
||||
- type: string
|
||||
enum:
|
||||
[
|
||||
"float",
|
||||
"int",
|
||||
"bool",
|
||||
"email",
|
||||
"unknown",
|
||||
"bytes",
|
||||
"dict",
|
||||
"datetime",
|
||||
"sql",
|
||||
]
|
||||
- type: object
|
||||
properties:
|
||||
str: {}
|
||||
required: [str]
|
||||
required:
|
||||
- key
|
||||
- typ
|
||||
type: object
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
props:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
key:
|
||||
type: string
|
||||
typ:
|
||||
oneOf:
|
||||
- type: string
|
||||
enum:
|
||||
[
|
||||
"float",
|
||||
"int",
|
||||
"bool",
|
||||
"email",
|
||||
"unknown",
|
||||
"bytes",
|
||||
"dict",
|
||||
"datetime",
|
||||
"sql",
|
||||
]
|
||||
- type: object
|
||||
properties:
|
||||
str: {}
|
||||
required: [str]
|
||||
required:
|
||||
- key
|
||||
- typ
|
||||
required:
|
||||
- object
|
||||
- object
|
||||
- type: object
|
||||
properties:
|
||||
list:
|
||||
|
||||
@@ -3079,7 +3079,7 @@ struct CancelJob {
|
||||
reason: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
enum PreviewKind {
|
||||
Code,
|
||||
@@ -3090,7 +3090,7 @@ enum PreviewKind {
|
||||
ScriptHash,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct Preview {
|
||||
content: Option<String>,
|
||||
kind: Option<PreviewKind>,
|
||||
|
||||
@@ -46,11 +46,13 @@ fn make_rules_for_arg_typ(typ: &Typ) -> Vec<SchemaValidationRule> {
|
||||
Typ::Sql => {
|
||||
rules.push(SchemaValidationRule::IsString);
|
||||
}
|
||||
Typ::Object(props) => {
|
||||
Typ::Object(object_type) => {
|
||||
let mut obj_rules = vec![];
|
||||
|
||||
for prop in props {
|
||||
obj_rules.push((prop.key.to_string(), make_rules_for_arg_typ(&prop.typ)));
|
||||
if let Some(props) = &object_type.props {
|
||||
for prop in props {
|
||||
obj_rules.push((prop.key.to_string(), make_rules_for_arg_typ(&prop.typ)));
|
||||
}
|
||||
}
|
||||
|
||||
rules.push(SchemaValidationRule::IsObject(obj_rules))
|
||||
|
||||
+137
-105
@@ -14,7 +14,11 @@ import {
|
||||
} from "./bootstrap/script_bootstrap.ts";
|
||||
import { Workspace } from "./workspace.ts";
|
||||
import { SchemaProperty } from "./bootstrap/common.ts";
|
||||
import { languagesWithRawReqsSupport, LanguageWithRawReqsSupport, ScriptLanguage } from "./script_common.ts";
|
||||
import {
|
||||
languagesWithRawReqsSupport,
|
||||
LanguageWithRawReqsSupport,
|
||||
ScriptLanguage,
|
||||
} from "./script_common.ts";
|
||||
import { inferContentTypeFromFilePath } from "./script_common.ts";
|
||||
import { GlobalDeps, exts, findGlobalDeps } from "./script.ts";
|
||||
import {
|
||||
@@ -37,12 +41,12 @@ export class LockfileGenerationError extends Error {
|
||||
}
|
||||
}
|
||||
|
||||
export async function generateAllMetadata() { }
|
||||
export async function generateAllMetadata() {}
|
||||
|
||||
function findClosestRawReqs(
|
||||
lang: LanguageWithRawReqsSupport | undefined,
|
||||
remotePath: string,
|
||||
globalDeps: GlobalDeps,
|
||||
globalDeps: GlobalDeps
|
||||
): string | undefined {
|
||||
let bestCandidate: { k: string; v: string } | undefined = undefined;
|
||||
if (lang) {
|
||||
@@ -74,11 +78,14 @@ async function generateFlowHash(
|
||||
// Get language name from path
|
||||
const lang = inferContentTypeFromFilePath(f.path, defaultTs);
|
||||
// Get lock for that language
|
||||
[, reqs] = Object.entries(rawReqs).find(([lang2, _]) => lang == lang2) ?? [];
|
||||
[, reqs] =
|
||||
Object.entries(rawReqs).find(([lang2, _]) => lang == lang2) ?? [];
|
||||
}
|
||||
|
||||
// Embed lock into hash
|
||||
hashes[f.path] = await generateHash(await f.getContentText() + (reqs ?? ""));
|
||||
hashes[f.path] = await generateHash(
|
||||
(await f.getContentText()) + (reqs ?? "")
|
||||
);
|
||||
}
|
||||
}
|
||||
return { ...hashes, [TOP_HASH]: await generateHash(JSON.stringify(hashes)) };
|
||||
@@ -92,7 +99,7 @@ export async function generateFlowLockInternal(
|
||||
},
|
||||
justUpdateMetadataLock?: boolean,
|
||||
noStaleMessage?: boolean,
|
||||
useRawReqs?: boolean,
|
||||
useRawReqs?: boolean
|
||||
): Promise<string | void> {
|
||||
if (folder.endsWith(SEP)) {
|
||||
folder = folder.substring(0, folder.length - 1);
|
||||
@@ -136,16 +143,20 @@ export async function generateFlowLockInternal(
|
||||
}
|
||||
|
||||
if (useRawReqs) {
|
||||
log.warn("If using local lockfiles, following redeployments from Web App will inevitably override generated lockfiles by CLI. To maintain your script's lockfiles you will need to redeploy only from CLI. (Behavior is subject to change)")
|
||||
log.warn(
|
||||
"If using local lockfiles, following redeployments from Web App will inevitably override generated lockfiles by CLI. To maintain your script's lockfiles you will need to redeploy only from CLI. (Behavior is subject to change)"
|
||||
);
|
||||
log.info(
|
||||
(await blueColor())(
|
||||
`Found raw requirements (${languagesWithRawReqsSupport.map((l) => l.rrFilename).join("/")}) for ${folder}, using it`,
|
||||
),
|
||||
`Found raw requirements (${languagesWithRawReqsSupport
|
||||
.map((l) => l.rrFilename)
|
||||
.join("/")}) for ${folder}, using it`
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
const flowValue = (await yamlParseFile(
|
||||
folder! + SEP + "flow.yaml",
|
||||
folder! + SEP + "flow.yaml"
|
||||
)) as FlowFile;
|
||||
|
||||
if (!justUpdateMetadataLock) {
|
||||
@@ -164,7 +175,7 @@ export async function generateFlowLockInternal(
|
||||
replaceInlineScripts(
|
||||
flowValue.value.modules,
|
||||
folder + SEP!,
|
||||
changedScripts,
|
||||
changedScripts
|
||||
);
|
||||
|
||||
//removeChangedLocks
|
||||
@@ -172,28 +183,26 @@ export async function generateFlowLockInternal(
|
||||
workspace,
|
||||
flowValue.value,
|
||||
remote_path,
|
||||
rawReqs,
|
||||
rawReqs
|
||||
);
|
||||
|
||||
const inlineScripts = extractInlineScriptsForFlows(
|
||||
flowValue.value.modules,
|
||||
newPathAssigner(opts.defaultTs ?? "bun"),
|
||||
newPathAssigner(opts.defaultTs ?? "bun")
|
||||
);
|
||||
inlineScripts
|
||||
.filter((s) => s.path.endsWith(".lock"))
|
||||
.forEach((s) => {
|
||||
Deno.writeTextFileSync(
|
||||
Deno.cwd() + SEP + folder + SEP + s.path,
|
||||
s.content,
|
||||
s.content
|
||||
);
|
||||
});
|
||||
|
||||
// Overwrite `flow.yaml` with the new lockfile references
|
||||
await Deno.writeTextFile(
|
||||
Deno.cwd() + SEP + folder + SEP + "flow.yaml",
|
||||
yamlStringify(
|
||||
flowValue as Record<string, any>
|
||||
)
|
||||
yamlStringify(flowValue as Record<string, any>)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -223,7 +232,7 @@ export async function generateScriptMetadataInternal(
|
||||
noStaleMessage: boolean,
|
||||
globalDeps: GlobalDeps,
|
||||
codebases: SyncCodebase[],
|
||||
justUpdateMetadataLock?: boolean,
|
||||
justUpdateMetadataLock?: boolean
|
||||
): Promise<string | undefined> {
|
||||
const remotePath = scriptPath
|
||||
.substring(0, scriptPath.indexOf("."))
|
||||
@@ -231,26 +240,24 @@ export async function generateScriptMetadataInternal(
|
||||
|
||||
const language = inferContentTypeFromFilePath(scriptPath, opts.defaultTs);
|
||||
|
||||
const rrLang = languagesWithRawReqsSupport.find((l) => language == l.language);
|
||||
|
||||
const rawReqs = findClosestRawReqs(
|
||||
rrLang,
|
||||
scriptPath,
|
||||
globalDeps,
|
||||
const rrLang = languagesWithRawReqsSupport.find(
|
||||
(l) => language == l.language
|
||||
);
|
||||
|
||||
const rawReqs = findClosestRawReqs(rrLang, scriptPath, globalDeps);
|
||||
|
||||
if (rawReqs && rrLang) {
|
||||
log.info(
|
||||
(await blueColor())(
|
||||
`Found raw requirements (${rrLang.rrFilename}) for ${scriptPath}, using it`,
|
||||
),
|
||||
`Found raw requirements (${rrLang.rrFilename}) for ${scriptPath}, using it`
|
||||
)
|
||||
);
|
||||
}
|
||||
const metadataWithType = await parseMetadataFile(
|
||||
remotePath,
|
||||
undefined,
|
||||
globalDeps,
|
||||
codebases,
|
||||
codebases
|
||||
);
|
||||
|
||||
// read script content
|
||||
@@ -262,7 +269,7 @@ export async function generateScriptMetadataInternal(
|
||||
if (await checkifMetadataUptodate(remotePath, hash, undefined)) {
|
||||
if (!noStaleMessage) {
|
||||
log.info(
|
||||
colors.green(`Script ${remotePath} metadata is up-to-date, skipping`),
|
||||
colors.green(`Script ${remotePath} metadata is up-to-date, skipping`)
|
||||
);
|
||||
}
|
||||
return;
|
||||
@@ -284,7 +291,7 @@ export async function generateScriptMetadataInternal(
|
||||
scriptContent,
|
||||
language,
|
||||
metadataParsedContent,
|
||||
scriptPath,
|
||||
scriptPath
|
||||
);
|
||||
}
|
||||
|
||||
@@ -298,7 +305,7 @@ export async function generateScriptMetadataInternal(
|
||||
language,
|
||||
remotePath,
|
||||
metadataParsedContent,
|
||||
rawReqs,
|
||||
rawReqs
|
||||
);
|
||||
} else {
|
||||
metadataParsedContent.lock = "";
|
||||
@@ -319,7 +326,7 @@ export async function generateScriptMetadataInternal(
|
||||
hash = await generateScriptHash(
|
||||
rawReqs,
|
||||
scriptContent,
|
||||
metadataContentUsedForHash,
|
||||
metadataContentUsedForHash
|
||||
);
|
||||
await updateMetadataGlobalLock(remotePath, hash);
|
||||
if (!justUpdateMetadataLock) {
|
||||
@@ -332,14 +339,14 @@ export async function updateScriptSchema(
|
||||
scriptContent: string,
|
||||
language: ScriptLanguage,
|
||||
metadataContent: Record<string, any>,
|
||||
path: string,
|
||||
path: string
|
||||
): Promise<void> {
|
||||
// infer schema from script content and update it inplace
|
||||
const result = await inferSchema(
|
||||
language,
|
||||
scriptContent,
|
||||
metadataContent.schema,
|
||||
path,
|
||||
path
|
||||
);
|
||||
metadataContent.schema = result.schema;
|
||||
if (result.has_preprocessor) {
|
||||
@@ -360,7 +367,7 @@ async function updateScriptLock(
|
||||
language: ScriptLanguage,
|
||||
remotePath: string,
|
||||
metadataContent: Record<string, any>,
|
||||
rawDeps: string | undefined,
|
||||
rawDeps: string | undefined
|
||||
): Promise<void> {
|
||||
if (
|
||||
!(
|
||||
@@ -393,7 +400,7 @@ async function updateScriptLock(
|
||||
raw_deps: rawDeps,
|
||||
entrypoint: remotePath,
|
||||
}),
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
let responseText = "reading response failed";
|
||||
@@ -404,11 +411,11 @@ async function updateScriptLock(
|
||||
if (lock === undefined) {
|
||||
if (response?.["error"]?.["message"]) {
|
||||
throw new LockfileGenerationError(
|
||||
`Failed to generate lockfile: ${response?.["error"]?.["message"]}`,
|
||||
`Failed to generate lockfile: ${response?.["error"]?.["message"]}`
|
||||
);
|
||||
}
|
||||
throw new LockfileGenerationError(
|
||||
`Failed to generate lockfile: ${JSON.stringify(response, null, 2)}`,
|
||||
`Failed to generate lockfile: ${JSON.stringify(response, null, 2)}`
|
||||
);
|
||||
}
|
||||
const lockPath = remotePath + ".script.lock";
|
||||
@@ -420,7 +427,7 @@ async function updateScriptLock(
|
||||
if (await Deno.stat(lockPath)) {
|
||||
await Deno.remove(lockPath);
|
||||
}
|
||||
} catch { }
|
||||
} catch {}
|
||||
metadataContent.lock = "";
|
||||
}
|
||||
} catch (e) {
|
||||
@@ -428,7 +435,7 @@ async function updateScriptLock(
|
||||
throw e;
|
||||
}
|
||||
throw new LockfileGenerationError(
|
||||
`Failed to generate lockfile:${rawResponse.statusText}, ${responseText}, ${e}`,
|
||||
`Failed to generate lockfile:${rawResponse.statusText}, ${responseText}, ${e}`
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -459,7 +466,7 @@ export async function updateFlow(
|
||||
use_local_lockfiles: true,
|
||||
raw_deps: rawDeps,
|
||||
}),
|
||||
},
|
||||
}
|
||||
);
|
||||
} else {
|
||||
// Standard dependency resolution on the server
|
||||
@@ -475,7 +482,7 @@ export async function updateFlow(
|
||||
flow_value,
|
||||
path: remotePath,
|
||||
}),
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@@ -489,20 +496,20 @@ export async function updateFlow(
|
||||
const msg = (res as any)?.["error"]?.["message"];
|
||||
if (msg) {
|
||||
throw new LockfileGenerationError(
|
||||
`Failed to generate lockfile: ${msg}`,
|
||||
`Failed to generate lockfile: ${msg}`
|
||||
);
|
||||
}
|
||||
throw new LockfileGenerationError(
|
||||
`Failed to generate lockfile: ${rawResponse.statusText}, ${responseText}`,
|
||||
`Failed to generate lockfile: ${rawResponse.statusText}, ${responseText}`
|
||||
);
|
||||
}
|
||||
return (res as any).updated_flow_value;
|
||||
} catch (e) {
|
||||
try {
|
||||
responseText = await rawResponse.text();
|
||||
} catch { }
|
||||
} catch {}
|
||||
throw new Error(
|
||||
`Failed to generate lockfile. Status was: ${rawResponse.statusText}, ${responseText}, ${e}`,
|
||||
`Failed to generate lockfile. Status was: ${rawResponse.statusText}, ${responseText}, ${e}`
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -514,7 +521,7 @@ export async function inferSchema(
|
||||
language: ScriptLanguage,
|
||||
content: string,
|
||||
currentSchema: any,
|
||||
path: string,
|
||||
path: string
|
||||
): Promise<{
|
||||
schema: any;
|
||||
has_preprocessor: boolean | undefined;
|
||||
@@ -640,8 +647,8 @@ export async function inferSchema(
|
||||
if (inferedSchema.type == "Invalid") {
|
||||
log.info(
|
||||
colors.yellow(
|
||||
`Script ${path} invalid, it cannot be parsed to infer schema.`,
|
||||
),
|
||||
`Script ${path} invalid, it cannot be parsed to infer schema.`
|
||||
)
|
||||
);
|
||||
return {
|
||||
schema: defaultScriptMetadata().schema,
|
||||
@@ -655,7 +662,7 @@ export async function inferSchema(
|
||||
}
|
||||
currentSchema.required = [];
|
||||
const oldProperties = JSON.parse(
|
||||
JSON.stringify(currentSchema?.properties ?? {}),
|
||||
JSON.stringify(currentSchema?.properties ?? {})
|
||||
);
|
||||
currentSchema.properties = {};
|
||||
|
||||
@@ -666,7 +673,7 @@ export async function inferSchema(
|
||||
currentSchema.properties[arg.name] = oldProperties[arg.name];
|
||||
}
|
||||
currentSchema.properties[arg.name] = sortObject(
|
||||
currentSchema.properties[arg.name],
|
||||
currentSchema.properties[arg.name]
|
||||
);
|
||||
|
||||
argSigToJsonSchemaType(arg.typ, currentSchema.properties[arg.name]);
|
||||
@@ -693,7 +700,7 @@ function sortObject(obj: any): any {
|
||||
...acc,
|
||||
[key]: obj[key],
|
||||
}),
|
||||
{},
|
||||
{}
|
||||
);
|
||||
}
|
||||
|
||||
@@ -703,24 +710,30 @@ export function argSigToJsonSchemaType(
|
||||
| string
|
||||
| { resource: string | null }
|
||||
| {
|
||||
list:
|
||||
| (string | { object: { key: string; typ: any }[] })
|
||||
| { str: any }
|
||||
| { object: { key: string; typ: any }[] }
|
||||
| null;
|
||||
}
|
||||
list:
|
||||
| (
|
||||
| string
|
||||
| {
|
||||
object: {
|
||||
name?: string;
|
||||
props?: { key: string; typ: any }[];
|
||||
};
|
||||
}
|
||||
)
|
||||
| { str: any }
|
||||
| { object: { name?: string; props?: { key: string; typ: any }[] } }
|
||||
| null;
|
||||
}
|
||||
| { dynselect: string }
|
||||
| { str: string[] | null }
|
||||
| { object: { key: string; typ: any }[] }
|
||||
| { object: { name?: string; props?: { key: string; typ: any }[] } }
|
||||
| {
|
||||
oneof: [
|
||||
{
|
||||
oneof: {
|
||||
label: string;
|
||||
properties: { key: string; typ: any }[];
|
||||
},
|
||||
];
|
||||
},
|
||||
oldS: SchemaProperty,
|
||||
}[];
|
||||
},
|
||||
oldS: SchemaProperty
|
||||
): void {
|
||||
const newS: SchemaProperty = { type: "" };
|
||||
if (t === "int") {
|
||||
@@ -770,9 +783,12 @@ export function argSigToJsonSchemaType(
|
||||
}
|
||||
} else if (typeof t !== "string" && `object` in t) {
|
||||
newS.type = "object";
|
||||
if (t.object) {
|
||||
if (t.object.name) {
|
||||
newS.format = `resource-${t.object.name}`;
|
||||
}
|
||||
if (t.object.props) {
|
||||
const properties: Record<string, any> = {};
|
||||
for (const prop of t.object) {
|
||||
for (const prop of t.object.props) {
|
||||
if (oldS.properties && prop.key in oldS.properties) {
|
||||
properties[prop.key] = oldS.properties[prop.key];
|
||||
} else {
|
||||
@@ -804,12 +820,24 @@ export function argSigToJsonSchemaType(
|
||||
newS.type = "array";
|
||||
if (t.list === "int" || t.list === "float") {
|
||||
newS.items = { type: "number" };
|
||||
newS.originalType = "number[]";
|
||||
} else if (t.list === "bytes") {
|
||||
newS.items = { type: "string", contentEncoding: "base64" };
|
||||
} else if (t.list == "string") {
|
||||
newS.items = { type: "string" };
|
||||
} else if (t.list && typeof t.list == "object" && "str" in t.list) {
|
||||
newS.originalType = "bytes[]";
|
||||
} else if (
|
||||
t.list &&
|
||||
typeof t.list == "object" &&
|
||||
"str" in t.list &&
|
||||
t.list.str
|
||||
) {
|
||||
newS.items = { type: "string", enum: t.list.str };
|
||||
newS.originalType = "enum[]";
|
||||
} else if (
|
||||
t.list == "string" ||
|
||||
(t.list && typeof t.list == "object" && "str" in t.list)
|
||||
) {
|
||||
newS.items = { type: "string", enum: oldS.items?.enum };
|
||||
newS.originalType = "string[]";
|
||||
} else if (
|
||||
t.list &&
|
||||
typeof t.list == "object" &&
|
||||
@@ -820,23 +848,30 @@ export function argSigToJsonSchemaType(
|
||||
type: "resource",
|
||||
resourceType: t.list.resource as string,
|
||||
};
|
||||
newS.originalType = "resource[]";
|
||||
} else if (
|
||||
t.list &&
|
||||
typeof t.list == "object" &&
|
||||
"object" in t.list &&
|
||||
t.list.object &&
|
||||
t.list.object.length > 0
|
||||
t.list.object
|
||||
) {
|
||||
const properties: Record<string, any> = {};
|
||||
for (const prop of t.list.object) {
|
||||
properties[prop.key] = { description: "", type: "" };
|
||||
|
||||
argSigToJsonSchemaType(prop.typ, properties[prop.key]);
|
||||
if (t.list.object.name) {
|
||||
newS.format = `resource-${t.list.object.name}`;
|
||||
}
|
||||
|
||||
newS.items = { type: "object", properties: properties };
|
||||
if (t.list.object.props && t.list.object.props.length > 0) {
|
||||
const properties: Record<string, any> = {};
|
||||
for (const prop of t.list.object.props) {
|
||||
properties[prop.key] = { description: "", type: "" };
|
||||
argSigToJsonSchemaType(prop.typ, properties[prop.key]);
|
||||
}
|
||||
newS.items = { type: "object", properties: properties };
|
||||
} else {
|
||||
newS.items = { type: "object" };
|
||||
}
|
||||
newS.originalType = "record[]";
|
||||
} else {
|
||||
newS.items = { type: "object" };
|
||||
newS.originalType = "object[]";
|
||||
}
|
||||
} else {
|
||||
newS.type = "object";
|
||||
@@ -886,16 +921,15 @@ export function argSigToJsonSchemaType(
|
||||
delete oldS.items;
|
||||
}
|
||||
|
||||
Object.assign(oldS, newS);
|
||||
if (oldS.format && !newS.format) {
|
||||
oldS.format = undefined
|
||||
}
|
||||
|
||||
Object.assign(oldS, newS);
|
||||
// if (sameItems && savedItems != undefined && savedItems.enum != undefined) {
|
||||
// sendUserToast(JSON.stringify(savedItems))
|
||||
// oldS.items = savedItems
|
||||
// }
|
||||
|
||||
if (oldS.format?.startsWith("resource-") && newS.type != "object") {
|
||||
oldS.format = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -912,9 +946,7 @@ export function replaceLock(o?: { lock?: string | string[] }) {
|
||||
o.lock = readInlinePathSync(lockPath);
|
||||
} catch (e) {
|
||||
log.info(
|
||||
colors.yellow(
|
||||
`Failed to read lockfile, doing as if it was empty: ${e}`,
|
||||
),
|
||||
colors.yellow(`Failed to read lockfile, doing as if it was empty: ${e}`)
|
||||
);
|
||||
o.lock = "";
|
||||
}
|
||||
@@ -924,13 +956,13 @@ export async function parseMetadataFile(
|
||||
scriptPath: string,
|
||||
generateMetadataIfMissing:
|
||||
| (GlobalOptions & {
|
||||
path: string;
|
||||
workspaceRemote: Workspace;
|
||||
schemaOnly?: boolean;
|
||||
})
|
||||
path: string;
|
||||
workspaceRemote: Workspace;
|
||||
schemaOnly?: boolean;
|
||||
})
|
||||
| undefined,
|
||||
globalDeps: GlobalDeps,
|
||||
codebases: SyncCodebase[],
|
||||
codebases: SyncCodebase[]
|
||||
): Promise<{ isJson: boolean; payload: any; path: string }> {
|
||||
let metadataFilePath = scriptPath + ".script.json";
|
||||
try {
|
||||
@@ -956,14 +988,14 @@ export async function parseMetadataFile(
|
||||
// no metadata file at all. Create it
|
||||
log.info(
|
||||
(await blueColor())(
|
||||
`Creating script metadata file for ${metadataFilePath}`,
|
||||
),
|
||||
`Creating script metadata file for ${metadataFilePath}`
|
||||
)
|
||||
);
|
||||
metadataFilePath = scriptPath + ".script.yaml";
|
||||
let scriptInitialMetadata = defaultScriptMetadata();
|
||||
const scriptInitialMetadataYaml = yamlStringify(
|
||||
scriptInitialMetadata as Record<string, any>,
|
||||
yamlOptions,
|
||||
yamlOptions
|
||||
);
|
||||
await Deno.writeTextFile(metadataFilePath, scriptInitialMetadataYaml, {
|
||||
createNew: true,
|
||||
@@ -972,8 +1004,8 @@ export async function parseMetadataFile(
|
||||
if (generateMetadataIfMissing) {
|
||||
log.info(
|
||||
(await blueColor())(
|
||||
`Generating lockfile and schema for ${metadataFilePath}`,
|
||||
),
|
||||
`Generating lockfile and schema for ${metadataFilePath}`
|
||||
)
|
||||
);
|
||||
try {
|
||||
await generateScriptMetadataInternal(
|
||||
@@ -984,10 +1016,10 @@ export async function parseMetadataFile(
|
||||
false,
|
||||
globalDeps,
|
||||
codebases,
|
||||
false,
|
||||
false
|
||||
);
|
||||
scriptInitialMetadata = (await yamlParseFile(
|
||||
metadataFilePath,
|
||||
metadataFilePath
|
||||
)) as ScriptMetadata;
|
||||
if (!generateMetadataIfMissing.schemaOnly) {
|
||||
replaceLock(scriptInitialMetadata);
|
||||
@@ -995,8 +1027,8 @@ export async function parseMetadataFile(
|
||||
} catch (e) {
|
||||
log.info(
|
||||
colors.yellow(
|
||||
`Failed to generate lockfile and schema for ${metadataFilePath}: ${e}`,
|
||||
),
|
||||
`Failed to generate lockfile and schema for ${metadataFilePath}: ${e}`
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1035,7 +1067,7 @@ export async function checkifMetadataUptodate(
|
||||
path: string,
|
||||
hash: string,
|
||||
conf: Lock | undefined,
|
||||
subpath?: string,
|
||||
subpath?: string
|
||||
) {
|
||||
if (!conf) {
|
||||
conf = await readLockfile();
|
||||
@@ -1051,17 +1083,17 @@ export async function checkifMetadataUptodate(
|
||||
export async function generateScriptHash(
|
||||
rawReqs: string | undefined,
|
||||
scriptContent: string,
|
||||
newMetadataContent: string,
|
||||
newMetadataContent: string
|
||||
) {
|
||||
return await generateHash(
|
||||
(rawReqs ?? "") + scriptContent + newMetadataContent,
|
||||
(rawReqs ?? "") + scriptContent + newMetadataContent
|
||||
);
|
||||
}
|
||||
|
||||
export async function updateMetadataGlobalLock(
|
||||
path: string,
|
||||
hash: string,
|
||||
subpath?: string,
|
||||
subpath?: string
|
||||
): Promise<void> {
|
||||
const conf = await readLockfile();
|
||||
if (!conf?.locks) {
|
||||
@@ -1080,6 +1112,6 @@ export async function updateMetadataGlobalLock(
|
||||
}
|
||||
await Deno.writeTextFile(
|
||||
WMILL_LOCKFILE,
|
||||
yamlStringify(conf as Record<string, any>, yamlOptions),
|
||||
yamlStringify(conf as Record<string, any>, yamlOptions)
|
||||
);
|
||||
}
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -254,7 +254,7 @@ const imports = {
|
||||
const ret = Object.entries(arg0);
|
||||
return ret;
|
||||
},
|
||||
__wbg_eval_b297c6e75720318e: function(arg0, arg1) {
|
||||
__wbg_eval_5e26645562cd9430: function(arg0, arg1) {
|
||||
const ret = eval(getStringFromWasm0(arg0, arg1));
|
||||
return ret;
|
||||
},
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Generated
+41
-41
@@ -74,17 +74,17 @@
|
||||
"vscode-languageclient": "~9.0.1",
|
||||
"vscode-uri": "~3.1.0",
|
||||
"vscode-ws-jsonrpc": "~3.4.0",
|
||||
"windmill-parser-wasm-csharp": "^1.437.1",
|
||||
"windmill-parser-wasm-go": "^1.429.0",
|
||||
"windmill-parser-wasm-java": "^1.478.1",
|
||||
"windmill-parser-wasm-nu": "^1.474.1",
|
||||
"windmill-parser-wasm-php": "^1.429.0",
|
||||
"windmill-parser-wasm-py": "^1.510.0",
|
||||
"windmill-parser-wasm-regex": "^1.510.0",
|
||||
"windmill-parser-wasm-rust": "^1.429.0",
|
||||
"windmill-parser-wasm-ts": "^1.510.0",
|
||||
"windmill-parser-wasm-yaml": "^1.429.0",
|
||||
"windmill-sql-datatype-parser-wasm": "^1.318.0",
|
||||
"windmill-parser-wasm-csharp": "1.510.1",
|
||||
"windmill-parser-wasm-go": "1.510.1",
|
||||
"windmill-parser-wasm-java": "1.510.1",
|
||||
"windmill-parser-wasm-nu": "1.510.1",
|
||||
"windmill-parser-wasm-php": "1.510.1",
|
||||
"windmill-parser-wasm-py": "1.510.1",
|
||||
"windmill-parser-wasm-regex": "1.510.1",
|
||||
"windmill-parser-wasm-rust": "1.510.1",
|
||||
"windmill-parser-wasm-ts": "1.510.1",
|
||||
"windmill-parser-wasm-yaml": "1.510.1",
|
||||
"windmill-sql-datatype-parser-wasm": "1.318.0",
|
||||
"xterm": "^5.3.0",
|
||||
"xterm-readline": "^1.1.2",
|
||||
"y-monaco": "^0.1.4",
|
||||
@@ -12940,54 +12940,54 @@
|
||||
}
|
||||
},
|
||||
"node_modules/windmill-parser-wasm-csharp": {
|
||||
"version": "1.437.1",
|
||||
"resolved": "https://registry.npmjs.org/windmill-parser-wasm-csharp/-/windmill-parser-wasm-csharp-1.437.1.tgz",
|
||||
"integrity": "sha512-qzB/kUE9JCf1CYFDz+50AI+SUVaZYn3lSgqmJ11Iuibl41AC4EIvfH4zrsOU53lcTOb9b4ZH3D6z9FjCCfqWsw=="
|
||||
"version": "1.510.1",
|
||||
"resolved": "https://registry.npmjs.org/windmill-parser-wasm-csharp/-/windmill-parser-wasm-csharp-1.510.1.tgz",
|
||||
"integrity": "sha512-qm09YmnbeYHLwYn1jUnObVzPhYO9NZKMlIO7nlo7zPJBXqksgG5fK/KCtwGw9rChrnz+DsvM9wP5FhrwRLMtwQ=="
|
||||
},
|
||||
"node_modules/windmill-parser-wasm-go": {
|
||||
"version": "1.429.0",
|
||||
"resolved": "https://registry.npmjs.org/windmill-parser-wasm-go/-/windmill-parser-wasm-go-1.429.0.tgz",
|
||||
"integrity": "sha512-M3jeGDqeTyPj9HyyX3msdzMrqIIzlfMfxTMsXS8m7MJp4Cm60qifMxD29Ipxb2B4WdzyGwCSlaBjLsXu0b3c5g=="
|
||||
"version": "1.510.1",
|
||||
"resolved": "https://registry.npmjs.org/windmill-parser-wasm-go/-/windmill-parser-wasm-go-1.510.1.tgz",
|
||||
"integrity": "sha512-HOkk6LXK0wrwvkn+zjm3Gxo90HmyL6TYqmLo2yp8fZuppy7GOngT27zwYeBtwONiPyvDKskzoqPQoEfd8VuUsQ=="
|
||||
},
|
||||
"node_modules/windmill-parser-wasm-java": {
|
||||
"version": "1.478.1",
|
||||
"resolved": "https://registry.npmjs.org/windmill-parser-wasm-java/-/windmill-parser-wasm-java-1.478.1.tgz",
|
||||
"integrity": "sha512-2gMr5pXaEExoLjeSMOpHzY4UjDU/JGjvlXSnBiQDHUaEoKSS7FWa5APzBKcmn8cM+mr2p8m4sMCyTXS88At0fQ=="
|
||||
"version": "1.510.1",
|
||||
"resolved": "https://registry.npmjs.org/windmill-parser-wasm-java/-/windmill-parser-wasm-java-1.510.1.tgz",
|
||||
"integrity": "sha512-Zle+JZT/ZwUArUVacUudYlS+CaHp2lSnkqD/IhWaRUG+gcv26VbERnrrHPonqXbVMS+eA9ElfXrFM5j0ukaXUw=="
|
||||
},
|
||||
"node_modules/windmill-parser-wasm-nu": {
|
||||
"version": "1.474.1",
|
||||
"resolved": "https://registry.npmjs.org/windmill-parser-wasm-nu/-/windmill-parser-wasm-nu-1.474.1.tgz",
|
||||
"integrity": "sha512-ikRhz9CWpWilt/7K8mmRniF2cdWnH6EgjuL189z76pcEQbuMwDO4eXocAdMDaufehOsTWpXmDtsYeFfmayPIpQ=="
|
||||
"version": "1.510.1",
|
||||
"resolved": "https://registry.npmjs.org/windmill-parser-wasm-nu/-/windmill-parser-wasm-nu-1.510.1.tgz",
|
||||
"integrity": "sha512-AJLFiUy6af+LpUe7CddDo4+JOmw3c0K/1iOWh8NdTwXcLDj90lL6089mdsVo1apyloLgrTbcuFDzZMXVGBgtCg=="
|
||||
},
|
||||
"node_modules/windmill-parser-wasm-php": {
|
||||
"version": "1.429.0",
|
||||
"resolved": "https://registry.npmjs.org/windmill-parser-wasm-php/-/windmill-parser-wasm-php-1.429.0.tgz",
|
||||
"integrity": "sha512-SGJAtNpfdRZftkGboxWsm/yQDnJBJodwPQUbX2cWk/aoNook6ULesZwsYtBC9WN1VH6TIskLiVPohMmu6jtXmw=="
|
||||
"version": "1.510.1",
|
||||
"resolved": "https://registry.npmjs.org/windmill-parser-wasm-php/-/windmill-parser-wasm-php-1.510.1.tgz",
|
||||
"integrity": "sha512-qM+yeaqPdMuAaPpqND31ZabpeHlPxxtmRWLs11cGHOCHU32FIaZ92/icNUoAxgBhdJjRhR4GXaeZG32nWit2cg=="
|
||||
},
|
||||
"node_modules/windmill-parser-wasm-py": {
|
||||
"version": "1.510.0",
|
||||
"resolved": "https://registry.npmjs.org/windmill-parser-wasm-py/-/windmill-parser-wasm-py-1.510.0.tgz",
|
||||
"integrity": "sha512-zH7p8POSO5gsTC94ZlyfVWecre0JuNHHfBMbotOV533J7PpSgGR20s+NfyNwlT7n4KmSdVmmxUMkmWAkeg8kOA=="
|
||||
"version": "1.510.1",
|
||||
"resolved": "https://registry.npmjs.org/windmill-parser-wasm-py/-/windmill-parser-wasm-py-1.510.1.tgz",
|
||||
"integrity": "sha512-0ES6W1l/j3NjvHgGX0l6wHvioMX3SsTWe4kQIkYP12ISpAIKfSvDpE2sFm6FQ+xoxekzGqYS4luPSmKX4aDYpQ=="
|
||||
},
|
||||
"node_modules/windmill-parser-wasm-regex": {
|
||||
"version": "1.510.0",
|
||||
"resolved": "https://registry.npmjs.org/windmill-parser-wasm-regex/-/windmill-parser-wasm-regex-1.510.0.tgz",
|
||||
"integrity": "sha512-AiM+oPbojiFsnK1dfZurc/AJdGMpUs8yOaSLOs55DvJD3Pm1L0WMSfBrti0PTUY1hrKmtavfWgGU7dlOeNz+rg=="
|
||||
"version": "1.510.1",
|
||||
"resolved": "https://registry.npmjs.org/windmill-parser-wasm-regex/-/windmill-parser-wasm-regex-1.510.1.tgz",
|
||||
"integrity": "sha512-CUH5+DVramdKD1zVQteWIdwJ2RK9vlfipwYKvXI33DCEAUupQdLdCEyLd8Ics0aZBOrpDoWpgILqkmzPKdRJEg=="
|
||||
},
|
||||
"node_modules/windmill-parser-wasm-rust": {
|
||||
"version": "1.429.0",
|
||||
"resolved": "https://registry.npmjs.org/windmill-parser-wasm-rust/-/windmill-parser-wasm-rust-1.429.0.tgz",
|
||||
"integrity": "sha512-c8mjpiw8RxoaBDtecb+sKeWM/IOjNr4Y06nHudGu8sMM48MNO1LhgcISLv8wl6Z9zWd7OzQrECJ6RLorpii5Uw=="
|
||||
"version": "1.510.1",
|
||||
"resolved": "https://registry.npmjs.org/windmill-parser-wasm-rust/-/windmill-parser-wasm-rust-1.510.1.tgz",
|
||||
"integrity": "sha512-tqT+w5gvwiX9NZCzT7iafh7dWrWSs46t+LI+N8/1+QOpv95G9/NJ/m0DIfH9Wyfkvtx0ge6igLMFha2wlPKG+w=="
|
||||
},
|
||||
"node_modules/windmill-parser-wasm-ts": {
|
||||
"version": "1.510.0",
|
||||
"resolved": "https://registry.npmjs.org/windmill-parser-wasm-ts/-/windmill-parser-wasm-ts-1.510.0.tgz",
|
||||
"integrity": "sha512-uxcE7gghDQ6lmkjemiNjhrnk0SviUwy80k25GERu5Ujliw8ceynw2cAv9k060+fWTcB3UazgNLbrhT9uPe0cQQ=="
|
||||
"version": "1.510.1",
|
||||
"resolved": "https://registry.npmjs.org/windmill-parser-wasm-ts/-/windmill-parser-wasm-ts-1.510.1.tgz",
|
||||
"integrity": "sha512-LIXFb/jETo+EOOvcEj7SUqPlRuc63bAZcGBvJDiZ/aUOMdPquCL4/7Eu+JzTrb8lSJpFu3ZU0ToUFCLIWzhn1w=="
|
||||
},
|
||||
"node_modules/windmill-parser-wasm-yaml": {
|
||||
"version": "1.429.0",
|
||||
"resolved": "https://registry.npmjs.org/windmill-parser-wasm-yaml/-/windmill-parser-wasm-yaml-1.429.0.tgz",
|
||||
"integrity": "sha512-elQYkaWOvzB8LiwVV9NbNqrupSmdtRY3mMEl+qmKTJhGLvYrVOxA8zyBtwGK0MGiFhTOO3ZO96LWSlDfBnpN9g=="
|
||||
"version": "1.510.1",
|
||||
"resolved": "https://registry.npmjs.org/windmill-parser-wasm-yaml/-/windmill-parser-wasm-yaml-1.510.1.tgz",
|
||||
"integrity": "sha512-zQ1imcKrhP3iccJ01BK0+tptguo3Xc+J5ku2lgrZ+YQdDcC2wjGb6gH+kvcsMXDE3WT4aRwV3nL+ecHa7WHSrw=="
|
||||
},
|
||||
"node_modules/windmill-sql-datatype-parser-wasm": {
|
||||
"version": "1.318.0",
|
||||
|
||||
+11
-11
@@ -141,17 +141,17 @@
|
||||
"vscode-languageclient": "~9.0.1",
|
||||
"vscode-uri": "~3.1.0",
|
||||
"vscode-ws-jsonrpc": "~3.4.0",
|
||||
"windmill-parser-wasm-csharp": "^1.437.1",
|
||||
"windmill-parser-wasm-go": "^1.429.0",
|
||||
"windmill-parser-wasm-java": "^1.478.1",
|
||||
"windmill-parser-wasm-nu": "^1.474.1",
|
||||
"windmill-parser-wasm-php": "^1.429.0",
|
||||
"windmill-parser-wasm-py": "^1.510.0",
|
||||
"windmill-parser-wasm-regex": "^1.510.0",
|
||||
"windmill-parser-wasm-rust": "^1.429.0",
|
||||
"windmill-parser-wasm-ts": "^1.510.0",
|
||||
"windmill-parser-wasm-yaml": "^1.429.0",
|
||||
"windmill-sql-datatype-parser-wasm": "^1.318.0",
|
||||
"windmill-parser-wasm-csharp": "1.510.1",
|
||||
"windmill-parser-wasm-go": "1.510.1",
|
||||
"windmill-parser-wasm-java": "1.510.1",
|
||||
"windmill-parser-wasm-nu": "1.510.1",
|
||||
"windmill-parser-wasm-php": "1.510.1",
|
||||
"windmill-parser-wasm-py": "1.510.1",
|
||||
"windmill-parser-wasm-regex": "1.510.1",
|
||||
"windmill-parser-wasm-rust": "1.510.1",
|
||||
"windmill-parser-wasm-ts": "1.510.1",
|
||||
"windmill-parser-wasm-yaml": "1.510.1",
|
||||
"windmill-sql-datatype-parser-wasm": "1.318.0",
|
||||
"xterm": "^5.3.0",
|
||||
"xterm-readline": "^1.1.2",
|
||||
"y-monaco": "^0.1.4",
|
||||
|
||||
@@ -683,7 +683,7 @@
|
||||
onchange={(x) => fileChanged(x, (val) => (value[i] = val))}
|
||||
multiple={false}
|
||||
/>
|
||||
{:else if itemsType?.type == 'object' && itemsType?.resourceType === undefined && itemsType?.properties === undefined}
|
||||
{:else if itemsType?.type == 'object' && itemsType?.resourceType === undefined && itemsType?.properties === undefined && !(format?.startsWith('resource-') && resourceTypes?.includes(format.split('-')[1]))}
|
||||
{#await import('$lib/components/JsonEditor.svelte')}
|
||||
<Loader2 class="animate-spin" />
|
||||
{:then Module}
|
||||
@@ -709,10 +709,16 @@
|
||||
enum_={itemsType?.enum ?? []}
|
||||
enumLabels={extra['enumLabels']}
|
||||
/>
|
||||
{:else if itemsType?.type == 'resource' && itemsType?.resourceType && resourceTypes?.includes(itemsType.resourceType)}
|
||||
{:else if (itemsType?.type == 'resource' && itemsType?.resourceType && resourceTypes?.includes(itemsType.resourceType)) || (format?.startsWith('resource-') && resourceTypes?.includes(format.split('-')[1]))}
|
||||
{@const resourceFormat =
|
||||
itemsType?.type == 'resource' &&
|
||||
itemsType.resourceType &&
|
||||
resourceTypes.includes(itemsType.resourceType)
|
||||
? `resource-${itemsType.resourceType}`
|
||||
: format!}
|
||||
<ObjectResourceInput
|
||||
bind:value={value[i]}
|
||||
format={'resource-' + itemsType?.resourceType}
|
||||
format={resourceFormat}
|
||||
defaultValue={undefined}
|
||||
/>
|
||||
{:else if itemsType?.type == 'resource'}
|
||||
@@ -780,7 +786,11 @@
|
||||
if (itemsType?.type == 'number') {
|
||||
value = value.concat(0)
|
||||
} else if (
|
||||
itemsType?.type == 'object' ||
|
||||
(itemsType?.type == 'object' &&
|
||||
!(
|
||||
format?.startsWith('resource-') &&
|
||||
resourceTypes?.includes(format.split('-')[1])
|
||||
)) ||
|
||||
(itemsType?.type == 'resource' &&
|
||||
!(
|
||||
itemsType?.resourceType && resourceTypes?.includes(itemsType?.resourceType)
|
||||
|
||||
@@ -252,6 +252,7 @@
|
||||
$effect.pre(() => {
|
||||
isValid = allTrue(inputCheck ?? {})
|
||||
})
|
||||
|
||||
const actions_render = $derived(actions)
|
||||
</script>
|
||||
|
||||
|
||||
@@ -6,14 +6,14 @@ export function argSigToJsonSchemaType(
|
||||
| { resource: string | null }
|
||||
| {
|
||||
list:
|
||||
| (string | { object: { key: string; typ: any }[] })
|
||||
| (string | { name?: string; props?: { key: string; typ: any }[] })
|
||||
| { str: any }
|
||||
| { object: { key: string; typ: any }[] }
|
||||
| { object: { name?: string; props?: { key: string; typ: any }[] } }
|
||||
| null
|
||||
}
|
||||
| { dynselect: string }
|
||||
| { str: string[] | null }
|
||||
| { object: { key: string; typ: any }[] }
|
||||
| { object: { name?: string; props?: { key: string; typ: any }[] } }
|
||||
| {
|
||||
oneof: {
|
||||
label: string
|
||||
@@ -69,9 +69,12 @@ export function argSigToJsonSchemaType(
|
||||
}
|
||||
} else if (typeof t !== 'string' && `object` in t) {
|
||||
newS.type = 'object'
|
||||
if (t.object) {
|
||||
if (t.object.name) {
|
||||
newS.format = `resource-${t.object.name}`
|
||||
}
|
||||
if (t.object.props) {
|
||||
const properties: Record<string, any> = {}
|
||||
for (const prop of t.object) {
|
||||
for (const prop of t.object.props) {
|
||||
if (oldS.properties && prop.key in oldS.properties) {
|
||||
properties[prop.key] = oldS.properties[prop.key]
|
||||
} else {
|
||||
@@ -119,21 +122,20 @@ export function argSigToJsonSchemaType(
|
||||
resourceType: t.list.resource as string
|
||||
}
|
||||
newS.originalType = 'resource[]'
|
||||
} else if (
|
||||
t.list &&
|
||||
typeof t.list == 'object' &&
|
||||
'object' in t.list &&
|
||||
t.list.object &&
|
||||
t.list.object.length > 0
|
||||
) {
|
||||
const properties: Record<string, any> = {}
|
||||
for (const prop of t.list.object) {
|
||||
properties[prop.key] = { description: '', type: '' }
|
||||
|
||||
argSigToJsonSchemaType(prop.typ, properties[prop.key])
|
||||
} else if (t.list && typeof t.list == 'object' && 'object' in t.list && t.list.object) {
|
||||
if (t.list.object.name) {
|
||||
newS.format = `resource-${t.list.object.name}`
|
||||
}
|
||||
if (t.list.object.props && t.list.object.props.length > 0) {
|
||||
const properties: Record<string, any> = {}
|
||||
for (const prop of t.list.object.props) {
|
||||
properties[prop.key] = { description: '', type: '' }
|
||||
argSigToJsonSchemaType(prop.typ, properties[prop.key])
|
||||
}
|
||||
newS.items = { type: 'object', properties: properties }
|
||||
} else {
|
||||
newS.items = { type: 'object' }
|
||||
}
|
||||
|
||||
newS.items = { type: 'object', properties: properties }
|
||||
newS.originalType = 'record[]'
|
||||
} else {
|
||||
newS.items = { type: 'object' }
|
||||
@@ -184,14 +186,14 @@ export function argSigToJsonSchemaType(
|
||||
delete oldS.items
|
||||
}
|
||||
|
||||
Object.assign(oldS, newS)
|
||||
if (oldS.format && !newS.format) {
|
||||
oldS.format = undefined
|
||||
}
|
||||
|
||||
Object.assign(oldS, newS)
|
||||
// if (sameItems && savedItems != undefined && savedItems.enum != undefined) {
|
||||
// sendUserToast(JSON.stringify(savedItems))
|
||||
// oldS.items = savedItems
|
||||
// }
|
||||
|
||||
if (oldS.format?.startsWith('resource-') && newS.type != 'object') {
|
||||
oldS.format = undefined
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user