# effects - we have new provider thingy with api that returns effects ```ts Server.provider2(cap, ({ invocation, invoke, id }) => { return { result: { ok: {} } fx: { join: invoke("offer/arrange", { with: id.did(), ... }) } } }) ``` - Server sends receipt back - Calls the local handler for the effects concurrently - handler for the scheduled events can be something like ```ts Server.schedule(OfferArrange, async ({ capability }) => { const response = await fetch(ribar.url, { body: capability.input }) const result = await response.json() if (result.ok) { return { result } } else { const sleep = invoke({ 'scheduler/sleep': { ms: 1000 } }) const reschedule = invoke({ 'offer/arrange': { ...cap.input _: sleep.wait() } }) return { result: { ok: {} }, fx: { fork: [sleep], join: reschedule } } } }) ``` - ⚠️ We need a way to not do request / response - ⚠️ We need a way to do `await/ok` from invocation spec ```ts Server.promise({ can: "wait/s3/put" }, async ({ input, context }) => { // writes into dynamo cid to invoke when that event // happens await context.s3.putListeners({ path: input.path, invoke: context.cid }) return context.suspend() }, (response) => { return { result: format(response) } }) Server.provide("wait/s3/put", async ({ invocation, input, context }) => { // writes into dynamo cid to invoke when that event // happens await context.s3.putListeners({ path: input.path, invoke: invocation.link() }) return Server.suspend() }) invoke("ucanto/resume", { task: { ... } data }) Server.create({ loadInvocation(cid) => { } }) Server.join(OfferArrange, async ({ capability, tx, events }) => { const response = await fetch(ribar.url, { body: capability.input }) const result = await response.json() if (result.ok) { return tx.return(result) } else { return tx .wait(invoke("wait/s3/put")) .and(invoke(myself)) } }) ``` - we need a schedulery thing on the server ```ts Server.create({ ..., events: { wait(time, invocation): WaitEvent, S3Put(path, invocation): S3PutEvent } // scheduler: { // wait: (date, invocation):void => { // }, // poll: (event, invocation):void => { // } // } }) ``` ```typescript Provider.task('s3/await/put', ({ input, invocation, agent }) => { // We create an `s3/wait/resume` invocation that we'll write // into s3 listener queue so that once it happens listener // (lambada) will be able to invoke it. const task = agent.invoke('s3/receive/put', { job: invocation.link() }) // We write into some place like dynamo db an event that we // want to receive and invocation in a CAR file that listener // will be able to use to send it to us. s3.listeners.put({ put: input.path }, await task.archive()) // We join this invocation with task which will be run by // the s3 listener. Whatever the result of the task is will // end up been result of this invocation as well ?? return Provider.join(task) }) Provider.provider('s3/receive/put', ({ input, invocation }) => { // we complete task with whatever was passed inside the input return input.result.ok ? { ok: {} } { error: input.result.error } }) function* s3(event) { const id = yield* Task.current() s3.listeners.put(event, id) const result = yield* Task.suspend(id) return { ok: {} } } ``` ### Spade Proxy Example ```typescript const OfferDetail = Schema.struct({ // CAR CID link: Schema.link(), // CAR size size: Schema.integer(), // CAR commP commitmentProof: Schema.link(), // URLs CAR can be fetched from src: Schema.url().array() }) const Offer = capability({ can: "aggregate/offer", with: BrokerDID, nb: Schema.struct({ // link to the offers who's blocks // will be attached to invocation offer: OfferDetail .array() .link() .attach(), // aggregate commP commitmentProof: Schema.link(), }) }) const Arrange = capability({ can: "offer/arrange", with: BrokerDID, nb: Schema.struct({ // aggregate commP commitmentProof: Schema.link() }) }) Provider.provide(Offer, async ({ capability, context }) => { // We create an invocation that can be executed later by // another actor if we give it to them // // ⚠️ This is a simple case because we do not need to compute // some data to be passed to the arrange handler. If we had to // do it we would have two options at our disposal // // 1. Delegate capability to invoke this as opposed to creating // an invocation. That would actually be better from OCAP // perspective but would imply caller should be capable of // doing ucan signing and invoking. // 2. Alternatively we could have pre-arranged a place where // data could be written and then read by `arrange` handler. // E.g we could presign S3 PUT URL and include it in `nb` // field when job of arrangment is done system could write // result into that URL and run this invocation. Handler // will then read the data from the URL and incorporate it // in the result. const arrange = await Arrange.invoke({ issuer: context.signer, audience: context.signer, with: context.signer.did(), nb: { commitmentProof: capability.commitmentProof }, expiration: monthFromNow }).delegate() // Then put in the DB record for this offer (keyed by commP) // and archived invocation which can be invoked by someone else db.put({ commitmentProof, run: await arrange.archive() }) return Provider // we return ok result .ok({ status: "queued" }) // and add a join effect we have not ran it however // so server will do no attempt to do so. .join(arrange.link()) }) Provider.provide(Arrange, async ({ capability }) => { // aggregate offer does not actually do anyithing because all // of the actual work was done by the outside system which // simply reported a result return Provider.ok({ commitmentProof: capability.commitmentProof }) }) ```