@ 耐用/ 变形
Cloudflare 持久对象和工人服务装订 RPC 的打入式呼叫器和呼叫器转换.
安装
npm install @durability/transforms
虚拟设置
Vite 插件需要 Wrangler , 读取其已验证的绑定配置, 包装可持久对象的根和所有其他对象绑定( 服务, KV, R2, Workflows, 队列等) , 生成添加声明 .with(...) 他们的怒火型。 瓦斯,秘密, 和模块blobs没有被触及。 诸如交叉工人服务等无法推断的Wrangler类型的捆绑物在运行时被包裹,但保留其手写声明.
const value = await env.CACHE.with(timeout, 1_000).get('key');
await env.MEDIA.with(retry, 2).put('file.txt', body);
const instance = await env.JOBS.with(timeout, 5_000).create({ params });
呼叫器只在客户端上运行 。 被调用者的变换和调用语境仍然需要您拥有的持久对象或工人 Entry 点 .
不进行变换,被包裹的捆绑行为与原作完全相同,包括同步方法和RPC承诺活线. 变换后,每一次呼叫都返回一个承诺 仍然支持管道连接,例如 stub.with(timeout, 1_000).counter().increment();连锁呼叫等待被改变的结果,而不是分享其来回. 关于返回物体的方法,例如: WorkflowInstance,没有改变.
对于 DurabilityRouting.client({...}) 财务报告和审定财务报表 DurabilitySemaphore.client({...}),插件注入 exportName 从目标持久对象的同模块导出。 目标必须是该模块输出的本地类,并被宣布为Wrangler本地持久对象类(包括迁移类). 支持导出别名 :
import { DurableObject } from 'cloudflare:workers';
import { DurabilityRouting as Routing } from 'durability/routing';
class InternalMailbox extends DurableObject {
enqueue(input: string) {}
}
export { InternalMailbox as Mailbox };
const router = Routing.client({
target: InternalMailbox,
invoke: (stub, input) => stub.enqueue(input),
...defaults,
});
结果 exportName 附加在其它属性后,所以扩展不能覆盖它。 查取故意限于在同一工人模块中可见的类申报和出口;进口类,再出口,动态目标,以及非入行配置在目标可见时被单独或拒绝. 设定 exportName 很明显是那些案子的逃生口 明确 exportName 值永远不会改变.
import { defineConfig } from 'vite';
import { doTransforms } from '@durability/transforms/vite';
export default defineConfig({
plugins: [doTransforms({ wrangler: './wrangler.jsonc' })],
});
生成的宣告默认为 do-transforms.generated.d.ts 在万克勒旁边 main 模块。 在应用程序的 TypeScript 项目中包含该文件 .
恢复已断裂的持久对象根
许多持久对象出错留下了根断裂,因此每次通过它发出的呼叫都会以相同的出错而失败. 启用 restoreBrokenStubs 重写插件的命名空间 get 财务报告和审定财务报表 getByName 调用后, 产生的分接点将替换为:
doTransforms({ wrangler: './wrangler.jsonc', restoreBrokenStubs: true });
失败的呼唤仍然拒绝,并且没有被重审. 下一个调用从命名空间获得一个与原参数相同的新分位符,这些参数只被评估一次,所以 env.MY_DO.get(env.MY_DO.newUniqueId()) 继续位于同一对象。 替换发生在呼叫器变换链的末端,所以每次尝试都是由 retry 运行在新鲜的树枝上。 没有插件, 使用 createRestorableStubFactory(env.MY_DO, 'getByName')(name)。 。 。 .
定义并安装变换器
import { DurableObject } from 'cloudflare:workers';
import {
applyTransforms,
createTransformContextTarget,
defineTransform,
registerTransform,
} from '@durability/transforms';
type RequestContext = { requestId?: string };
class ExampleObject extends DurableObject {
setContext(context: RequestContext) {
return createTransformContextTarget(this, context);
}
async greet(name: string) {
return `Hello, ${name}`;
}
}
const observability = defineTransform<ExampleObject, RequestContext>()
.caller(
(requestId: string) =>
async ({ next }) =>
next({ context: { requestId } })
)
.callee((metricName: string) => async ({ context, next }) => {
console.log(metricName, context.requestId);
return next();
});
applyTransforms(ExampleObject, {
methods: {
greet: [registerTransform(observability, 'greet_calls')],
},
});
const stub = env.EXAMPLE.getByName('example').with(
observability,
crypto.randomUUID()
);
await stub.greet('Ada');
applyTransforms 变异类原型并被累积. 在模块初始化时调用一次, 而不是每个请求或实例 .
为持久对象类定义的调用器变换也收到 actorId,则 DurableObjectId 中标的物体。 为其他目标定义的变换,例如: defineTransform<object>(),不看到字段,而永久对象转换不能附加在不是永久对象根的绑定上。 身份证留在呼叫者身上,不发给被呼叫者:
const actorLog = defineTransform<ExampleObject>().caller(
() =>
async ({ actorId, method, next }) => {
console.log('rpc', method, actorId.toString());
return next();
}
);
await env.EXAMPLE.getByName('example').with(actorLog).greet('Ada');
变换上下文是不信任的调用元数据. 它适合申请身份证、追查和暗示,但不能被接受为身份、租赁、角色或授权的证据。 由独立认证的被调用人数据衍生授权.
内置呼叫器变换
import { retry, timeout } from '@durability/transforms';
const value = await stub
.with(timeout, 5_000)
.with(retry, {
retries: 3,
delay: ({ attempt }) => Math.min(1_000 * 2 ** (attempt - 1), 30_000),
})
.read();
呼叫者暂停等待, 但不会取消远程操作 。 每一次重试都是新的RPC引用,因此重试只使用"一能"方法或通过"一能"键. 何时 delay 被省略,重试时使用被盖住的指数后退并全焦.
其他内置设施包括:
betterResultCodec用于序列化和再水合better-result在一个版本信封中的值。 遗留的0.1.0有效载荷因形状模糊而默认被否决;临时设定acceptLegacy: true只有在与一个年长的被调用者沟通时.errorBoundary将调用器可见的抛出转换为“更好的结果”出错.abortAsSuccess将预期持久对象重置为未定义的成功结果.largeObjectStream以传输大型 JSON 对象结果。 它需要一个标准Schema验证器,并强制使用一个调用器解码限制,但它仍然缓冲了完整的序列化表示;使用本土流进行无约束的数据.
手工包装
当 Vite 插件无法使用时, 直接包入目标或命名空间 :
import {
createTransformStub,
timeout,
withTransforms,
} from '@durability/transforms';
const service = createTransformStub(env.MY_SERVICE).with(timeout, 5_000);
const namespace = withTransforms(env.MY_DURABLE_OBJECT);
const id = namespace.idFromName('example');
const object = namespace.get(id).with(timeout, 5_000);