@xyo-network/bridge-http 2.107.6 → 2.109.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/dist/browser/HttpBridgeBase.d.cts.map +1 -1
  2. package/dist/browser/HttpBridgeBase.d.mts.map +1 -1
  3. package/dist/browser/HttpBridgeBase.d.ts.map +1 -1
  4. package/dist/browser/HttpBridgeFull.d.cts +11 -4
  5. package/dist/browser/HttpBridgeFull.d.cts.map +1 -1
  6. package/dist/browser/HttpBridgeFull.d.mts +11 -4
  7. package/dist/browser/HttpBridgeFull.d.mts.map +1 -1
  8. package/dist/browser/HttpBridgeFull.d.ts +11 -4
  9. package/dist/browser/HttpBridgeFull.d.ts.map +1 -1
  10. package/dist/browser/index-browser.cjs +4 -4
  11. package/dist/browser/index-browser.cjs.map +1 -1
  12. package/dist/browser/index-browser.js +4 -4
  13. package/dist/browser/index-browser.js.map +1 -1
  14. package/dist/neutral/HttpBridgeBase.d.cts.map +1 -1
  15. package/dist/neutral/HttpBridgeBase.d.mts.map +1 -1
  16. package/dist/neutral/HttpBridgeBase.d.ts.map +1 -1
  17. package/dist/neutral/HttpBridgeFull.d.cts +11 -4
  18. package/dist/neutral/HttpBridgeFull.d.cts.map +1 -1
  19. package/dist/neutral/HttpBridgeFull.d.mts +11 -4
  20. package/dist/neutral/HttpBridgeFull.d.mts.map +1 -1
  21. package/dist/neutral/HttpBridgeFull.d.ts +11 -4
  22. package/dist/neutral/HttpBridgeFull.d.ts.map +1 -1
  23. package/dist/neutral/index-browser.cjs +4 -4
  24. package/dist/neutral/index-browser.cjs.map +1 -1
  25. package/dist/neutral/index-browser.js +4 -4
  26. package/dist/neutral/index-browser.js.map +1 -1
  27. package/dist/node/HttpBridgeBase.d.cts.map +1 -1
  28. package/dist/node/HttpBridgeBase.d.mts.map +1 -1
  29. package/dist/node/HttpBridgeBase.d.ts.map +1 -1
  30. package/dist/node/HttpBridgeFull.d.cts +11 -4
  31. package/dist/node/HttpBridgeFull.d.cts.map +1 -1
  32. package/dist/node/HttpBridgeFull.d.mts +11 -4
  33. package/dist/node/HttpBridgeFull.d.mts.map +1 -1
  34. package/dist/node/HttpBridgeFull.d.ts +11 -4
  35. package/dist/node/HttpBridgeFull.d.ts.map +1 -1
  36. package/dist/node/index.cjs +93 -42
  37. package/dist/node/index.cjs.map +1 -1
  38. package/dist/node/index.js +93 -42
  39. package/dist/node/index.js.map +1 -1
  40. package/package.json +25 -21
  41. package/src/HttpBridgeBase.ts +5 -4
  42. package/src/HttpBridgeFull.ts +125 -44
  43. package/src/HttpBridgeModuleResolver.ts +1 -1
  44. package/src/ModuleProxy/ModuleProxy.ts +1 -1
  45. package/xy.config.ts +1 -0
@@ -4,16 +4,45 @@ import { assertEx } from '@xylabs/assert'
4
4
  import { exists } from '@xylabs/exists'
5
5
  import { Address } from '@xylabs/hex'
6
6
  import { toJsonString } from '@xylabs/object'
7
- import { ApiEnvelopeSuccess } from '@xyo-network/api-models'
7
+ import {
8
+ asyncHandler,
9
+ customPoweredByHeader,
10
+ disableCaseSensitiveRouting,
11
+ disableExpressDefaultPoweredByHeader,
12
+ jsonBodyParser,
13
+ responseProfiler,
14
+ useRequestCounters,
15
+ } from '@xylabs/sdk-api-express-ecs'
8
16
  import { isQueryBoundWitness, QueryBoundWitness } from '@xyo-network/boundwitness-model'
9
17
  import { BridgeExposeOptions, BridgeParams, BridgeUnexposeOptions } from '@xyo-network/bridge-model'
18
+ import { standardResponses } from '@xyo-network/express-node-middleware'
10
19
  import { AnyConfigSchema, creatableModule, ModuleInstance, ModuleQueryResult, resolveAddressToInstanceUp } from '@xyo-network/module-model'
11
20
  import { Payload } from '@xyo-network/payload-model'
12
21
  import express, { Application, Request, Response } from 'express'
22
+ import { StatusCodes } from 'http-status-codes'
13
23
 
14
24
  import { HttpBridgeBase } from './HttpBridgeBase'
15
25
  import { HttpBridgeConfig } from './HttpBridgeConfig'
16
26
 
27
+ /**
28
+ * The type of the path parameters for the address path.
29
+ */
30
+ type AddressPathParams = {
31
+ address: Address
32
+ }
33
+
34
+ /**
35
+ * The type of the request body for the address path.
36
+ */
37
+ type PostAddressRequestBody = [QueryBoundWitness, undefined | Payload[]]
38
+
39
+ // TODO: This does not match the error response shape of the legacy bridge BUT it its the
40
+ // shape this bridge is currently returning. Massage this into the standard
41
+ // error shape constructed via middleware.
42
+ type ErrorResponseBody = {
43
+ error: string
44
+ }
45
+
17
46
  export interface HttpBridgeParams extends BridgeParams<AnyConfigSchema<HttpBridgeConfig>> {}
18
47
 
19
48
  @creatableModule()
@@ -22,25 +51,13 @@ export class HttpBridge<TParams extends HttpBridgeParams> extends HttpBridgeBase
22
51
  protected _exposedModules: WeakRef<ModuleInstance>[] = []
23
52
  protected _server?: Server
24
53
 
25
- get app() {
26
- this._app =
27
- this._app ??
28
- (() => {
29
- const app = express()
30
- app.use(express.json())
31
-
32
- app.post<Payload[]>('/', (req, res) => {
33
- this.handlePost(req, res)
34
- })
35
-
36
- return app
37
- })()
38
- return this._app
54
+ protected get app() {
55
+ if (!this._app) this._app = this.initializeApp()
56
+ return assertEx(this._app, () => 'App not initialized')
39
57
  }
40
58
 
41
59
  async exposeChild(mod: ModuleInstance, options?: BridgeExposeOptions | undefined): Promise<ModuleInstance[]> {
42
60
  const { maxDepth = 5 } = options ?? {}
43
- console.log(`exposeChild: ${mod.address} ${mod?.id} ${maxDepth}`)
44
61
  assertEx(this.config.host, () => 'Not configured as a host')
45
62
  this._exposedModules.push(new WeakRef(mod))
46
63
  const children = maxDepth > 0 ? (await mod.publicChildren?.()) ?? [] : []
@@ -58,7 +75,6 @@ export class HttpBridge<TParams extends HttpBridgeParams> extends HttpBridgeBase
58
75
  override async exposeHandler(address: Address, options?: BridgeExposeOptions | undefined): Promise<ModuleInstance[]> {
59
76
  const { required = true } = options ?? {}
60
77
  const mod = await resolveAddressToInstanceUp(this, address)
61
- console.log(`exposeHandler: ${address} ${mod?.id}`)
62
78
  if (required && !mod) {
63
79
  throw new Error(`Unable to find required module: ${address}`)
64
80
  }
@@ -73,11 +89,11 @@ export class HttpBridge<TParams extends HttpBridgeParams> extends HttpBridgeBase
73
89
  }
74
90
 
75
91
  override async startHandler(): Promise<boolean> {
76
- return (await super.startHandler()) && this.startHttpServer()
92
+ return (await super.startHandler()) && (await this.startHttpServer())
77
93
  }
78
94
 
79
95
  override async stopHandler(_timeout?: number | undefined): Promise<boolean> {
80
- return (await super.stopHandler()) && this.stopHttpServer()
96
+ return (await super.stopHandler()) && (await this.stopHttpServer())
81
97
  }
82
98
 
83
99
  override async unexposeHandler(address: Address, options?: BridgeUnexposeOptions | undefined): Promise<ModuleInstance[]> {
@@ -100,44 +116,109 @@ export class HttpBridge<TParams extends HttpBridgeParams> extends HttpBridgeBase
100
116
 
101
117
  protected async callLocalModule(address: Address, query: QueryBoundWitness, payloads: Payload[]): Promise<ModuleQueryResult | null> {
102
118
  const mod = this._exposedModules.find((ref) => ref.deref()?.address === address)?.deref()
103
- if (mod) {
104
- return await mod.query(query, payloads)
119
+ return mod ? await mod.query(query, payloads) : null
120
+ }
121
+
122
+ protected async handleGet(req: Request<AddressPathParams, ModuleQueryResult, PostAddressRequestBody>, res: Response) {
123
+ const { address } = req.params
124
+ try {
125
+ if (address == this.address) {
126
+ res.json(await this.stateQuery(this.account))
127
+ } else {
128
+ const mod = this._exposedModules.find((ref) => ref.deref()?.address === address)?.deref()
129
+ // TODO: Use standard errors middleware
130
+ if (mod) {
131
+ res.json(await mod.stateQuery(this.account))
132
+ } else {
133
+ res.status(StatusCodes.NOT_FOUND).json({ error: 'Module not found' })
134
+ }
135
+ }
136
+ } catch (ex) {
137
+ // TODO: Sanitize message
138
+ res.status(StatusCodes.INTERNAL_SERVER_ERROR).json({ error: (ex as Error).message })
105
139
  }
106
- return null
107
140
  }
108
141
 
109
- protected handlePost(req: Request<Payload[]>, res: Response) {
110
- const allPayloads = req.body as Payload[]
111
- const query = allPayloads.find(isQueryBoundWitness) as QueryBoundWitness
112
- const payloads = allPayloads.filter((payload) => !isQueryBoundWitness(payload))
113
- this.callLocalModule(req.route, query, payloads)
114
- .then((result) => {
142
+ protected async handlePost(req: Request<AddressPathParams, ModuleQueryResult, PostAddressRequestBody>, res: Response) {
143
+ const { address } = req.params
144
+ const [bw, payloads = []] = Array.isArray(req.body) ? req.body : []
145
+ const query = isQueryBoundWitness(bw) ? bw : undefined
146
+ if (!query) {
147
+ // TODO: Use standard errors middleware
148
+ res.status(StatusCodes.BAD_REQUEST).json({ error: 'No query provided' })
149
+ return
150
+ }
151
+ try {
152
+ if (address == this.address) {
153
+ const result = await this.query(query, payloads)
154
+ return res.json(result)
155
+ } else {
156
+ const result = await this.callLocalModule(address, query, payloads)
157
+ // TODO: Use standard errors middleware
115
158
  if (result === null) {
116
- res.status(404).json({ error: 'Module not found' })
159
+ res.status(StatusCodes.NOT_FOUND).json({ error: 'Module not found' })
117
160
  } else {
118
- const envelope = {
119
- data: result,
120
- } as ApiEnvelopeSuccess<ModuleQueryResult>
121
- res.json(envelope)
161
+ res.json(result)
122
162
  }
123
- })
124
- .catch((ex) => {
125
- res.status(500).json({ error: (ex as Error).message })
126
- })
163
+ }
164
+ } catch (ex) {
165
+ // TODO: Sanitize message
166
+ res.status(StatusCodes.INTERNAL_SERVER_ERROR).json({ error: (ex as Error).message })
167
+ }
127
168
  }
128
169
 
129
- protected startHttpServer() {
170
+ protected initializeApp() {
171
+ // Create the express app
172
+ const app = express()
173
+
174
+ // Add middleware
175
+ app.use(responseProfiler)
176
+ app.use(jsonBodyParser)
177
+ app.use(standardResponses)
178
+ disableExpressDefaultPoweredByHeader(app)
179
+ app.use(customPoweredByHeader)
180
+ disableCaseSensitiveRouting(app)
181
+ useRequestCounters(app)
182
+
183
+ // Add routes
184
+ // Redirect all requests to the root to this module's address
185
+ app.get('/', (_req, res) => res.redirect(StatusCodes.MOVED_TEMPORARILY, `/${this.address}`))
186
+ app.post('/', (_req, res) => res.redirect(StatusCodes.TEMPORARY_REDIRECT, `/${this.address}`))
187
+
188
+ app.get<AddressPathParams, ModuleQueryResult>(
189
+ '/:address',
190
+ asyncHandler(async (req, res) => await this.handleGet(req, res)),
191
+ )
192
+ app.post<AddressPathParams, ModuleQueryResult, PostAddressRequestBody>(
193
+ '/:address',
194
+ asyncHandler(async (req, res) => await this.handlePost(req, res)),
195
+ )
196
+ return app
197
+ }
198
+
199
+ protected startHttpServer(): Promise<boolean> {
130
200
  if (this.config.host) {
131
201
  assertEx(!this._server, () => 'Server already started')
132
202
  this._server = this.app.listen(this.config.host?.port ?? 3030)
133
203
  }
134
- return true
204
+ return Promise.resolve(true)
135
205
  }
136
206
 
137
- protected stopHttpServer() {
138
- const server = assertEx(this._server, () => 'Server not started')
139
- server.close()
140
- this._server = undefined
141
- return true
207
+ protected stopHttpServer(): Promise<boolean> {
208
+ if (this.config.host) {
209
+ return new Promise((resolve, reject) => {
210
+ if (this._server) {
211
+ this._server.close((err) => {
212
+ if (err) {
213
+ reject(err)
214
+ } else {
215
+ this._server = undefined
216
+ resolve(true)
217
+ }
218
+ })
219
+ }
220
+ })
221
+ }
222
+ return Promise.resolve(true)
142
223
  }
143
224
  }
@@ -65,7 +65,7 @@ export class HttpBridgeModuleResolver<
65
65
  return
66
66
  }
67
67
  }
68
- const account = Account.randomSync()
68
+ const account = await Account.random()
69
69
  const finalParams: HttpModuleProxyParams = {
70
70
  account,
71
71
  archiving: this.params.archiving,
@@ -84,7 +84,7 @@ export class HttpModuleProxy<
84
84
  dead: this.dead,
85
85
  downResolver: this.downResolver,
86
86
  logger: this.logger,
87
- module: this,
87
+ mod: this,
88
88
  transformers: this.moduleIdentifierTransformers,
89
89
  upResolver: this.upResolver,
90
90
  }
package/xy.config.ts CHANGED
@@ -13,4 +13,5 @@ const config: XyTsupConfig = {
13
13
  },
14
14
  }
15
15
 
16
+ // eslint-disable-next-line import/no-default-export
16
17
  export default config