Skip to content

Commit a483778

Browse files
authored
Merge pull request #1238 from permaweb/feat/compatibility
Feat/compatibility
2 parents b6eb66e + 37c8c0d commit a483778

File tree

10 files changed

+623
-128
lines changed

10 files changed

+623
-128
lines changed

connect/package-lock.json

Lines changed: 101 additions & 79 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

connect/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
"./dist"
3838
],
3939
"scripts": {
40+
"clean:install": "rm -rf node_modules package-lock.json || true && npm cache clean --force && npm install",
4041
"build": "npm run build:types && npm run build:src",
4142
"build:src": "node esbuild.js",
4243
"build:types": "tsc src/index.js --skipLibCheck --declaration --allowJs --emitDeclarationOnly --outDir dist",
@@ -47,6 +48,7 @@
4748
},
4849
"dependencies": {
4950
"@dha-team/arbundles": "1.0.3",
51+
"@permaweb/ao-core-libs": "0.0.7",
5052
"@permaweb/ao-scheduler-utils": "~0.0.25",
5153
"@permaweb/protocol-tag-utils": "~0.0.2",
5254
"axios": "^1.7.9",

connect/src/client/ao-core.js

Lines changed: 267 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,267 @@
1+
import { debugLog } from '../logger.js'
2+
3+
function convertToLegacyOutput(jsonRes) {
4+
let body = {}
5+
try {
6+
body = JSON.parse(jsonRes?.results?.json?.body)
7+
debugLog('info', 'HyperBEAM Response Body:', body)
8+
} catch (_) { }
9+
10+
debugLog('info', 'Parsed HyperBEAM Response Body:', body)
11+
12+
return {
13+
Output: body?.Output || {},
14+
Messages: body?.Messages || [],
15+
Assignments: body?.Assignments || [],
16+
Spawns: body?.Spawns || [],
17+
Error: body?.Error,
18+
...(body ?? {})
19+
}
20+
}
21+
22+
const baseParams = {
23+
method: 'POST',
24+
'signing-format': 'ans104',
25+
'accept-bundle': 'true',
26+
}
27+
28+
const httpParams = { ...baseParams, 'accept-codec': '[email protected]' }
29+
const jsonParams = { ...baseParams, 'require-codec': 'application/json' }
30+
31+
const getAOParams = (type) => ({
32+
Type: type,
33+
'Data-Protocol': 'ao',
34+
Variant: 'ao.N.1'
35+
})
36+
37+
const getTags = (args) =>
38+
args.tags
39+
? Object.fromEntries(args.tags.map(tag => [tag.name, tag.value]))
40+
: {}
41+
42+
const getData = (args) => args.data ?? '1984'
43+
44+
export function requestWith(deps) {
45+
return async (args) => {
46+
try {
47+
return deps.aoCore.request(args);
48+
}
49+
catch (e) {
50+
throw new Error(e.message ?? 'Error sending request')
51+
}
52+
}
53+
}
54+
55+
export function spawnWith(deps) {
56+
return async (args) => {
57+
let scheduler = deps.scheduler
58+
59+
if (!scheduler && deps.url) {
60+
const schedulerRes = await fetch(`${deps.url}/[email protected]/info/address`)
61+
scheduler = await schedulerRes.text()
62+
}
63+
64+
const module = process.env.MODULE || args.module
65+
66+
if (!scheduler) throw new Error('No scheduler provided')
67+
if (!module) throw new Error('No module provided')
68+
69+
const authority = process.env.AUTHORITY || args.authority || scheduler
70+
71+
debugLog('info', 'Node URL:', deps.url)
72+
debugLog('info', 'Scheduler:', scheduler)
73+
debugLog('info', 'Authority:', authority)
74+
debugLog('info', 'Module:', module)
75+
76+
try {
77+
const params = {
78+
path: '/push',
79+
device: '[email protected]',
80+
'scheduler-device': '[email protected]',
81+
'push-device': '[email protected]',
82+
'execution-device': '[email protected]',
83+
Authority: authority,
84+
Scheduler: scheduler,
85+
Module: module,
86+
data: getData(args),
87+
...getTags(args),
88+
...getAOParams('Process'),
89+
...httpParams
90+
}
91+
92+
const response = await deps.aoCore.request(params)
93+
94+
try {
95+
const processId = response.headers.get('process')
96+
debugLog('info', 'Process ID:', processId)
97+
98+
if (processId) return processId;
99+
else throw new Error('Error spawning process')
100+
} catch (e) {
101+
throw new Error(e.message ?? 'Error spawning process')
102+
}
103+
} catch (e) {
104+
throw new Error(e.message ?? 'Error spawning process')
105+
}
106+
}
107+
}
108+
109+
export function messageWith(deps) {
110+
return async (args) => {
111+
try {
112+
const params = {
113+
path: `/${args.process}[email protected]/push`,
114+
target: args.process,
115+
data: getData(args),
116+
...getTags(args),
117+
...getAOParams('Message'),
118+
...jsonParams,
119+
}
120+
121+
const response = await deps.aoCore.request(params)
122+
if (response.ok) {
123+
const parsedResponse = await response.json()
124+
125+
if (args.opts?.fullResponse) return convertToLegacyOutput(parsedResponse)
126+
else return parsedResponse.slot
127+
}
128+
else throw new Error('Error sending message')
129+
} catch (e) {
130+
throw new Error(e.message ?? 'Error sending message')
131+
}
132+
}
133+
}
134+
135+
export function resultWith(deps) {
136+
return async (args) => {
137+
try {
138+
const params = {
139+
path: `/${args.process}[email protected]/compute=${args.slot ?? args.message}`,
140+
target: args.process,
141+
data: getData(args),
142+
...getTags(args),
143+
...jsonParams
144+
}
145+
const response = await deps.aoCore.request(params)
146+
if (response.ok) {
147+
const parsedResponse = await response.json()
148+
return convertToLegacyOutput(parsedResponse)
149+
}
150+
else throw new Error('Error getting result')
151+
} catch (e) {
152+
throw new Error(e.message ?? 'Error getting result')
153+
}
154+
}
155+
}
156+
157+
export function resultsWith(deps) {
158+
return async (args) => {
159+
try {
160+
const slotParams = {
161+
path: `/${args.process}/slot/current`,
162+
...httpParams
163+
}
164+
165+
const slotResponse = await deps.aoCore.request(slotParams)
166+
if (slotResponse.ok) {
167+
try {
168+
const currentSlot = await slotResponse.text();
169+
170+
const resultsParams = {
171+
path: `/${args.process}/compute=${currentSlot}`,
172+
...jsonParams
173+
}
174+
175+
const resultsResponse = await deps.aoCore.request(resultsParams)
176+
177+
if (resultsResponse.ok) {
178+
let parsedResultsResponse = await resultsResponse.json();
179+
180+
return {
181+
edges: [
182+
{
183+
cursor: currentSlot,
184+
node: {
185+
...convertToLegacyOutput(parsedResultsResponse)
186+
}
187+
}
188+
]
189+
}
190+
}
191+
192+
else throw new Error('Error getting results')
193+
} catch (e) {
194+
throw new Error(e.message ?? 'Error getting current process slot')
195+
}
196+
}
197+
else throw new Error('Error getting results')
198+
} catch (e) {
199+
throw new Error(e.message ?? 'Error getting results')
200+
}
201+
}
202+
}
203+
204+
export function dryrunWith(deps) {
205+
return async (args) => {
206+
try {
207+
const tags = getTags(args)
208+
const tagsAsParams = Object.entries(tags)?.length > 0
209+
? `&${Object.entries(tags).map(([key, value]) => `${key}=${value}`).join('&')}`
210+
: ''
211+
212+
const path = `/${args.process}[email protected]/as=execution/compute${tagsAsParams}`
213+
const params = {
214+
path,
215+
target: args.process,
216+
data: getData(args),
217+
...jsonParams
218+
}
219+
220+
const response = await deps.aoCore.request(params)
221+
if (response.ok) {
222+
const parsedResponse = await response.json()
223+
return convertToLegacyOutput(parsedResponse)
224+
}
225+
else throw new Error('Error running dryrun')
226+
} catch (e) {
227+
throw new Error(e.message ?? 'Error running dryrun')
228+
}
229+
}
230+
}
231+
232+
async function _retryInitPush(deps, processId, maxAttempts = 10) {
233+
const params = {
234+
path: `/${processId}/push`,
235+
target: processId,
236+
Action: 'Eval',
237+
data: 'require(\'.process\')._version',
238+
...getAOParams('Message'),
239+
...httpParams
240+
}
241+
242+
let lastError = null
243+
244+
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
245+
try {
246+
const initPush = await deps.aoCore.request(params)
247+
if (initPush.ok) {
248+
debugLog('info', `Init push succeeded on attempt ${attempt}`);
249+
return initPush
250+
} else {
251+
debugLog('warn', `Init push attempt ${attempt} returned ok=false`, {
252+
status: initPush.status,
253+
body: initPush
254+
})
255+
lastError = new Error(`Init push returned ok=false (status=${initPush.status})`);
256+
await new Promise((r) => setTimeout(r, 500));
257+
}
258+
} catch (err) {
259+
debugLog('warn', `Init push attempt ${attempt} threw`, err)
260+
lastError = err
261+
}
262+
263+
if (attempt === maxAttempts) break
264+
}
265+
266+
throw new Error(`Init push failed after ${maxAttempts} attempts: ${lastError?.message || 'unknown'}`)
267+
}

0 commit comments

Comments
 (0)