mirror of
https://github.com/alibaba/higress.git
synced 2026-05-09 21:37:31 +08:00
feat: support wasm-assemblyscript sdk (#1175)
This commit is contained in:
@@ -0,0 +1,80 @@
|
||||
# 功能说明
|
||||
`custom-response`插件支持配置自定义的响应,包括自定义 HTTP 应答状态码、HTTP 应答头,以及 HTTP 应答 Body。可以用于 Mock 响应,也可以用于判断特定状态码后给出自定义应答,例如在触发网关限流策略时实现自定义响应。
|
||||
|
||||
# 配置字段
|
||||
|
||||
| 名称 | 数据类型 | 填写要求 | 默认值 | 描述 |
|
||||
| -------- | -------- | -------- | -------- | -------- |
|
||||
| status_code | number | 选填 | 200 | 自定义 HTTP 应答状态码 |
|
||||
| headers | array of string | 选填 | - | 自定义 HTTP 应答头,key 和 value 用`=`分隔 |
|
||||
| body | string | 选填 | - | 自定义 HTTP 应答 Body |
|
||||
| enable_on_status | array of number | 选填 | - | 匹配原始状态码,生成自定义响应,不填写时,不判断原始状态码 |
|
||||
|
||||
# 配置示例
|
||||
|
||||
## Mock 应答场景
|
||||
|
||||
```yaml
|
||||
status_code: 200
|
||||
headers:
|
||||
- Content-Type=application/json
|
||||
- Hello=World
|
||||
body: "{\"hello\":\"world\"}"
|
||||
|
||||
```
|
||||
|
||||
根据该配置,请求将返回自定义应答如下:
|
||||
|
||||
```text
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: application/json
|
||||
Hello: World
|
||||
Content-Length: 17
|
||||
|
||||
{"hello":"world"}
|
||||
```
|
||||
|
||||
## 触发限流时自定义响应
|
||||
|
||||
```yaml
|
||||
enable_on_status:
|
||||
- 429
|
||||
status_code: 302
|
||||
headers:
|
||||
- Location=https://example.com
|
||||
```
|
||||
|
||||
触发网关限流时一般会返回 `429` 状态码,这时请求将返回自定义应答如下:
|
||||
|
||||
```text
|
||||
HTTP/1.1 302 Found
|
||||
Location: https://example.com
|
||||
```
|
||||
|
||||
从而实现基于浏览器 302 重定向机制,将限流后的用户引导到其他页面,比如可以是一个 CDN 上的静态页面。
|
||||
|
||||
如果希望触发限流时,正常返回其他应答,参考 Mock 应答场景配置相应的字段即可。
|
||||
|
||||
## 对特定路由或域名开启
|
||||
```yaml
|
||||
# 使用 matchRules 字段进行细粒度规则配置
|
||||
matchRules:
|
||||
# 规则一:按 Ingress 名称匹配生效
|
||||
- ingress:
|
||||
- default/foo
|
||||
- default/bar
|
||||
body: "{\"hello\":\"world\"}"
|
||||
# 规则二:按域名匹配生效
|
||||
- domain:
|
||||
- "*.example.com"
|
||||
- test.com
|
||||
enable_on_status:
|
||||
- 429
|
||||
status_code: 200
|
||||
headers:
|
||||
- Content-Type=application/json
|
||||
body: "{\"errmsg\": \"rate limited\"}"
|
||||
```
|
||||
此例 `ingress` 中指定的 `default/foo` 和 `default/bar` 对应 default 命名空间下名为 foo 和 bar 的 Ingress,当匹配到这两个 Ingress 时,将使用此段配置;
|
||||
此例 `domain` 中指定的 `*.example.com` 和 `test.com` 用于匹配请求的域名,当发现域名匹配时,将使用此段配置;
|
||||
配置的匹配生效顺序,将按照 `matchRules` 下规则的排列顺序,匹配第一个规则后生效对应配置,后续规则将被忽略。
|
||||
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"targets": {
|
||||
"debug": {
|
||||
"outFile": "build/debug.wasm",
|
||||
"textFile": "build/debug.wat",
|
||||
"sourceMap": true,
|
||||
"debug": true
|
||||
},
|
||||
"release": {
|
||||
"outFile": "build/release.wasm",
|
||||
"textFile": "build/release.wat",
|
||||
"sourceMap": true,
|
||||
"optimizeLevel": 3,
|
||||
"shrinkLevel": 0,
|
||||
"converge": false,
|
||||
"noAssert": false,
|
||||
"debug": true
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"bindings": "esm",
|
||||
"use": "abort=abort_proc_exit"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
export * from "@higress/proxy-wasm-assemblyscript-sdk/assembly/proxy";
|
||||
import { SetCtx, HttpContext, ProcessRequestHeadersBy, Logger, ParseConfigBy, ParseResult, ProcessResponseHeadersBy } from "@higress/wasm-assemblyscript/assembly";
|
||||
import { FilterHeadersStatusValues, Headers, send_http_response, stream_context, HeaderPair } from "@higress/proxy-wasm-assemblyscript-sdk/assembly"
|
||||
import { JSON } from "assemblyscript-json/assembly";
|
||||
|
||||
class CustomResponseConfig {
|
||||
statusCode: u32;
|
||||
headers: Headers;
|
||||
body: ArrayBuffer;
|
||||
enableOnStatus: Array<u32>;
|
||||
contentType: string;
|
||||
constructor() {
|
||||
this.statusCode = 200;
|
||||
this.headers = [];
|
||||
this.body = new ArrayBuffer(0);
|
||||
this.enableOnStatus = [];
|
||||
this.contentType = "text/plain; charset=utf-8";
|
||||
}
|
||||
}
|
||||
|
||||
SetCtx<CustomResponseConfig>(
|
||||
"custom-response",
|
||||
[ParseConfigBy<CustomResponseConfig>(parseConfig),
|
||||
ProcessRequestHeadersBy<CustomResponseConfig>(onHttpRequestHeaders),
|
||||
ProcessResponseHeadersBy<CustomResponseConfig>(onHttpResponseHeaders),])
|
||||
|
||||
function parseConfig(json: JSON.Obj): ParseResult<CustomResponseConfig> {
|
||||
let headersArray = json.getArr("headers");
|
||||
let config = new CustomResponseConfig();
|
||||
if (headersArray != null) {
|
||||
for (let i = 0; i < headersArray.valueOf().length; i++) {
|
||||
let header = headersArray._arr[i];
|
||||
let jsonString = (<JSON.Str>header).toString()
|
||||
let kv = jsonString.split("=")
|
||||
if (kv.length == 2) {
|
||||
let key = kv[0].trim();
|
||||
let value = kv[1].trim();
|
||||
if (key.toLowerCase() == "content-type") {
|
||||
config.contentType = value;
|
||||
} else if (key.toLowerCase() == "content-length") {
|
||||
continue;
|
||||
} else {
|
||||
config.headers.push(new HeaderPair(String.UTF8.encode(key), String.UTF8.encode(value)));
|
||||
}
|
||||
} else {
|
||||
Logger.Error("parse header failed");
|
||||
return new ParseResult<CustomResponseConfig>(null, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
let body = json.getString("body");
|
||||
if (body != null) {
|
||||
config.body = String.UTF8.encode(body.valueOf());
|
||||
}
|
||||
config.headers.push(new HeaderPair(String.UTF8.encode("content-type"), String.UTF8.encode(config.contentType)));
|
||||
|
||||
let statusCode = json.getInteger("statusCode");
|
||||
if (statusCode != null) {
|
||||
config.statusCode = statusCode.valueOf() as u32;
|
||||
}
|
||||
|
||||
let enableOnStatus = json.getArr("enableOnStatus");
|
||||
|
||||
if (enableOnStatus != null) {
|
||||
for (let i = 0; i < enableOnStatus.valueOf().length; i++) {
|
||||
let status = enableOnStatus._arr[i];
|
||||
if (status.isInteger) {
|
||||
config.enableOnStatus.push((<JSON.Integer>status).valueOf() as u32);
|
||||
}
|
||||
}
|
||||
}
|
||||
return new ParseResult<CustomResponseConfig>(config, true);
|
||||
}
|
||||
|
||||
function onHttpRequestHeaders(context: HttpContext, config: CustomResponseConfig): FilterHeadersStatusValues {
|
||||
if (config.enableOnStatus.length != 0) {
|
||||
return FilterHeadersStatusValues.Continue;
|
||||
}
|
||||
send_http_response(config.statusCode, "custom-response", config.body, config.headers);
|
||||
return FilterHeadersStatusValues.StopIteration;
|
||||
}
|
||||
|
||||
function onHttpResponseHeaders(context: HttpContext, config: CustomResponseConfig): FilterHeadersStatusValues {
|
||||
let statusCodeStr = stream_context.headers.response.get(":status")
|
||||
if (statusCodeStr == "") {
|
||||
Logger.Error("get http response status code failed");
|
||||
return FilterHeadersStatusValues.Continue;
|
||||
}
|
||||
let statusCode = parseInt(statusCodeStr);
|
||||
for (let i = 0; i < config.enableOnStatus.length; i++) {
|
||||
if (statusCode == config.enableOnStatus[i]) {
|
||||
send_http_response(config.statusCode, "custom-response", config.body, config.headers);
|
||||
}
|
||||
}
|
||||
return FilterHeadersStatusValues.Continue;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"extends": "assemblyscript/std/assembly.json",
|
||||
"include": [
|
||||
"./**/*.ts"
|
||||
]
|
||||
}
|
||||
75
plugins/wasm-assemblyscript/extensions/custom-response/package-lock.json
generated
Normal file
75
plugins/wasm-assemblyscript/extensions/custom-response/package-lock.json
generated
Normal file
@@ -0,0 +1,75 @@
|
||||
{
|
||||
"name": "custom-response",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "custom-response",
|
||||
"version": "1.0.0",
|
||||
"license": "ISC",
|
||||
"devDependencies": {
|
||||
"@higress/proxy-wasm-assemblyscript-sdk": "^0.0.2",
|
||||
"@higress/wasm-assemblyscript": "^0.0.3",
|
||||
"assemblyscript": "^0.27.29",
|
||||
"assemblyscript-json": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@higress/proxy-wasm-assemblyscript-sdk": {
|
||||
"version": "0.0.2",
|
||||
"resolved": "https://registry.npmmirror.com/@higress/proxy-wasm-assemblyscript-sdk/-/proxy-wasm-assemblyscript-sdk-0.0.2.tgz",
|
||||
"integrity": "sha512-0J1tFJMTE6o37JpGJBLq0wc5kBC/fpbISrP+KFb4bAEeshu6daXzD2P3bAfJXmW+oZdY0WGptTGXWx8pf9Fk+g==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@higress/wasm-assemblyscript": {
|
||||
"version": "0.0.3",
|
||||
"resolved": "https://registry.npmmirror.com/@higress/wasm-assemblyscript/-/wasm-assemblyscript-0.0.3.tgz",
|
||||
"integrity": "sha512-D9hTvjAt54WoedNBsYAp9q/mDPWOO9yoGY7yG7Gkgp3KB7O5lHEEu2T6V8K14DpfC8ObSP26EhBcJ6G70JjODg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/assemblyscript": {
|
||||
"version": "0.27.29",
|
||||
"resolved": "https://registry.npmmirror.com/assemblyscript/-/assemblyscript-0.27.29.tgz",
|
||||
"integrity": "sha512-pH6udb7aE2F0t6cTh+0uCepmucykhMnAmm7k0kkAU3SY7LvpIngEBZWM6p5VCguu4EpmKGwEuZpZbEXzJ/frHQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"binaryen": "116.0.0-nightly.20240114",
|
||||
"long": "^5.2.1"
|
||||
},
|
||||
"bin": {
|
||||
"asc": "bin/asc.js",
|
||||
"asinit": "bin/asinit.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16",
|
||||
"npm": ">=7"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/assemblyscript"
|
||||
}
|
||||
},
|
||||
"node_modules/assemblyscript-json": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/assemblyscript-json/-/assemblyscript-json-1.1.0.tgz",
|
||||
"integrity": "sha512-UbE8ts8csTWQgd5TnSPN7MRV9NveuHv1bVnKmDLoo/tzjqxkmsZb3lu59Uk8H7SGoqdkDSEE049alx/nHnSdFw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/binaryen": {
|
||||
"version": "116.0.0-nightly.20240114",
|
||||
"resolved": "https://registry.npmmirror.com/binaryen/-/binaryen-116.0.0-nightly.20240114.tgz",
|
||||
"integrity": "sha512-0GZrojJnuhoe+hiwji7QFaL3tBlJoA+KFUN7ouYSDGZLSo9CKM8swQX8n/UcbR0d1VuZKU+nhogNzv423JEu5A==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"wasm-opt": "bin/wasm-opt",
|
||||
"wasm2js": "bin/wasm2js"
|
||||
}
|
||||
},
|
||||
"node_modules/long": {
|
||||
"version": "5.2.3",
|
||||
"resolved": "https://registry.npmmirror.com/long/-/long-5.2.3.tgz",
|
||||
"integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"name": "custom-response",
|
||||
"version": "1.0.0",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "node tests",
|
||||
"asbuild:debug": "asc assembly/index.ts --target debug",
|
||||
"asbuild:release": "asc assembly/index.ts --target release",
|
||||
"asbuild": "npm run asbuild:debug && npm run asbuild:release",
|
||||
"start": "npx serve ."
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"description": "",
|
||||
"devDependencies": {
|
||||
"assemblyscript": "^0.27.29",
|
||||
"assemblyscript-json": "^1.1.0",
|
||||
"@higress/proxy-wasm-assemblyscript-sdk": "^0.0.2",
|
||||
"@higress/wasm-assemblyscript": "^0.0.3"
|
||||
},
|
||||
"type": "module",
|
||||
"exports": {
|
||||
".": {
|
||||
"import": "./build/release.js",
|
||||
"types": "./build/release.d.ts"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"targets": {
|
||||
"debug": {
|
||||
"outFile": "build/debug.wasm",
|
||||
"textFile": "build/debug.wat",
|
||||
"sourceMap": true,
|
||||
"debug": true
|
||||
},
|
||||
"release": {
|
||||
"outFile": "build/release.wasm",
|
||||
"textFile": "build/release.wat",
|
||||
"sourceMap": true,
|
||||
"optimizeLevel": 3,
|
||||
"shrinkLevel": 0,
|
||||
"converge": false,
|
||||
"noAssert": false,
|
||||
"debug": true
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"bindings": "esm",
|
||||
"use": "abort=abort_proc_exit"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
export * from "@higress/proxy-wasm-assemblyscript-sdk/assembly/proxy";
|
||||
import { SetCtx, HttpContext, ProcessRequestHeadersBy, Logger, ParseResult, ParseConfigBy, RegisteTickFunc, ProcessResponseHeadersBy } from "@higress/wasm-assemblyscript/assembly";
|
||||
import { FilterHeadersStatusValues, send_http_response, stream_context } from "@higress/proxy-wasm-assemblyscript-sdk/assembly"
|
||||
import { JSON } from "assemblyscript-json/assembly";
|
||||
class HelloWorldConfig {
|
||||
}
|
||||
|
||||
SetCtx<HelloWorldConfig>("hello-world",
|
||||
[ParseConfigBy<HelloWorldConfig>(parseConfig),
|
||||
ProcessRequestHeadersBy<HelloWorldConfig>(onHttpRequestHeaders),
|
||||
ProcessResponseHeadersBy<HelloWorldConfig>(onHttpResponseHeaders)
|
||||
])
|
||||
|
||||
function parseConfig(json: JSON.Obj): ParseResult<HelloWorldConfig> {
|
||||
RegisteTickFunc(2000, () => {
|
||||
Logger.Debug("tick 2s");
|
||||
})
|
||||
RegisteTickFunc(5000, () => {
|
||||
Logger.Debug("tick 5s");
|
||||
})
|
||||
return new ParseResult<HelloWorldConfig>(new HelloWorldConfig(), true);
|
||||
}
|
||||
|
||||
class TestContext{
|
||||
value: string
|
||||
constructor(value: string){
|
||||
this.value = value
|
||||
}
|
||||
}
|
||||
function onHttpRequestHeaders(context: HttpContext, config: HelloWorldConfig): FilterHeadersStatusValues {
|
||||
stream_context.headers.request.add("hello", "world");
|
||||
Logger.Debug("[hello-world] logger test");
|
||||
context.SetContext("test-set-context", changetype<usize>(new TestContext("value")))
|
||||
send_http_response(200, "hello-world", String.UTF8.encode("[wasm-assemblyscript]hello world"), []);
|
||||
return FilterHeadersStatusValues.Continue;
|
||||
}
|
||||
|
||||
function onHttpResponseHeaders(context: HttpContext, config: HelloWorldConfig): FilterHeadersStatusValues {
|
||||
const str = changetype<TestContext>(context.GetContext("test-set-context")).value;
|
||||
Logger.Debug("[hello-world] test-set-context: " + str);
|
||||
return FilterHeadersStatusValues.Continue;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"extends": "assemblyscript/std/assembly.json",
|
||||
"include": [
|
||||
"./**/*.ts"
|
||||
]
|
||||
}
|
||||
75
plugins/wasm-assemblyscript/extensions/hello-world/package-lock.json
generated
Normal file
75
plugins/wasm-assemblyscript/extensions/hello-world/package-lock.json
generated
Normal file
@@ -0,0 +1,75 @@
|
||||
{
|
||||
"name": "hello-world",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "hello-world",
|
||||
"version": "1.0.0",
|
||||
"license": "ISC",
|
||||
"devDependencies": {
|
||||
"@higress/proxy-wasm-assemblyscript-sdk": "^0.0.2",
|
||||
"@higress/wasm-assemblyscript": "^0.0.3",
|
||||
"assemblyscript": "^0.27.29",
|
||||
"assemblyscript-json": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@higress/proxy-wasm-assemblyscript-sdk": {
|
||||
"version": "0.0.2",
|
||||
"resolved": "https://registry.npmmirror.com/@higress/proxy-wasm-assemblyscript-sdk/-/proxy-wasm-assemblyscript-sdk-0.0.2.tgz",
|
||||
"integrity": "sha512-0J1tFJMTE6o37JpGJBLq0wc5kBC/fpbISrP+KFb4bAEeshu6daXzD2P3bAfJXmW+oZdY0WGptTGXWx8pf9Fk+g==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@higress/wasm-assemblyscript": {
|
||||
"version": "0.0.3",
|
||||
"resolved": "https://registry.npmmirror.com/@higress/wasm-assemblyscript/-/wasm-assemblyscript-0.0.3.tgz",
|
||||
"integrity": "sha512-D9hTvjAt54WoedNBsYAp9q/mDPWOO9yoGY7yG7Gkgp3KB7O5lHEEu2T6V8K14DpfC8ObSP26EhBcJ6G70JjODg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/assemblyscript": {
|
||||
"version": "0.27.29",
|
||||
"resolved": "https://registry.npmmirror.com/assemblyscript/-/assemblyscript-0.27.29.tgz",
|
||||
"integrity": "sha512-pH6udb7aE2F0t6cTh+0uCepmucykhMnAmm7k0kkAU3SY7LvpIngEBZWM6p5VCguu4EpmKGwEuZpZbEXzJ/frHQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"binaryen": "116.0.0-nightly.20240114",
|
||||
"long": "^5.2.1"
|
||||
},
|
||||
"bin": {
|
||||
"asc": "bin/asc.js",
|
||||
"asinit": "bin/asinit.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16",
|
||||
"npm": ">=7"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/assemblyscript"
|
||||
}
|
||||
},
|
||||
"node_modules/assemblyscript-json": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/assemblyscript-json/-/assemblyscript-json-1.1.0.tgz",
|
||||
"integrity": "sha512-UbE8ts8csTWQgd5TnSPN7MRV9NveuHv1bVnKmDLoo/tzjqxkmsZb3lu59Uk8H7SGoqdkDSEE049alx/nHnSdFw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/binaryen": {
|
||||
"version": "116.0.0-nightly.20240114",
|
||||
"resolved": "https://registry.npmmirror.com/binaryen/-/binaryen-116.0.0-nightly.20240114.tgz",
|
||||
"integrity": "sha512-0GZrojJnuhoe+hiwji7QFaL3tBlJoA+KFUN7ouYSDGZLSo9CKM8swQX8n/UcbR0d1VuZKU+nhogNzv423JEu5A==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"wasm-opt": "bin/wasm-opt",
|
||||
"wasm2js": "bin/wasm2js"
|
||||
}
|
||||
},
|
||||
"node_modules/long": {
|
||||
"version": "5.2.3",
|
||||
"resolved": "https://registry.npmmirror.com/long/-/long-5.2.3.tgz",
|
||||
"integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"name": "hello-world",
|
||||
"version": "1.0.0",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "node tests",
|
||||
"asbuild:debug": "asc assembly/index.ts --target debug",
|
||||
"asbuild:release": "asc assembly/index.ts --target release",
|
||||
"asbuild": "npm run asbuild:debug && npm run asbuild:release",
|
||||
"start": "npx serve ."
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"description": "",
|
||||
"devDependencies": {
|
||||
"assemblyscript": "^0.27.29",
|
||||
"assemblyscript-json": "^1.1.0",
|
||||
"@higress/proxy-wasm-assemblyscript-sdk": "^0.0.2",
|
||||
"@higress/wasm-assemblyscript": "^0.0.3"
|
||||
},
|
||||
"type": "module",
|
||||
"exports": {
|
||||
".": {
|
||||
"import": "./build/release.js",
|
||||
"types": "./build/release.d.ts"
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user