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>
86 lines
3.0 KiB
Rust
86 lines
3.0 KiB
Rust
//! 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 M7–M11.
|
||
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);
|
||
}
|