feat: release Dever 0.1.5 with direct workers and long transactions
This commit is contained in:
1 parent
89629c2197
commit
a900d7e8db
153 files changed
+3676
-3708
No files matched your search
@@ -99,11 +99,13 @@ pub(super) fn validate_rest_bindings(program: &Program, errors: &mut Vec<Diagnos
|
||||
ExpressionKind::Call {
|
||||
target: CallTarget::Function(target),
|
||||
..
|
||||
} => {
|
||||
if !rest_function_allowed(program, *target, &model_domain) {
|
||||
errors.push(Diagnostic::error("C013", "REST field bindings call only pure same-domain Domain functions or pure standard library functions", value.span));
|
||||
}
|
||||
} if !rest_function_allowed(program, *target, &model_domain) => {
|
||||
errors.push(Diagnostic::error("C013", "REST field bindings call only pure same-domain Domain functions or pure standard library functions", value.span));
|
||||
}
|
||||
ExpressionKind::Call {
|
||||
target: CallTarget::Function(_),
|
||||
..
|
||||
} => {}
|
||||
ExpressionKind::Call { .. }
|
||||
| ExpressionKind::ModelOperation { .. }
|
||||
| ExpressionKind::JobEnqueue { .. }
|
||||
@@ -270,57 +272,44 @@ fn rest_intrinsic_allowed(operation: crate::intrinsic::Intrinsic) -> bool {
|
||||
}
|
||||
|
||||
pub(super) fn validate_write_transactions(program: &Program, errors: &mut Vec<Diagnostic>) {
|
||||
let write_routes = program
|
||||
let roots = program
|
||||
.api_routes
|
||||
.iter()
|
||||
.filter(|route| route.method != "GET")
|
||||
.map(|route| route.function)
|
||||
.map(|route| (route.function, route.long))
|
||||
.chain(
|
||||
program
|
||||
.api_commands
|
||||
.iter()
|
||||
.map(|command| (command.function, command.long)),
|
||||
)
|
||||
.chain(program.jobs.iter().map(|job| (job.function, job.long)))
|
||||
.collect::<std::collections::BTreeSet<_>>();
|
||||
for function in &write_routes {
|
||||
let root = crate::specialize::Specialization {
|
||||
function: *function,
|
||||
handlers: Vec::new(),
|
||||
};
|
||||
let password_crypto = crate::specialize::uses_intrinsic(
|
||||
program,
|
||||
&root,
|
||||
crate::intrinsic::Intrinsic::CryptoPasswordVerify,
|
||||
) || crate::specialize::uses_intrinsic(
|
||||
program,
|
||||
&root,
|
||||
crate::intrinsic::Intrinsic::CryptoPasswordHash,
|
||||
);
|
||||
if password_crypto
|
||||
&& let Some(span) = crate::specialize::write_outside_transaction(program, &root)
|
||||
{
|
||||
let mut error = Diagnostic::error(
|
||||
"C014",
|
||||
"password crypto API writes must be enclosed in an explicit transaction function",
|
||||
program.functions[*function].span,
|
||||
);
|
||||
error.related.push(crate::diagnostic::Label {
|
||||
span,
|
||||
message: "database write occurs outside the short transaction boundary".into(),
|
||||
});
|
||||
errors.push(error);
|
||||
}
|
||||
}
|
||||
let roots = write_routes
|
||||
.into_iter()
|
||||
.chain(program.api_commands.iter().map(|command| command.function))
|
||||
.chain(program.jobs.iter().map(|job| job.function))
|
||||
.collect::<std::collections::BTreeSet<_>>();
|
||||
for function in roots {
|
||||
for (function, long) in roots {
|
||||
let root = crate::specialize::Specialization {
|
||||
function,
|
||||
handlers: Vec::new(),
|
||||
};
|
||||
if crate::specialize::writes_database(program, &root)
|
||||
if long {
|
||||
if let Some(span) = crate::specialize::write_outside_transaction(program, &root) {
|
||||
let mut error = Diagnostic::error(
|
||||
"C014",
|
||||
"long entry database writes must be enclosed in an explicit transaction function",
|
||||
program.functions[function].span,
|
||||
);
|
||||
error.related.push(crate::diagnostic::Label {
|
||||
span,
|
||||
message: "database write occurs outside the explicit transaction boundary"
|
||||
.into(),
|
||||
});
|
||||
errors.push(error);
|
||||
}
|
||||
} else if crate::specialize::writes_database(program, &root)
|
||||
&& crate::specialize::starts_transaction_unsafe_work(program, &root)
|
||||
{
|
||||
errors.push(Diagnostic::error(
|
||||
"C014",
|
||||
"an API, CMD, or Job database transaction cannot start concurrent work",
|
||||
"automatic entry transactions cannot contain external I/O, Workers, waits, concurrency, or password crypto; declare the entry 'long' and enclose its writes in explicit transaction functions",
|
||||
program.functions[function].span,
|
||||
));
|
||||
}
|
||||
@@ -526,6 +515,7 @@ pub(super) fn register(
|
||||
let path = format!("/{prefix}/{}", binding.action.text);
|
||||
if occupy(&mut routes, (method, path.clone()), binding.span, "HTTP route", errors) {
|
||||
entries.routes.push(ApiRoute { method, path, function: id, inputs,
|
||||
long: binding.long,
|
||||
directory: directory.clone(), anonymous: binding.anonymous,
|
||||
component: component.clone(), domain: domain.clone(),
|
||||
action: binding.action.text.clone(), auth: None, permission: None,
|
||||
@@ -536,7 +526,7 @@ pub(super) fn register(
|
||||
} else {
|
||||
let name = format!("{domain_name}.{}", binding.action.text);
|
||||
if occupy(&mut commands, name.clone(), binding.span, "command", errors) {
|
||||
entries.commands.push(ApiCommand { name, function: id, inputs, components: Vec::new() });
|
||||
entries.commands.push(ApiCommand { name, function: id, long: binding.long, inputs, components: Vec::new() });
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -981,6 +971,7 @@ fn legacy_routes(
|
||||
inputs,
|
||||
directory: Vec::new(),
|
||||
anonymous: false,
|
||||
long: false,
|
||||
span: clause.span,
|
||||
component: function.name.split('.').next().unwrap_or("app").to_owned(),
|
||||
domain: function.name.split('.').nth(1).unwrap_or("main").to_owned(),
|
||||
|
||||
@@ -113,18 +113,19 @@ pub(super) fn register(
|
||||
let mut scheduled = BTreeSet::new();
|
||||
for declaration in &package.declarations {
|
||||
match declaration {
|
||||
Declaration::Database(binding) if is_job => {
|
||||
if connection
|
||||
.replace(ConnectionSelector::Explicit(binding.name.text.clone()))
|
||||
.is_some()
|
||||
{
|
||||
errors.push(Diagnostic::error(
|
||||
"C002",
|
||||
"duplicate Job database binding",
|
||||
binding.span,
|
||||
));
|
||||
}
|
||||
Declaration::Database(binding)
|
||||
if is_job
|
||||
&& connection
|
||||
.replace(ConnectionSelector::Explicit(binding.name.text.clone()))
|
||||
.is_some() =>
|
||||
{
|
||||
errors.push(Diagnostic::error(
|
||||
"C002",
|
||||
"duplicate Job database binding",
|
||||
binding.span,
|
||||
));
|
||||
}
|
||||
Declaration::Database(_) if is_job => {}
|
||||
Declaration::Function(clause) => {
|
||||
let declared = matches!(clause.kind, FunctionKind::Job { .. });
|
||||
if is_job != declared {
|
||||
@@ -159,6 +160,7 @@ pub(super) fn register(
|
||||
let FunctionKind::Job {
|
||||
attempts,
|
||||
timeout_ms,
|
||||
long,
|
||||
} = entry.kind
|
||||
else {
|
||||
continue;
|
||||
@@ -210,6 +212,7 @@ pub(super) fn register(
|
||||
};
|
||||
jobs.push(Job {
|
||||
function,
|
||||
long,
|
||||
connection: connection.clone(),
|
||||
payload,
|
||||
attempts,
|
||||
|
||||
@@ -525,23 +525,19 @@ fn validate_model_declarations(
|
||||
let mut sql = BTreeSet::new();
|
||||
for declaration in &package.declarations {
|
||||
match declaration {
|
||||
Declaration::Database(binding) => {
|
||||
if database.replace(binding.span).is_some() {
|
||||
errors.push(Diagnostic::error(
|
||||
"C002",
|
||||
"a Model may declare only one database binding",
|
||||
binding.span,
|
||||
));
|
||||
}
|
||||
Declaration::Database(binding) if database.replace(binding.span).is_some() => {
|
||||
errors.push(Diagnostic::error(
|
||||
"C002",
|
||||
"a Model may declare only one database binding",
|
||||
binding.span,
|
||||
));
|
||||
}
|
||||
Declaration::Seed(value) => {
|
||||
if seed.replace(value.span).is_some() {
|
||||
errors.push(Diagnostic::error(
|
||||
"C002",
|
||||
"a Model may declare only one seed block",
|
||||
value.span,
|
||||
));
|
||||
}
|
||||
Declaration::Seed(value) if seed.replace(value.span).is_some() => {
|
||||
errors.push(Diagnostic::error(
|
||||
"C002",
|
||||
"a Model may declare only one seed block",
|
||||
value.span,
|
||||
));
|
||||
}
|
||||
Declaration::Migration(migration) => {
|
||||
if !migrations.insert(&migration.name.text) {
|
||||
|
||||
@@ -548,12 +548,7 @@ pub(super) fn referenced_types(ty: &Type, visit: &mut impl FnMut(usize)) {
|
||||
| Type::Stream(element)
|
||||
| Type::AsyncStream(element)
|
||||
| Type::RowStream(element)
|
||||
| Type::Related(element)
|
||||
| Type::Channel(element) => {
|
||||
if !matches!(ty, Type::Related(_)) {
|
||||
referenced_types(element, visit)
|
||||
}
|
||||
}
|
||||
| Type::Channel(element) => referenced_types(element, visit),
|
||||
Type::Map(key, value) | Type::MapEntry(key, value) => {
|
||||
referenced_types(key, visit);
|
||||
referenced_types(value, visit);
|
||||
|
||||
Reference in new issue
Block a user