diff --git a/CHANGELOG.md b/CHANGELOG.md index 37af25c..454cad2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ # CHANGELOG +## v1.3.3 (2026-05-30) +- **新增**: 注册到 `jsmod`,提供 `call` 和 `load` 能力。支持在 JS 环境中通过简洁的对象传参发起 API 编排调用。 + ## v1.0.3 (2026-05-09) ### Security diff --git a/go.mod b/go.mod index 38faa7f..aaa088e 100644 --- a/go.mod +++ b/go.mod @@ -8,6 +8,7 @@ require ( apigo.cc/go/crypto v1.3.1 apigo.cc/go/encoding v1.3.1 apigo.cc/go/http v1.3.2 + apigo.cc/go/jsmod v1.0.1 apigo.cc/go/safe v1.3.1 ) diff --git a/go.sum b/go.sum index b97531d..44042a8 100644 --- a/go.sum +++ b/go.sum @@ -12,6 +12,8 @@ apigo.cc/go/http v1.3.2 h1:0Or5KfoIq4+yeWKYusYPV8XLPw8XuzJMeaFv7dZViLI= apigo.cc/go/http v1.3.2/go.mod h1:Q9R7Ors0Fz2A6Mxg0dykO2PjCzdAHRRXreOUMjMOLwA= apigo.cc/go/id v1.3.1 h1:pkqi6VeWyQoHuIu0Zbx/RRxIAdM61Js0j6cY1M9XVCk= apigo.cc/go/id v1.3.1/go.mod h1:P2/vl3tyW3US+ayOFSMoPIOCulNLBngNYPhXJC/Z7J4= +apigo.cc/go/jsmod v1.0.1 h1:vaz3cMQi75UVoALLfyV/Trs8iP/Nh28yN57IvBFpPGk= +apigo.cc/go/jsmod v1.0.1/go.mod h1:bmyeZtOAP/j5am+YRnaiM89smysK24K7ebk0koFtsSw= apigo.cc/go/log v1.3.4 h1:UT8Neb9r4QjjbCFbTzw+ZeTxd+DmdmR5gNExeR4Cj+g= apigo.cc/go/log v1.3.4/go.mod h1:/Q/2r51xWSsrS4QN5U9jLiTw8n6qNC8kG9nuVHweY20= apigo.cc/go/rand v1.3.1 h1:7FvsI6PtQ5XrWER0dTiLVo0p7GIxRidT/TBKhVy93j8= diff --git a/js_export.go b/js_export.go new file mode 100644 index 0000000..a470667 --- /dev/null +++ b/js_export.go @@ -0,0 +1,91 @@ +package api + +import ( + "context" + + "apigo.cc/go/cast" + "apigo.cc/go/jsmod" +) + +func init() { + jsmod.Register("api", map[string]any{ + "call": CallAny, + "load": Load, + }) +} + +func CallAny(ctx context.Context, actionName string, payload any, options ...map[string]any) (any, error) { + var opt map[string]any + if len(options) > 0 { + opt = options[0] + } + + if payload == nil { + payload = make(map[string]any) + } + + // 1. Pre-fill the payload from GlobalConfigs (manually doing what api.Call does later) + actionConfig, safeBufs := GetActionConfig(actionName) + defer func() { + for _, sb := range safeBufs { + sb.Close() + } + }() + fill(payload, actionConfig) + + // 2. Wrap into jsAction for interface satisfaction + ja := &jsAction{ + name: actionName, + payload: payload, + options: opt, + } + + return Call[any](ja) +} + +// jsAction implements Action, SignerAction, URLAction, MethodAction, and ConfigurableAction. +// It redirects reflection-based 'fill' and 'json.Marshal' to its internal payload. +type jsAction struct { + name string + payload any + options map[string]any +} + +func (a *jsAction) ActionName() string { return a.name } + +func (a *jsAction) SignerName() string { + if a.options != nil { + if s := cast.String(a.options["signer"]); s != "" { + return s + } + } + return "" +} + +func (a *jsAction) GetURL() string { + if a.options != nil { + if u := cast.String(a.options["url"]); u != "" { + return u + } + } + return "" +} + +func (a *jsAction) GetMethod() string { + if a.options != nil { + if m := cast.String(a.options["method"]); m != "" { + return m + } + } + return "" +} + +func (a *jsAction) Config() map[string]any { + return a.options +} + +// MarshalJSON ensures that when this action is sent via HTTP (as JSON), +// only the internal payload is sent, not the metadata fields. +func (a *jsAction) MarshalJSON() ([]byte, error) { + return cast.ToJSONBytes(a.payload) +}