Files
PiCloud/crates/executor-core/src/sdk/mod.rs
MechaCat02 be0c618672 feat(interceptors): ordered before-chains + cycle guard + ctx plumbing (§9.4 M1+M2)
M1: introduces a clone-cheap InterceptorCtx { interceptors, self_engine,
limits } threaded into every SDK register fn (kv/docs/files/queue/pubsub/http)
so the non-KV services carry the hook seam (unused until M7-M11); KvHandle
collapses its three fields into one ictx.

M2: replaces the nearest-only resolver with ordered before/after chains. The
trait becomes resolve(cx, service, op) -> InterceptorChain { before, after }
(migration 0074 adds a phase column + phase-aware unique indexes). The before
-chain runs ancestor->app (depth DESC) so a group compliance guard can't be
bypassed by a descendant; single-marker behavior is byte-identical to before.
An identity cycle guard (thread-local visited-set keyed by script_id) denies a
detected cycle, alongside the existing binary re-entrancy break. Fail-closed
verdict preserved (allow only on #{ allowed: true }; a Dangling entry or a
missing engine back-ref denies); app_id still derives from cx.app_id only.

after-chains resolve but stay unused until M3. Pinned by two new interceptor
journeys (ancestor->app chain ordering; self-referential no-recurse).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 22:54:51 +02:00

86 lines
3.0 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//! SDK plumbing — types and the per-call registration entry point.
//!
//! `executor-core` is responsible for building the per-invocation Rhai
//! engine and wiring stateful services into it. v1.1.0 ships the
//! shapes (`Services` bundle, `SdkCallCx`, `register_all` entry point)
//! but no actual services — subsequent v1.1.x PRs (KV in v1.1.1,
//! docs in v1.1.2, …) extend `register_all` rather than re-threading
//! plumbing through `engine.rs`.
//!
//! Bridge functions (`json_to_dynamic` / `dynamic_to_json`) also live
//! here so service modules can convert values without `engine.rs`
//! being the only home for the conversion logic.
pub mod bridge;
pub mod cx;
pub mod dead_letters;
pub mod docs;
pub mod email;
pub mod emit_budget;
pub mod files;
pub mod http;
pub mod interceptor;
pub mod invoke;
pub mod kv;
pub mod pubsub;
pub mod queue;
pub mod retry;
pub mod secrets;
pub mod stdlib;
pub mod users;
pub mod vars;
pub mod workflow;
pub use bridge::{
dynamic_to_json, dynamic_to_json_capped, json_to_dynamic, JsonSizeError,
MAX_JSON_MATERIALIZE_BYTES,
};
pub use cx::SdkCallCx;
use std::sync::Arc;
use picloud_shared::Services;
use rhai::Engine as RhaiEngine;
use crate::engine::Engine;
use crate::sandbox::Limits;
/// Single hook every v1.1.x stateful service registers into. Called
/// once per invocation, just after `build_engine` constructs the
/// sandboxed Rhai engine and just before script compilation.
///
/// v1.1.9 adds the `limits` + `self_engine` parameters needed by the
/// `invoke` bridge for synchronous re-entry. `self_engine` is `None`
/// in harnesses that didn't call `Engine::set_self_weak` after
/// construction; the invoke bridge surfaces a clear error in that case.
pub fn register_all(
engine: &mut RhaiEngine,
services: &Services,
cx: Arc<SdkCallCx>,
limits: Limits,
self_engine: Option<Arc<Engine>>,
) {
// §9.4 M1: build the interceptor ctx once and thread it into every SDK
// service that has (or will gain) a before/after hook. Only `kv` runs a
// hook today; docs/files/queue/pubsub/http carry it for M7M11.
let ictx = interceptor::InterceptorCtx {
interceptors: services.interceptors.clone(),
self_engine: self_engine.clone(),
limits,
};
kv::register(engine, services, cx.clone(), ictx.clone());
docs::register(engine, services, cx.clone(), ictx.clone());
dead_letters::register(engine, services, cx.clone());
http::register(engine, services, cx.clone(), ictx.clone());
files::register(engine, services, cx.clone(), ictx.clone());
pubsub::register(engine, services, cx.clone(), ictx.clone());
queue::register(engine, services, cx.clone(), ictx);
retry::register(engine, services, cx.clone());
secrets::register(engine, services, cx.clone());
vars::register(engine, services, cx.clone());
email::register(engine, services, cx.clone());
users::register(engine, services, cx.clone());
workflow::register(engine, services, cx.clone());
invoke::register(engine, services, cx, limits, self_engine);
}