@xyo-network/chain-bridge 1.16.8 → 1.16.10

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.
@@ -0,0 +1,2 @@
1
+ export declare const TODO = true;
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/indexers/index.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,IAAI,OAAO,CAAA"}
@@ -0,0 +1,47 @@
1
+ import type { AnyConfigSchema } from '@xyo-network/module-model';
2
+ import type { Payload } from '@xyo-network/payload-model';
3
+ import { AbstractSentinel } from '@xyo-network/sentinel-abstract';
4
+ import type { SentinelConfig, SentinelInstance, SentinelModuleEventData, SentinelParams } from '@xyo-network/sentinel-model';
5
+ import type { XyoConnection, XyoGateway, XyoGatewayRunner, XyoViewer } from '@xyo-network/xl1-protocol-sdk';
6
+ export declare const XL1toEvmBridgeSentinelConfigSchema = "network.xyo.sentinel.chain.evm.bridge.config";
7
+ export type XL1toEvmBridgeSentinelConfigSchema = typeof XL1toEvmBridgeSentinelConfigSchema;
8
+ /**
9
+ * The configuration for the XL1 to EVM bridge sentinel
10
+ */
11
+ export type XL1toEvmBridgeSentinelConfig = SentinelConfig<{
12
+ /**
13
+ * The interval in milliseconds between checking for bridging intents to fulfill
14
+ */
15
+ bridgeIntentFulfillmentInterval: number;
16
+ /**
17
+ * The schema for the XL1 to EVM bridge sentinel config
18
+ */
19
+ schema: XL1toEvmBridgeSentinelConfigSchema;
20
+ }>;
21
+ interface XL1toEvmBridgeSentinelParamFields {
22
+ gateway: XyoGateway;
23
+ }
24
+ export type XL1toEvmBridgeSentinelParams<TConfig extends AnyConfigSchema<XL1toEvmBridgeSentinelConfig> = AnyConfigSchema<XL1toEvmBridgeSentinelConfig>> = SentinelParams<TConfig> & XL1toEvmBridgeSentinelParamFields;
25
+ export declare class XL1toEvmBridgeSentinel<TParams extends XL1toEvmBridgeSentinelParams = XL1toEvmBridgeSentinelParams, TEventData extends SentinelModuleEventData<SentinelInstance<TParams>> = SentinelModuleEventData<SentinelInstance<TParams>>> extends AbstractSentinel<TParams, TEventData> {
26
+ static readonly configSchemas: string[];
27
+ static readonly defaultConfigSchema = "network.xyo.sentinel.chain.evm.bridge.config";
28
+ private _bridgeAttemptsCounter;
29
+ private _bridgeCheckCounter;
30
+ private _bridgeErrorCounter;
31
+ private _bridgeSuccessCounter;
32
+ private _connection;
33
+ private _gateway;
34
+ private _reportMutex;
35
+ private _viewer;
36
+ protected get bridgeIntentFulfillmentInterval(): number;
37
+ protected get connection(): XyoConnection;
38
+ protected get gateway(): XyoGatewayRunner;
39
+ protected get viewer(): XyoViewer;
40
+ createHandler(): Promise<void>;
41
+ reportHandler(payloads?: Payload[]): Promise<Payload[]>;
42
+ private processAllBridgeIntents;
43
+ private processBridgeIntent;
44
+ private submitBridgeInitiationTransaction;
45
+ }
46
+ export {};
47
+ //# sourceMappingURL=XL1toEvmBridgeSentinel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"XL1toEvmBridgeSentinel.d.ts","sourceRoot":"","sources":["../../../../src/modules/XL1toEvmBridgeSentinel/XL1toEvmBridgeSentinel.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAA;AAChE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,4BAA4B,CAAA;AACzD,OAAO,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAA;AACjE,OAAO,KAAK,EACV,cAAc,EAAE,gBAAgB,EAAE,uBAAuB,EAAE,cAAc,EAC1E,MAAM,6BAA6B,CAAA;AAGpC,OAAO,KAAK,EACV,aAAa,EAAE,UAAU,EAAE,gBAAgB,EAAE,SAAS,EACvD,MAAM,+BAA+B,CAAA;AAOtC,eAAO,MAAM,kCAAkC,iDAAiD,CAAA;AAChG,MAAM,MAAM,kCAAkC,GAAG,OAAO,kCAAkC,CAAA;AAE1F;;GAEG;AACH,MAAM,MAAM,4BAA4B,GAAG,cAAc,CAAC;IACxD;;OAEG;IACH,+BAA+B,EAAE,MAAM,CAAA;IACvC;;OAEG;IACH,MAAM,EAAE,kCAAkC,CAAA;CAE3C,CAAC,CAAA;AAEF,UAAU,iCAAiC;IACzC,OAAO,EAAE,UAAU,CAAA;CACpB;AAED,MAAM,MAAM,4BAA4B,CACtC,OAAO,SAAS,eAAe,CAAC,4BAA4B,CAAC,GAAG,eAAe,CAAC,4BAA4B,CAAC,IAC3G,cAAc,CAAC,OAAO,CAAC,GAAG,iCAAiC,CAAA;AAI/D,qBAAa,sBAAsB,CACjC,OAAO,SAAS,4BAA4B,GAAG,4BAA4B,EAC3E,UAAU,SAAS,uBAAuB,CAAC,gBAAgB,CAAC,OAAO,CAAC,CAAC,GAAG,uBAAuB,CAAC,gBAAgB,CAAC,OAAO,CAAC,CAAC,CAC1H,SAAQ,gBAAgB,CAAC,OAAO,EAAE,UAAU,CAAC;IAC7C,gBAAyB,aAAa,WAAuC;IAC7E,gBAAyB,mBAAmB,kDAAqC;IACjF,OAAO,CAAC,sBAAsB,CAAiC;IAC/D,OAAO,CAAC,mBAAmB,CAAiC;IAC5D,OAAO,CAAC,mBAAmB,CAAiC;IAC5D,OAAO,CAAC,qBAAqB,CAAiC;IAC9D,OAAO,CAAC,WAAW,CAA2B;IAC9C,OAAO,CAAC,QAAQ,CAAwB;IACxC,OAAO,CAAC,YAAY,CAAc;IAClC,OAAO,CAAC,OAAO,CAAuB;IAEtC,SAAS,KAAK,+BAA+B,IAAI,MAAM,CAEtD;IAED,SAAS,KAAK,UAAU,IAAI,aAAa,CAExC;IAED,SAAS,KAAK,OAAO,IAAI,gBAAgB,CAExC;IAED,SAAS,KAAK,MAAM,IAAI,SAAS,CAEhC;IAEc,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC;IAgB9B,aAAa,CAAC,QAAQ,CAAC,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC;YAqBxD,uBAAuB;YAgBvB,mBAAmB;YAiBnB,iCAAiC;CAQhD"}
@@ -0,0 +1,2 @@
1
+ export * from './XL1toEvmBridgeSentinel.ts';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/modules/XL1toEvmBridgeSentinel/index.ts"],"names":[],"mappings":"AAAA,cAAc,6BAA6B,CAAA"}
@@ -0,0 +1,2 @@
1
+ export * from './XL1toEvmBridgeSentinel/index.ts';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/modules/index.ts"],"names":[],"mappings":"AAAA,cAAc,mCAAmC,CAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xyo-network/chain-bridge",
3
- "version": "1.16.8",
3
+ "version": "1.16.10",
4
4
  "description": "XYO Layer One Bridge",
5
5
  "homepage": "https://xylabs.com",
6
6
  "bugs": {
@@ -49,23 +49,25 @@
49
49
  "@opentelemetry/instrumentation": "~0.208.0",
50
50
  "@opentelemetry/instrumentation-express": "~0.57.0",
51
51
  "@opentelemetry/instrumentation-http": "~0.208.0",
52
- "@xylabs/express": "~5.0.24",
53
- "@xylabs/hex": "~5.0.24",
54
- "@xylabs/logger": "~5.0.24",
55
- "@xylabs/typeof": "~5.0.24",
52
+ "@xylabs/express": "~5.0.25",
53
+ "@xylabs/hex": "~5.0.25",
54
+ "@xylabs/logger": "~5.0.25",
55
+ "@xylabs/typeof": "~5.0.25",
56
56
  "@xyo-network/archivist-model": "~5.1.21",
57
57
  "@xyo-network/boundwitness-model": "~5.1.21",
58
- "@xyo-network/chain-protocol": "~1.16.8",
58
+ "@xyo-network/chain-protocol": "~1.16.10",
59
59
  "@xyo-network/manifest-model": "~5.1.21",
60
60
  "@xyo-network/module-factory-locator": "~5.1.21",
61
61
  "@xyo-network/module-model": "~5.1.21",
62
62
  "@xyo-network/node-model": "~5.1.21",
63
63
  "@xyo-network/payload-builder": "~5.1.21",
64
64
  "@xyo-network/payload-model": "~5.1.21",
65
+ "@xyo-network/sentinel-abstract": "~5.1.21",
66
+ "@xyo-network/sentinel-model": "~5.1.21",
65
67
  "@xyo-network/typechain": "~4.0.10",
66
68
  "@xyo-network/wallet-model": "~5.1.21",
67
- "@xyo-network/xl1-protocol": "~1.13.6",
68
- "@xyo-network/xl1-protocol-sdk": "~1.16.8",
69
+ "@xyo-network/xl1-protocol": "~1.13.11",
70
+ "@xyo-network/xl1-protocol-sdk": "~1.16.10",
69
71
  "compression": "~1.8.1",
70
72
  "cors": "~2.8.5",
71
73
  "express": "~5.1.0",
@@ -74,17 +76,18 @@
74
76
  "zod": "~4.1.12"
75
77
  },
76
78
  "devDependencies": {
79
+ "@opentelemetry/api": "~1.9.0",
77
80
  "@types/compression": "~1.8.1",
78
81
  "@types/cors": "~2.8.19",
79
82
  "@types/express": "5.0.5",
80
83
  "@types/express-serve-static-core": "~5.1.0",
81
84
  "@types/node": "~24.10.1",
82
- "@xylabs/assert": "~5.0.24",
83
- "@xylabs/base": "~5.0.24",
84
- "@xylabs/delay": "~5.0.24",
85
- "@xylabs/mongo": "~5.0.24",
86
- "@xylabs/object": "~5.0.24",
87
- "@xylabs/promise": "~5.0.24",
85
+ "@xylabs/assert": "~5.0.25",
86
+ "@xylabs/base": "~5.0.25",
87
+ "@xylabs/delay": "~5.0.25",
88
+ "@xylabs/mongo": "~5.0.25",
89
+ "@xylabs/object": "~5.0.25",
90
+ "@xylabs/promise": "~5.0.25",
88
91
  "@xylabs/ts-scripts-yarn3": "~7.2.8",
89
92
  "@xylabs/tsconfig": "~7.2.8",
90
93
  "@xyo-network/account": "~5.1.21",
@@ -96,10 +99,10 @@
96
99
  "@xyo-network/bios": "~7.1.1",
97
100
  "@xyo-network/bios-model": "~7.1.1",
98
101
  "@xyo-network/boundwitness-builder": "~5.1.21",
99
- "@xyo-network/chain-modules": "~1.16.8",
100
- "@xyo-network/chain-protocol": "~1.16.8",
101
- "@xyo-network/chain-services": "~1.16.8",
102
- "@xyo-network/chain-telemetry": "~1.16.8",
102
+ "@xyo-network/chain-modules": "~1.16.10",
103
+ "@xyo-network/chain-protocol": "~1.16.10",
104
+ "@xyo-network/chain-services": "~1.16.10",
105
+ "@xyo-network/chain-telemetry": "~1.16.10",
103
106
  "@xyo-network/manifest-wrapper": "~5.1.21",
104
107
  "@xyo-network/module-abstract": "~5.1.21",
105
108
  "@xyo-network/module-abstract-mongodb": "~5.1.21",
@@ -108,7 +111,8 @@
108
111
  "@xyo-network/payload-builder": "~5.1.21",
109
112
  "@xyo-network/sentinel-memory": "~5.1.21",
110
113
  "@xyo-network/wallet": "~5.1.21",
111
- "@xyo-network/xl1-protocol": "~1.13.6",
114
+ "@xyo-network/xl1-protocol": "~1.13.11",
115
+ "async-mutex": "~0.5.0",
112
116
  "dotenv": "~17.2.3",
113
117
  "eslint": "^9.39.1",
114
118
  "ethers": "~6.15.0",
@@ -116,7 +120,7 @@
116
120
  "nodemon": "~3.1.11",
117
121
  "tslib": "~2.8.1",
118
122
  "typescript": "~5.9.3",
119
- "vitest": "~4.0.8",
123
+ "vitest": "~4.0.9",
120
124
  "vitest-mock-extended": "~3.1.0"
121
125
  },
122
126
  "engines": {
@@ -0,0 +1 @@
1
+ export const TODO = true
@@ -0,0 +1,159 @@
1
+ import type { Attributes, Counter } from '@opentelemetry/api'
2
+ import { assertEx } from '@xylabs/assert'
3
+ import { delay } from '@xylabs/delay'
4
+ import { isDefined, isUndefined } from '@xylabs/typeof'
5
+ import type { AnyConfigSchema } from '@xyo-network/module-model'
6
+ import type { Payload } from '@xyo-network/payload-model'
7
+ import { AbstractSentinel } from '@xyo-network/sentinel-abstract'
8
+ import type {
9
+ SentinelConfig, SentinelInstance, SentinelModuleEventData, SentinelParams,
10
+ } from '@xyo-network/sentinel-model'
11
+ import type { BridgeIntent, SignedHydratedTransaction } from '@xyo-network/xl1-protocol'
12
+ import { isBridgeIntent } from '@xyo-network/xl1-protocol'
13
+ import type {
14
+ XyoConnection, XyoGateway, XyoGatewayRunner, XyoViewer,
15
+ } from '@xyo-network/xl1-protocol-sdk'
16
+ import {
17
+ flattenHydratedTransaction, flattenHydratedTransactions,
18
+ SimpleXyoGatewayRunner,
19
+ } from '@xyo-network/xl1-protocol-sdk'
20
+ import { Mutex } from 'async-mutex'
21
+
22
+ export const XL1toEvmBridgeSentinelConfigSchema = 'network.xyo.sentinel.chain.evm.bridge.config'
23
+ export type XL1toEvmBridgeSentinelConfigSchema = typeof XL1toEvmBridgeSentinelConfigSchema
24
+
25
+ /**
26
+ * The configuration for the XL1 to EVM bridge sentinel
27
+ */
28
+ export type XL1toEvmBridgeSentinelConfig = SentinelConfig<{
29
+ /**
30
+ * The interval in milliseconds between checking for bridging intents to fulfill
31
+ */
32
+ bridgeIntentFulfillmentInterval: number
33
+ /**
34
+ * The schema for the XL1 to EVM bridge sentinel config
35
+ */
36
+ schema: XL1toEvmBridgeSentinelConfigSchema
37
+
38
+ }>
39
+
40
+ interface XL1toEvmBridgeSentinelParamFields {
41
+ gateway: XyoGateway
42
+ }
43
+
44
+ export type XL1toEvmBridgeSentinelParams<
45
+ TConfig extends AnyConfigSchema<XL1toEvmBridgeSentinelConfig> = AnyConfigSchema<XL1toEvmBridgeSentinelConfig>,
46
+ > = SentinelParams<TConfig> & XL1toEvmBridgeSentinelParamFields
47
+
48
+ const defaultStepClaimIntervalMs = 15_000
49
+
50
+ export class XL1toEvmBridgeSentinel<
51
+ TParams extends XL1toEvmBridgeSentinelParams = XL1toEvmBridgeSentinelParams,
52
+ TEventData extends SentinelModuleEventData<SentinelInstance<TParams>> = SentinelModuleEventData<SentinelInstance<TParams>>,
53
+ > extends AbstractSentinel<TParams, TEventData> {
54
+ static override readonly configSchemas = [XL1toEvmBridgeSentinelConfigSchema]
55
+ static override readonly defaultConfigSchema = XL1toEvmBridgeSentinelConfigSchema
56
+ private _bridgeAttemptsCounter: Counter<Attributes> | undefined
57
+ private _bridgeCheckCounter: Counter<Attributes> | undefined
58
+ private _bridgeErrorCounter: Counter<Attributes> | undefined
59
+ private _bridgeSuccessCounter: Counter<Attributes> | undefined
60
+ private _connection: XyoConnection | undefined
61
+ private _gateway: XyoGateway | undefined
62
+ private _reportMutex = new Mutex()
63
+ private _viewer: XyoViewer | undefined
64
+
65
+ protected get bridgeIntentFulfillmentInterval(): number {
66
+ return isDefined(this.config.bridgeIntentFulfillmentInterval) ? this.config.bridgeIntentFulfillmentInterval : defaultStepClaimIntervalMs
67
+ }
68
+
69
+ protected get connection(): XyoConnection {
70
+ return assertEx(this._connection, () => 'Connection is not defined')
71
+ }
72
+
73
+ protected get gateway(): XyoGatewayRunner {
74
+ return new SimpleXyoGatewayRunner(assertEx(this._gateway, () => 'Gateway is not defined'))
75
+ }
76
+
77
+ protected get viewer(): XyoViewer {
78
+ return assertEx(this._viewer, () => 'Viewer is not defined')
79
+ }
80
+
81
+ override async createHandler(): Promise<void> {
82
+ await super.createHandler()
83
+ // Create meters for tracking bridge attempts, successes, and errors.
84
+ this._bridgeAttemptsCounter = this.meter?.createCounter('xl1_to_evm_bridge_sentinel_attempts_total', { description: 'Number of bridge attempts' })
85
+ this._bridgeCheckCounter = this.meter?.createCounter('xl1_to_evm_bridge_sentinel_check_total', { description: 'Number of bridge checks' })
86
+ this._bridgeSuccessCounter = this.meter?.createCounter('xl1_to_evm_bridge_sentinel_success_total', { description: 'Number of bridge successes' })
87
+ this._bridgeErrorCounter = this.meter?.createCounter('xl1_to_evm_bridge_sentinel_errors_total', { description: 'Number of bridge errors' })
88
+
89
+ const gateway = assertEx(this.params.gateway, () => 'Gateway parameter is required')
90
+ const connection = assertEx(gateway.connectionInstance, () => 'Gateway connection is required')
91
+ const viewer = assertEx(connection.viewer, () => 'Viewer is not defined in gateway connection')
92
+ this._gateway = gateway
93
+ this._connection = connection
94
+ this._viewer = viewer
95
+ }
96
+
97
+ override async reportHandler(payloads?: Payload[]): Promise<Payload[]> {
98
+ if (this._reportMutex.isLocked()) {
99
+ this.logger?.debug(`XL1toEvmBridgeSentinel [${this.id}] is already running, skipping report [${Date.now()}]`)
100
+ return []
101
+ }
102
+ return await this._reportMutex.runExclusive(async () => {
103
+ const response: Payload[] = []
104
+ if (isUndefined(payloads) || payloads.length === 0) {
105
+ await this.processAllBridgeIntents()
106
+ } else {
107
+ // Process provided payloads
108
+ const bridgeIntentsToProcess = payloads.filter(isBridgeIntent)
109
+ for (const bridgeIntent of bridgeIntentsToProcess) {
110
+ const result = await this.processBridgeIntent(bridgeIntent)
111
+ if (isDefined(result)) response.push(...flattenHydratedTransaction(result))
112
+ }
113
+ }
114
+ return response
115
+ })
116
+ }
117
+
118
+ private async processAllBridgeIntents(): Promise<Payload[]> {
119
+ // No payloads to process, just do unprocessed bridge intents since last run
120
+ const results: SignedHydratedTransaction[] = []
121
+ // TODO: Grab from iterable map of pending bridge intents
122
+ const bridgeIntents: BridgeIntent[] = []
123
+
124
+ for (const bridgeIntent of bridgeIntents) {
125
+ const result = await this.processBridgeIntent(bridgeIntent)
126
+ if (isDefined(result)) {
127
+ results.push(result)
128
+ }
129
+ await delay(this.bridgeIntentFulfillmentInterval)
130
+ }
131
+ return flattenHydratedTransactions(results)
132
+ }
133
+
134
+ private async processBridgeIntent(bridgeIntent: BridgeIntent): Promise<SignedHydratedTransaction | undefined> {
135
+ this.logger?.info(`Processing bridge request for ${bridgeIntent.srcAddress} with nonce ${bridgeIntent.nonce}`)
136
+ try {
137
+ this._bridgeCheckCounter?.add(1)
138
+ this._bridgeAttemptsCounter?.add(1)
139
+ // Create single transaction for all claims
140
+ const tx = await this.submitBridgeInitiationTransaction(bridgeIntent, this.gateway)
141
+ this.logger?.info(`Claimed rewards for ${bridgeIntent.srcAddress} with nonce ${bridgeIntent.nonce}`)
142
+ this._bridgeSuccessCounter?.add(1)
143
+ // Return submitted transaction
144
+ return tx
145
+ } catch (error) {
146
+ this._bridgeErrorCounter?.add(1)
147
+ this.logger?.error(`Error claiming rewards for ${bridgeIntent.srcAddress} with nonce ${bridgeIntent.nonce}:`, error)
148
+ }
149
+ }
150
+
151
+ private async submitBridgeInitiationTransaction(
152
+ bridgeIntent: BridgeIntent,
153
+ gateway: XyoGatewayRunner,
154
+ ): Promise<SignedHydratedTransaction | undefined> {
155
+ const currentBlockNumber = await this.viewer.currentBlockNumber()
156
+ const result = await gateway.addPayloadsToChain?.([], [], { nbf: currentBlockNumber, exp: currentBlockNumber + 10 })
157
+ if (isDefined(result)) return result[1]
158
+ }
159
+ }
@@ -0,0 +1 @@
1
+ export * from './XL1toEvmBridgeSentinel.ts'
@@ -0,0 +1 @@
1
+ export * from './XL1toEvmBridgeSentinel/index.ts'
@@ -1,2 +0,0 @@
1
- export * from './requestHandlerValidator.ts';
2
- //# sourceMappingURL=index.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../src/server/routes/bridge/middleware/index.ts"],"names":[],"mappings":"AAAA,cAAc,8BAA8B,CAAA"}
@@ -1,32 +0,0 @@
1
- import type { NextFunction, Request, RequestHandler, Response } from 'express';
2
- import type { ZodType } from 'zod';
3
- import { z } from 'zod';
4
- /**
5
- * Empty Zod schema for requests with no parameters.
6
- */
7
- export declare const EmptyParamsZod: z.ZodObject<{}, z.core.$catchall<z.ZodString>>;
8
- /**
9
- * Empty Zod schema for requests with no query parameters.
10
- */
11
- export declare const EmptyQueryParamsZod: z.ZodObject<{}, z.core.$catchall<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>>;
12
- /**
13
- * Default validation schemas for request handler validator.
14
- */
15
- export declare const ValidateRequestDefaults: {
16
- params: z.ZodObject<{}, z.core.$catchall<z.ZodString>>;
17
- query: z.ZodObject<{}, z.core.$catchall<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>>;
18
- body: z.ZodJSONSchema;
19
- response: z.ZodJSONSchema;
20
- };
21
- /**
22
- * Factory for Express middleware that validates request and response objects using Zod schemas.
23
- * @param schemas The Zod schemas to use for validation.
24
- * @returns A middleware function for validating requests and responses.
25
- */
26
- export declare function requestHandlerValidator<TParams extends typeof EmptyQueryParamsZod | ZodType<Record<string, string>> = typeof EmptyQueryParamsZod, TQuery extends typeof EmptyQueryParamsZod | ZodType<Record<string, string | string[]>> = typeof EmptyQueryParamsZod, TBody extends ZodType<unknown> = ZodType<unknown>, TResponse extends ZodType<unknown> = ZodType<unknown>>(schemas?: Partial<{
27
- body: TBody;
28
- params: TParams;
29
- query: TQuery;
30
- response: TResponse;
31
- }>): (handler: (req: Request<z.core.output<TParams>, z.core.output<TResponse>, z.core.output<TBody>, z.core.output<TQuery>>, res: Response<z.core.output<TResponse>>, next: NextFunction) => unknown) => RequestHandler;
32
- //# sourceMappingURL=requestHandlerValidator.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"requestHandlerValidator.d.ts","sourceRoot":"","sources":["../../../../../../src/server/routes/bridge/middleware/requestHandlerValidator.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,YAAY,EAAE,OAAO,EAAE,cAAc,EAAE,QAAQ,EAChD,MAAM,SAAS,CAAA;AAEhB,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,KAAK,CAAA;AAClC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB;;GAEG;AACH,eAAO,MAAM,cAAc,gDAAoC,CAAA;AAE/D;;GAEG;AACH,eAAO,MAAM,mBAAmB,gGAAoE,CAAA;AAEpG;;GAEG;AACH,eAAO,MAAM,uBAAuB;;;;;CAKnC,CAAA;AAID;;;;GAIG;AACH,wBAAgB,uBAAuB,CACrC,OAAO,SAAS,OAAO,mBAAmB,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,GAAG,OAAO,mBAAmB,EACzG,MAAM,SAAS,OAAO,mBAAmB,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,CAAC,GAAG,OAAO,mBAAmB,EACnH,KAAK,SAAS,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,EACjD,SAAS,SAAS,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,EACrD,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,IAAI,EAAE,KAAK,CAAA;IACX,MAAM,EAAE,OAAO,CAAA;IACf,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,SAAS,CAAA;CACpB,CAAC,IAOQ,SAAS,CAAC,GAAG,EAAE,OAAO,+FAA0B,EAAE,GAAG,EAAE,QAAQ,0BAAK,EAAE,IAAI,EAAE,YAAY,KAAK,OAAO,KAAG,cAAc,CAiE9H"}
@@ -1 +0,0 @@
1
- export * from './requestHandlerValidator.ts'
@@ -1,120 +0,0 @@
1
- /* eslint-disable @typescript-eslint/no-explicit-any */
2
- import type { ExpressError } from '@xylabs/express'
3
- import { isPromise } from '@xylabs/typeof'
4
- import type {
5
- NextFunction, Request, RequestHandler, Response,
6
- } from 'express'
7
- import { ReasonPhrases, StatusCodes } from 'http-status-codes'
8
- import type { ZodType } from 'zod'
9
- import { z } from 'zod'
10
-
11
- /**
12
- * Empty Zod schema for requests with no parameters.
13
- */
14
- export const EmptyParamsZod = z.object({}).catchall(z.string())
15
-
16
- /**
17
- * Empty Zod schema for requests with no query parameters.
18
- */
19
- export const EmptyQueryParamsZod = z.object({}).catchall(z.union([z.string(), z.array(z.string())]))
20
-
21
- /**
22
- * Default validation schemas for request handler validator.
23
- */
24
- export const ValidateRequestDefaults = {
25
- params: EmptyParamsZod,
26
- query: EmptyQueryParamsZod,
27
- body: z.json(),
28
- response: z.json(),
29
- }
30
-
31
- type ValidatableRequestKey = 'params' | 'query' | 'body'
32
-
33
- /**
34
- * Factory for Express middleware that validates request and response objects using Zod schemas.
35
- * @param schemas The Zod schemas to use for validation.
36
- * @returns A middleware function for validating requests and responses.
37
- */
38
- export function requestHandlerValidator<
39
- TParams extends typeof EmptyQueryParamsZod | ZodType<Record<string, string>> = typeof EmptyQueryParamsZod,
40
- TQuery extends typeof EmptyQueryParamsZod | ZodType<Record<string, string | string[]>> = typeof EmptyQueryParamsZod,
41
- TBody extends ZodType<unknown> = ZodType<unknown>,
42
- TResponse extends ZodType<unknown> = ZodType<unknown>,
43
- >(schemas?: Partial<{
44
- body: TBody
45
- params: TParams
46
- query: TQuery
47
- response: TResponse
48
- }>) {
49
- type Params = z.infer<TParams>
50
- type Query = z.infer<TQuery>
51
- type Body = z.infer<TBody>
52
- type Res = z.infer<TResponse>
53
- const validators = { ...ValidateRequestDefaults, ...schemas }
54
-
55
- return (handler: (req: Request<Params, Res, Body, Query>, res: Response<Res>, next: NextFunction) => unknown): RequestHandler => {
56
- return async (req: Request, res: Response, next: NextFunction) => {
57
- const originalJson = res.json.bind(res)
58
- try {
59
- // Validate incoming request
60
- const errors: string[] = []
61
- const keys: ValidatableRequestKey[] = ['params', 'query', 'body']
62
- for (const key of keys) {
63
- const validator = validators[key]
64
- const result = validator.safeParse(req[key])
65
- if (result.success) {
66
- Object.assign(req[key], result.data)
67
- } else {
68
- errors.push(
69
- ...result.error.issues.map(
70
- issue => (issue.path.length === 0)
71
- ? `${key}: ${issue.message}`
72
- : `${key}.${issue.path.join('.')}: ${issue.message}`,
73
- ),
74
- )
75
- }
76
- }
77
-
78
- // If there were validation errors, short-circuit and return Bad Request
79
- if (errors.length > 0) {
80
- const message = errors.join('; ')
81
- const err: ExpressError = new Error(message)
82
- err.name = ReasonPhrases.BAD_REQUEST
83
- err.statusCode = StatusCodes.BAD_REQUEST
84
- next(err)
85
- return false
86
- }
87
-
88
- // Wrap res.json to validate outgoing response
89
- res.json = (data: any) => {
90
- const result = validators.response.safeParse(data)
91
- if (result.success) {
92
- return originalJson(result.data)
93
- } else {
94
- const message = result.error.issues.map(
95
- issue => (issue.path.length === 0)
96
- ? `response: ${issue.message}`
97
- : `response.${issue.path.join('.')}: ${issue.message}`,
98
- ).join('; ')
99
- const err: ExpressError = new Error(message)
100
- err.name = ReasonPhrases.INTERNAL_SERVER_ERROR
101
- err.statusCode = StatusCodes.INTERNAL_SERVER_ERROR
102
-
103
- // Restore original json function in case the error handler wants to use it
104
- res.json = originalJson
105
- throw err
106
- }
107
- }
108
-
109
- // Automatically handle async errors
110
- const result = handler(req as any, res as any, next)
111
- if (result && isPromise(result)) {
112
- await result
113
- }
114
- } catch (err) {
115
- res.json = originalJson
116
- next(err)
117
- }
118
- }
119
- }
120
- }