# 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
})
})
```