Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions connect/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,8 @@ const messageId = await message({

> Optional: Pass `returnAssignmentSlot: true` to `message` to get the scheduled slot of this message

> Optional: Pass `returnMessageId: true` with `returnAssignmentSlot: true` to return both values as `{ slot, id }`

> You can pass a 32 byte `anchor` to `message` which will be set on the DataItem

#### `signMessage`
Expand Down
2 changes: 1 addition & 1 deletion connect/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@permaweb/aoconnect",
"version": "0.0.94",
"version": "0.0.95",
"repository": {
"type": "git",
"url": "https://github.com/permaweb/ao.git",
Expand Down
29 changes: 28 additions & 1 deletion connect/src/client/ao-core.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,26 @@ const getTags = (args) =>

const getData = (args) => args.data ?? '1984'

const getMessageId = (response, parsedResponse) =>
parsedResponse.id ??
parsedResponse.messageId ??
parsedResponse.message?.id ??
response.headers?.get?.('id') ??
response.headers?.get?.('message')

const getMessageResponse = ({ response, parsedResponse, returnAssignmentSlot, returnMessageId }) => {
const slot = parsedResponse.slot

if (returnMessageId) {
const id = getMessageId(response, parsedResponse)
if (!id) throw new Error('Message id not found in response')
if (returnAssignmentSlot) return { slot, id }
return id
}

return slot
}

export function requestWith(deps) {
return async (args) => {
try {
Expand Down Expand Up @@ -126,7 +146,14 @@ export function messageWith(deps) {
const parsedResponse = await response.json()

if (args.opts?.fullResponse) return normalizeOutput(parsedResponse)
else return parsedResponse.slot
else {
return getMessageResponse({
response,
parsedResponse,
returnAssignmentSlot: args.returnAssignmentSlot,
returnMessageId: args.returnMessageId
})
}
}
else throw new Error('Error sending message')
} catch (e) {
Expand Down
58 changes: 58 additions & 0 deletions connect/src/client/ao-core.unit.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { describe, test } from 'node:test'
import * as assert from 'node:assert'

import { messageWith } from './ao-core.js'

function responseWith (body, headers = {}) {
return {
ok: true,
headers: {
get: (name) => headers[name] ?? headers[name.toLowerCase()] ?? null
},
json: async () => body
}
}

function createMessage (body, headers) {
return messageWith({
aoCore: {
request: async () => responseWith(body, headers)
}
})
}

describe('ao-core message', () => {
test('returns the assignment slot by default', async () => {
const message = createMessage({ slot: 42, id: 'message-123' })

const res = await message({ process: 'process-asdf' })

assert.equal(res, 42)
})

test('returns the message id when returnMessageId is true', async () => {
const message = createMessage({ slot: 42, id: 'message-123' })

const res = await message({
process: 'process-asdf',
returnMessageId: true
})

assert.equal(res, 'message-123')
})

test('returns slot and id when returnAssignmentSlot and returnMessageId are true', async () => {
const message = createMessage({ slot: 42, id: 'message-123' })

const res = await message({
process: 'process-asdf',
returnAssignmentSlot: true,
returnMessageId: true
})

assert.deepStrictEqual(res, {
slot: 42,
id: 'message-123'
})
})
})
3 changes: 2 additions & 1 deletion connect/src/dal.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,8 @@ export const deployMessageSchema = z.function()
tags: z.array(tagSchema),
anchor: z.string().optional(),
signer: z.any().nullish(),
returnAssignmentSlot: z.boolean().optional()
returnAssignmentSlot: z.boolean().optional(),
returnMessageId: z.boolean().optional()
}))
.returns(z.promise(
z.object({
Expand Down
22 changes: 17 additions & 5 deletions connect/src/lib/message/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,21 +15,33 @@ import { prepareMessageWith, sendSignedMessageWith, uploadMessageWith } from './
* @property {{ name: string, value: string }[]} [tags]
* @property {string} [anchor]
* @property {Types['signer']} [signer]
* @property {boolean} [returnAssignmentSlot]
* @property {boolean} [returnMessageId]
*
* @callback SendMessage
* @param {SendMessageArgs} args
* @returns {Promise<string>} the id of the data item that represents this message
* @returns {Promise<string | { slot: string, id: string }>} the id of the data item that represents this message, the assignment slot, or both
*
* @param {Env1} - the environment
* @returns {SendMessage}
*/
export function messageWith (env) {
const uploadMessage = uploadMessageWith(env)

return ({ process, data, tags, anchor, signer, returnAssignmentSlot }) => {
return of({ id: process, data, tags, anchor, signer, returnAssignmentSlot })
return ({ process, data, tags, anchor, signer, returnAssignmentSlot, returnMessageId }) => {
return of({ id: process, data, tags, anchor, signer, returnAssignmentSlot, returnMessageId })
.chain(uploadMessage)
.map((ctx) => returnAssignmentSlot ? ctx.assignmentSlot.toString() : ctx.messageId)
.map((ctx) => {
const id = ctx.messageId

if (returnAssignmentSlot) {
const slot = ctx.assignmentSlot.toString()
if (returnMessageId) return { slot, id }
return slot
}

return id
})
.bimap(errFrom, identity)
.toPromise()
}
Expand Down Expand Up @@ -57,4 +69,4 @@ export function signedMessageWith (env) {
.bimap(errFrom, identity)
.toPromise()
}
}
}
62 changes: 62 additions & 0 deletions connect/src/lib/message/index.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { describe, test } from 'node:test'
import * as assert from 'node:assert'

import { createLogger } from '../../logger.js'
import { messageWith } from './index.js'

const logger = createLogger('message')

function createMessage (deployMessage) {
return messageWith({ deployMessage, logger })
}

describe('message', () => {
test('returns the message id by default', async () => {
const message = createMessage(async ({ returnMessageId }) => {
assert.equal(returnMessageId, undefined)
return { messageId: 'data-item-123', assignmentSlot: '42' }
})

const res = await message({
process: 'process-asdf',
signer: () => {}
})

assert.equal(res, 'data-item-123')
})

test('returns the assignment slot when returnAssignmentSlot is true', async () => {
const message = createMessage(async ({ returnAssignmentSlot }) => {
assert.equal(returnAssignmentSlot, true)
return { messageId: 'data-item-123', assignmentSlot: '42' }
})

const res = await message({
process: 'process-asdf',
signer: () => {},
returnAssignmentSlot: true
})

assert.equal(res, '42')
})

test('returns slot and id when returnAssignmentSlot and returnMessageId are true', async () => {
const message = createMessage(async ({ returnAssignmentSlot, returnMessageId }) => {
assert.equal(returnAssignmentSlot, true)
assert.equal(returnMessageId, true)
return { messageId: 'data-item-123', assignmentSlot: '42' }
})

const res = await message({
process: 'process-asdf',
signer: () => {},
returnAssignmentSlot: true,
returnMessageId: true
})

assert.deepStrictEqual(res, {
slot: '42',
id: 'data-item-123'
})
})
})
5 changes: 3 additions & 2 deletions connect/src/lib/message/upload-message.js
Original file line number Diff line number Diff line change
Expand Up @@ -115,14 +115,15 @@ export function uploadMessageWith (env) {
return of(ctx)
.chain(buildTags)
.chain(buildData)
.chain(fromPromise(({ id, data, tags, anchor, signer, returnAssignmentSlot }) => {
.chain(fromPromise(({ id, data, tags, anchor, signer, returnAssignmentSlot, returnMessageId }) => {
return deployMessage({
processId: id,
data,
tags,
anchor,
signer: signerSchema.implement(signer || env.signer),
returnAssignmentSlot
returnAssignmentSlot,
returnMessageId
})
}))
.map(res => ({
Expand Down
Loading