UNPKG

restate-for-dummies

Version:

Type-safe Restate SDK wrapper with simplified API

390 lines (377 loc) 14.8 kB
// src/typed-object.ts import * as restate from "@restatedev/restate-sdk"; // src/client-proxy.ts import { Opts, rpc, SendOpts } from "@restatedev/restate-sdk"; import { Opts as IngressOpts, rpc as ingressRpc, SendOpts as IngressSendOpts } from "@restatedev/restate-sdk-clients"; function isOpts(obj) { return obj instanceof Opts; } function isSendOpts(obj) { return obj instanceof SendOpts; } function isIngressOpts(obj) { return obj instanceof IngressOpts; } function isIngressSendOpts(obj) { return obj instanceof IngressSendOpts; } function createArgsHandler(rpcModule, optsType, typeGuard, getExistingOpts) { return (args, serde) => { if (args.length === 0) { const opts2 = optsType === "call" ? rpcModule.opts({ input: serde, output: serde }) : rpcModule.sendOpts({ input: serde }); return [{}, opts2]; } const lastArg = args[args.length - 1]; if (typeGuard(lastArg)) { const existingOpts = getExistingOpts(lastArg); const mergedOpts = optsType === "call" ? rpcModule.opts({ ...existingOpts, input: existingOpts.input || serde, output: existingOpts.output || serde }) : rpcModule.sendOpts({ ...existingOpts, input: existingOpts.input || serde }); return [...args.slice(0, -1), mergedOpts]; } const opts = optsType === "call" ? rpcModule.opts({ input: serde, output: serde }) : rpcModule.sendOpts({ input: serde }); return [...args, opts]; }; } var handleCallArgs = createArgsHandler(rpc, "call", isOpts, (opts) => opts.getOpts()); var handleSendArgs = createArgsHandler(rpc, "send", isSendOpts, (opts) => opts.getOpts()); var handleIngressCallArgs = createArgsHandler(ingressRpc, "call", isIngressOpts, (opts) => opts.opts); var handleIngressSendArgs = createArgsHandler(ingressRpc, "send", isIngressSendOpts, (opts) => opts.opts); function createClientWrapper(client, serde, argsHandler, preserveThis = false) { return new Proxy(client, { get(target, prop, receiver) { const value = Reflect.get(target, prop, receiver); if (typeof value !== "function") { return value; } if (preserveThis) { return function(...args) { const processedArgs = argsHandler(args, serde); return value.apply(this || target, processedArgs); }; } else { return (...args) => { const processedArgs = argsHandler(args, serde); return value.apply(target, processedArgs); }; } } }); } function wrapClient(client, serde) { return createClientWrapper(client, serde, handleCallArgs); } function wrapSendClient(client, serde) { return createClientWrapper(client, serde, handleSendArgs); } function wrapIngressClient(client, serde) { return createClientWrapper(client, serde, handleIngressCallArgs, true); } function wrapIngressWorkflowClient(client, serde) { return createClientWrapper(client, serde, handleIngressCallArgs); } function wrapIngressSendClient(client, serde) { return createClientWrapper(client, serde, handleIngressSendArgs); } // src/client-wrapper.ts function createServiceClient(ctx, service, serde) { const rawClient = ctx.serviceClient(service); return wrapClient(rawClient, serde); } function createObjectClient(ctx, object, key, serde) { const rawClient = ctx.objectClient(object, key); return wrapClient(rawClient, serde); } function createWorkflowClient(ctx, workflow, key, serde) { const rawClient = ctx.workflowClient(workflow, key); return wrapClient(rawClient, serde); } function createServiceSendClient(ctx, service, serde) { const rawClient = ctx.serviceSendClient(service); return wrapSendClient(rawClient, serde); } function createObjectSendClient(ctx, object, key, serde) { const rawClient = ctx.objectSendClient(object, key); return wrapSendClient(rawClient, serde); } function createWorkflowSendClient(ctx, workflow, key, serde) { const rawClient = ctx.workflowSendClient(workflow, key); return wrapSendClient(rawClient, serde); } // src/utils.ts var run = (ctx, name, action, serde, opts) => ctx.run(name, action, { ...opts, serde }); var get = (ctx, key, serde) => ctx.get(key, serde); var set = (ctx, key, value, serde) => ctx.set(key, value, serde); // src/typed-object.ts function typedObject(name, SerdeClass) { return (handlers2) => { const transformedHandlers = {}; const serde = new SerdeClass; for (const [key, handlerFn] of Object.entries(handlers2)) { transformedHandlers[key] = restate.handlers.object.exclusive({ input: new SerdeClass, output: new SerdeClass }, async (ctx, ...args) => { const getState = (key2, opts) => get(ctx, key2, new SerdeClass); const setState = (key2, value, opts) => set(ctx, key2, value, new SerdeClass); const clearState = (key2) => ctx.clear(key2); const runStep = (name2, action, opts) => run(ctx, name2, action, new SerdeClass, opts); const context = { clearState, ctx, getState, runStep, setState, service: (service) => createServiceClient(ctx, service, serde), serviceSend: (service) => createServiceSendClient(ctx, service, serde), object: (object2, key2) => createObjectClient(ctx, object2, key2, serde), objectSend: (object2, key2) => createObjectSendClient(ctx, object2, key2, serde), workflow: (workflow, key2) => createWorkflowClient(ctx, workflow, key2, serde), workflowSend: (workflow, key2) => createWorkflowSendClient(ctx, workflow, key2, serde) }; return handlerFn(context, ...args); }); } return restate.object({ name, handlers: transformedHandlers }); }; } // src/typed-service.ts import * as restate2 from "@restatedev/restate-sdk"; function createRestateService(name, SerdeClass) { const serde = new SerdeClass; return (handlers3) => { const transformedHandlers = {}; for (const [key, handlerFn] of Object.entries(handlers3)) { transformedHandlers[key] = restate2.handlers.handler({ input: new SerdeClass, output: new SerdeClass }, async (ctx, ...args) => { const runStep = (name2, action, opts) => run(ctx, name2, action, new SerdeClass, opts); const context = { ctx, runStep, service: (service3) => createServiceClient(ctx, service3, serde), serviceSend: (service3) => createServiceSendClient(ctx, service3, serde), object: (object2, key2) => createObjectClient(ctx, object2, key2, serde), objectSend: (object2, key2) => createObjectSendClient(ctx, object2, key2, serde), workflow: (workflow, key2) => createWorkflowClient(ctx, workflow, key2, serde), workflowSend: (workflow, key2) => createWorkflowSendClient(ctx, workflow, key2, serde) }; return handlerFn(context, ...args); }); } const service2 = restate2.service({ name, handlers: transformedHandlers }); return service2; }; } // src/typed-workflow.ts import * as restate3 from "@restatedev/restate-sdk"; function createRestateWorkflow(name, SerdeClass) { return (inputHandlers) => { const serde = new SerdeClass; const { run: runHandler, ...sharedHandlers } = inputHandlers; const transformedRunHandler = restate3.handlers.workflow.workflow({ input: new SerdeClass, output: new SerdeClass }, async (ctx, ...args) => { const getState = (key, opts) => get(ctx, key, new SerdeClass); const setState = (key, value, opts) => set(ctx, key, value, new SerdeClass); const clearState = (key) => ctx.clear(key); const runStep = (name2, action, opts) => run(ctx, name2, action, new SerdeClass, opts); const context = { clearState, ctx, getState, setState, runStep, service: (service2) => createServiceClient(ctx, service2, serde), serviceSend: (service2) => createServiceSendClient(ctx, service2, serde), object: (object2, key) => createObjectClient(ctx, object2, key, serde), objectSend: (object2, key) => createObjectSendClient(ctx, object2, key, serde), workflow: (workflow2, key) => createWorkflowClient(ctx, workflow2, key, serde), workflowSend: (workflow2, key) => createWorkflowSendClient(ctx, workflow2, key, serde) }; return runHandler(context, ...args); }); const transformedSharedHandlers = {}; for (const [key, handlerFn] of Object.entries(sharedHandlers)) { transformedSharedHandlers[key] = restate3.handlers.workflow.shared({ input: new SerdeClass, output: new SerdeClass }, async (ctx, ...args) => { const getState = (key2, opts) => get(ctx, key2, new SerdeClass); const runStep = (name2, action, opts) => run(ctx, name2, action, new SerdeClass, opts); const context = { ctx, getState, runStep, service: (service2) => createServiceClient(ctx, service2, serde), serviceSend: (service2) => createServiceSendClient(ctx, service2, serde), object: (object2, key2) => createObjectClient(ctx, object2, key2, serde), objectSend: (object2, key2) => createObjectSendClient(ctx, object2, key2, serde), workflow: (workflow2, key2) => createWorkflowClient(ctx, workflow2, key2, serde), workflowSend: (workflow2, key2) => createWorkflowSendClient(ctx, workflow2, key2, serde) }; return handlerFn(context, ...args); }); } const transformedHandlers = { run: transformedRunHandler, ...transformedSharedHandlers }; return restate3.workflow({ name, handlers: transformedHandlers }); }; } // src/standalone-clients.ts import * as restate4 from "@restatedev/restate-sdk-clients"; function createServiceClient2(service2, serde, url) { const ingress = restate4.connect({ url }); const rawClient = ingress.serviceClient(service2); return wrapIngressClient(rawClient, serde); } function createServiceSendClient2(service2, serde, url) { const ingress = restate4.connect({ url }); const rawClient = ingress.serviceSendClient(service2); return wrapIngressSendClient(rawClient, serde); } function createObjectClient2(object2, key, serde, url) { const ingress = restate4.connect({ url }); const rawClient = ingress.objectClient(object2, key); return wrapIngressClient(rawClient, serde); } function createObjectSendClient2(object2, key, serde, url) { const ingress = restate4.connect({ url }); const rawClient = ingress.objectSendClient(object2, key); return wrapIngressSendClient(rawClient, serde); } function createWorkflowClient2(workflow2, key, serde, url) { const ingress = restate4.connect({ url }); const rawClient = ingress.workflowClient(workflow2, key); return wrapIngressWorkflowClient(rawClient, serde); } function createWorkflowSendClient2(workflow2, key, serde, url) { const ingress = restate4.connect({ url }); const rawClient = ingress.workflowClient(workflow2, key); return wrapIngressWorkflowClient(rawClient, serde); } // src/factory.ts import * as restate5 from "@restatedev/restate-sdk-clients"; // src/typed-event-object.ts function createEventObject(name, SerdeClass) { return (handlers4) => { const wrappedHandlers = {}; for (const [eventName, handler] of Object.entries(handlers4)) { wrappedHandlers[eventName] = async (context, data) => { const eventUuid = context.ctx.rand.uuidv4(); const eventTimestamp = context.ctx.date.now(); const result = await handler({ ...context, eventUuid, eventTimestamp }, data); const eventsList = await context.getState("events") ?? []; const event = { event: eventName, data, timestamp: eventTimestamp, uuid: eventUuid }; const newEventsList = [ ...eventsList, event ]; await context.setState("events", newEventsList); return result; }; } return typedObject(name, SerdeClass)(wrappedHandlers); }; } // src/factory.ts class RestateClient { SerdeClass; url; constructor({ SerdeClass = restate5.serde.JsonSerde, restateUrl } = {}) { this.SerdeClass = SerdeClass; this.url = restateUrl ?? process.env.RESTATE_URL ?? "http://localhost:8080"; } createObject = (name) => typedObject(name, this.SerdeClass); createService = (name) => createRestateService(name, this.SerdeClass); createWorkflow = (name) => createRestateWorkflow(name, this.SerdeClass); createEventHandler = (name) => createEventObject(name, this.SerdeClass); serviceClient = (service2) => { return createServiceClient2(service2, new this.SerdeClass, this.url); }; serviceSendClient = (service2) => { return createServiceSendClient2(service2, new this.SerdeClass, this.url); }; objectClient = (object2, key) => { return createObjectClient2(object2, key, new this.SerdeClass, this.url); }; objectSendClient = (object2, key) => { return createObjectSendClient2(object2, key, new this.SerdeClass, this.url); }; workflowClient = (workflow2, key) => { return createWorkflowClient2(workflow2, key, new this.SerdeClass, this.url); }; workflowSendClient = (workflow2, key) => { return createWorkflowSendClient2(workflow2, key, new this.SerdeClass, this.url); }; } // src/index.ts import * as restate6 from "@restatedev/restate-sdk"; import * as restateClients from "@restatedev/restate-sdk-clients"; import * as restateFetch from "@restatedev/restate-sdk/fetch"; import * as restateLambda from "@restatedev/restate-sdk/lambda"; export { wrapSendClient, wrapIngressWorkflowClient, wrapIngressSendClient, wrapIngressClient, wrapClient, createRestateWorkflow as typedWorkflow, createRestateService as typedService, typedObject, restateLambda, restateFetch, restateClients, restate6 as restate, createWorkflowSendClient as createWrappedWorkflowSendClient, createWorkflowClient as createWrappedWorkflowClient, createServiceSendClient as createWrappedServiceSendClient, createServiceClient as createWrappedServiceClient, createObjectSendClient as createWrappedObjectSendClient, createObjectClient as createWrappedObjectClient, createWorkflowSendClient2 as createWorkflowSendClient, createWorkflowClient2 as createWorkflowClient, createServiceSendClient2 as createServiceSendClient, createServiceClient2 as createServiceClient, createRestateWorkflow, createRestateService, typedObject as createRestateObject, createObjectSendClient2 as createObjectSendClient, createObjectClient2 as createObjectClient, createEventObject, RestateClient }; //# debugId=F92AE1210039200364756E2164756E21