@reactive-contracts/server 0.1.1-beta → 0.1.3-beta

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reactive-contracts/server",
3
- "version": "0.1.1-beta",
3
+ "version": "0.1.3-beta",
4
4
  "description": "Server-side implementation utilities for Reactive Contracts",
5
5
  "keywords": [
6
6
  "contracts",
@@ -32,10 +32,11 @@
32
32
  "types": "./dist/index.d.ts",
33
33
  "files": [
34
34
  "dist",
35
+ "!dist/**/*.map",
35
36
  "README.md"
36
37
  ],
37
38
  "dependencies": {
38
- "@reactive-contracts/core": "0.1.1-beta"
39
+ "@reactive-contracts/core": "0.1.3-beta"
39
40
  },
40
41
  "devDependencies": {
41
42
  "@types/express": "^5.0.6",
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/implementContract.ts","../src/express.ts"],"names":[],"mappings":";;;AA6BO,SAAS,iBAAA,CACd,UACA,cAAA,EACkC;AAElC,EAAA,IAAI,CAAC,cAAA,CAAe,OAAA,IAAW,OAAO,cAAA,CAAe,YAAY,UAAA,EAAY;AAC3E,IAAA,MAAM,IAAI,KAAA,CAAM,CAAA,SAAA,EAAY,QAAA,CAAS,UAAA,CAAW,IAAI,CAAA,6BAAA,CAA+B,CAAA;AAAA,EACrF;AAEA,EAAA,MAAM,OAAA,GAAU,OAAO,MAAA,EAAiB,OAAA,GAA2B,EAAC,KAAsB;AACxF,IAAA,IAAI;AAEF,MAAA,IAAI,eAAe,QAAA,EAAU;AAC3B,QAAA,MAAM,OAAA,GAAU,MAAM,cAAA,CAAe,QAAA,CAAS,MAAM,CAAA;AACpD,QAAA,IAAI,CAAC,OAAA,EAAS;AACZ,UAAA,MAAM,IAAI,MAAM,6BAA6B,CAAA;AAAA,QAC/C;AAAA,MACF;AAGA,MAAA,MAAM,SAAA,GAAY,KAAK,GAAA,EAAI;AAC3B,MAAA,MAAM,MAAA,GAAS,MAAM,cAAA,CAAe,OAAA,CAAQ,QAAQ,OAAO,CAAA;AAC3D,MAAA,MAAM,aAAA,GAAgB,IAAA,CAAK,GAAA,EAAI,GAAI,SAAA;AAGnC,MAAA,IAAI,QAAA,CAAS,UAAA,CAAW,WAAA,EAAa,OAAA,EAAS;AAC5C,QAAA,MAAM,aAAa,YAAA,CAAa,QAAA,CAAS,UAAA,CAAW,WAAA,CAAY,QAAQ,GAAG,CAAA;AAC3E,QAAA,IAAI,gBAAgB,UAAA,EAAY;AAC9B,UAAA,OAAA,CAAQ,IAAA;AAAA,YACN,YAAY,QAAA,CAAS,UAAA,CAAW,IAAI,CAAA,8BAAA,EAAiC,aAAa,QAAQ,UAAU,CAAA,EAAA;AAAA,WACtG;AAAA,QACF;AAAA,MACF;AAEA,MAAA,OAAO,MAAA;AAAA,IACT,SAAS,KAAA,EAAO;AACd,MAAA,MAAM,MAAM,KAAA,YAAiB,KAAA,GAAQ,KAAA,GAAQ,IAAI,MAAM,eAAe,CAAA;AAGtE,MAAA,cAAA,CAAe,OAAA,GAAU,GAAA,EAAK,MAAA,EAAQ,OAAO,CAAA;AAE7C,MAAA,MAAM,GAAA;AAAA,IACR;AAAA,EACF,CAAA;AAEA,EAAA,OAAO;AAAA,IACL,QAAA;AAAA,IACA,cAAA;AAAA,IACA;AAAA,GACF;AACF;AAKA,SAAS,aAAa,OAAA,EAAyB;AAC7C,EAAA,MAAM,KAAA,GAAQ,OAAA,CAAQ,KAAA,CAAM,iBAAiB,CAAA;AAC7C,EAAA,IAAI,CAAC,SAAS,CAAC,KAAA,CAAM,CAAC,CAAA,IAAK,CAAC,KAAA,CAAM,CAAC,CAAA,EAAG;AACpC,IAAA,MAAM,IAAI,KAAA,CAAM,CAAA,wBAAA,EAA2B,OAAO,CAAA,CAAE,CAAA;AAAA,EACtD;AAEA,EAAA,MAAM,KAAA,GAAQ,QAAA,CAAS,KAAA,CAAM,CAAC,GAAG,EAAE,CAAA;AACnC,EAAA,MAAM,IAAA,GAAO,MAAM,CAAC,CAAA;AAEpB,EAAA,QAAQ,IAAA;AAAM,IACZ,KAAK,IAAA;AACH,MAAA,OAAO,KAAA;AAAA,IACT,KAAK,GAAA;AACH,MAAA,OAAO,KAAA,GAAQ,GAAA;AAAA,IACjB,KAAK,GAAA;AACH,MAAA,OAAO,QAAQ,EAAA,GAAK,GAAA;AAAA,IACtB;AACE,MAAA,MAAM,IAAI,KAAA,CAAM,CAAA,sBAAA,EAAyB,IAAI,CAAA,CAAE,CAAA;AAAA;AAErD;;;AChGO,SAAS,sBACd,QAAA,EACoE;AACpE,EAAA,OAAO,OAAO,GAAA,EAAc,GAAA,EAAe,IAAA,KAAuB;AAChE,IAAA,IAAI;AACF,MAAA,MAAM,SAAA,GAAY,KAAK,GAAA,EAAI;AAG3B,MAAA,MAAM,EAAE,MAAA,GAAS,IAAI,QAAA,EAAU,YAAA,KAAiB,GAAA,CAAI,IAAA;AAGpD,MAAA,IAAI,YAAA,IAAgB,YAAA,KAAiB,QAAA,CAAS,QAAA,CAAS,WAAW,IAAA,EAAM;AACtE,QAAA,GAAA,CAAI,MAAA,CAAO,GAAG,CAAA,CAAE,IAAA,CAAK;AAAA,UACnB,KAAA,EAAO,wBAAA;AAAA,UACP,QAAA,EAAU,QAAA,CAAS,QAAA,CAAS,UAAA,CAAW,IAAA;AAAA,UACvC,QAAA,EAAU;AAAA,SACX,CAAA;AACD,QAAA;AAAA,MACF;AAGA,MAAA,MAAM,WAAA,GAAc,GAAA;AACpB,MAAA,MAAM,YAAY,WAAA,CAAY,IAAA;AAC9B,MAAA,MAAM,MAAA,GAAS,MAAM,QAAA,CAAS,OAAA,CAAQ,MAAA,EAAQ;AAAA,QAC5C,MACE,OAAO,SAAA,KAAc,QAAA,IAAY,SAAA,KAAc,OAC1C,SAAA,GACD,KAAA,CAAA;AAAA;AAAA,QACN,SAAS,GAAA,CAAI,OAAA;AAAA,QACb,IAAI,GAAA,CAAI;AAAA,OACT,CAAA;AAED,MAAA,MAAM,aAAA,GAAgB,IAAA,CAAK,GAAA,EAAI,GAAI,SAAA;AAGnC,MAAA,MAAM,aAAA,GAAgB,qBAAA,CAAsB,QAAA,CAAS,QAAA,EAAU,aAAa,CAAA;AAG5E,MAAA,GAAA,CAAI,IAAA,CAAK;AAAA,QACP,IAAA,EAAM,MAAA;AAAA,QACN,MAAA,EAAQ;AAAA,UACN,OAAA,EAAS,aAAA;AAAA,UACT,SAAA,EAAW,OAAA;AAAA,UACX,YAAA,EAAc;AAAA,SAChB;AAAA,QACA,QAAA,EAAU;AAAA,UACR,aAAA;AAAA,UACA,QAAA,EAAU,KAAA;AAAA,UACV,SAAA,EAAW;AAAA;AACb,OACD,CAAA;AAAA,IACH,SAAS,KAAA,EAAO;AACd,MAAA,IAAA,CAAK,KAAK,CAAA;AAAA,IACZ;AAAA,EACF,CAAA;AACF;AAMO,SAAS,qBAEd,SAAA,EAA2F;AAC3F,EAAA,OAAO,OAAO,GAAA,EAAc,GAAA,EAAe,IAAA,KAAuB;AAChE,IAAA,IAAI;AAEF,MAAA,MAAM,YAAA,GAAe,GAAA,CAAI,MAAA,CAAO,QAAA,IAAY,IAAI,IAAA,CAAK,QAAA;AAErD,MAAA,IAAI,CAAC,YAAA,EAAc;AACjB,QAAA,GAAA,CAAI,MAAA,CAAO,GAAG,CAAA,CAAE,IAAA,CAAK;AAAA,UACnB,KAAA,EAAO;AAAA,SACR,CAAA;AACD,QAAA;AAAA,MACF;AAEA,MAAA,MAAM,QAAA,GAAW,UAAU,YAAY,CAAA;AAEvC,MAAA,IAAI,CAAC,QAAA,EAAU;AACb,QAAA,GAAA,CAAI,MAAA,CAAO,GAAG,CAAA,CAAE,IAAA,CAAK;AAAA,UACnB,KAAA,EAAO,oBAAA;AAAA,UACP,QAAA,EAAU,YAAA;AAAA,UACV,SAAA,EAAW,MAAA,CAAO,IAAA,CAAK,SAAS;AAAA,SACjC,CAAA;AACD,QAAA;AAAA,MACF;AAGA,MAAA,MAAM,OAAA,GAAU,sBAAsB,QAAQ,CAAA;AAC9C,MAAA,MAAM,OAAA,CAAQ,GAAA,EAAK,GAAA,EAAK,IAAI,CAAA;AAAA,IAC9B,SAAS,KAAA,EAAO;AACd,MAAA,IAAA,CAAK,KAAK,CAAA;AAAA,IACZ;AAAA,EACF,CAAA;AACF;AAKA,SAAS,qBAAA,CACP,UACA,aAAA,EACoC;AACpC,EAAA,MAAM,iBAAA,GAAoB,QAAA,CAAS,UAAA,CAAW,WAAA,EAAa,OAAA;AAE3D,EAAA,IAAI,CAAC,iBAAA,EAAmB;AACtB,IAAA,OAAO,QAAA;AAAA,EACT;AAEA,EAAA,MAAM,UAAA,GAAa,gBAAA,CAAiB,iBAAA,CAAkB,GAAG,CAAA;AACzD,EAAA,IAAI,eAAe,IAAA,EAAM;AACvB,IAAA,OAAO,QAAA;AAAA,EACT;AAEA,EAAA,IAAI,iBAAiB,UAAA,EAAY;AAC/B,IAAA,OAAO,QAAA;AAAA,EACT,CAAA,MAAA,IAAW,aAAA,IAAiB,UAAA,GAAa,GAAA,EAAK;AAC5C,IAAA,OAAO,UAAA;AAAA,EACT,CAAA,MAAO;AACL,IAAA,OAAO,UAAA;AAAA,EACT;AACF;AAKA,SAAS,iBAAiB,OAAA,EAAgC;AACxD,EAAA,MAAM,KAAA,GAAQ,OAAA,CAAQ,KAAA,CAAM,iBAAiB,CAAA;AAC7C,EAAA,IAAI,CAAC,SAAS,CAAC,KAAA,CAAM,CAAC,CAAA,IAAK,CAAC,KAAA,CAAM,CAAC,CAAA,EAAG;AACpC,IAAA,OAAO,IAAA;AAAA,EACT;AAEA,EAAA,MAAM,KAAA,GAAQ,QAAA,CAAS,KAAA,CAAM,CAAC,GAAG,EAAE,CAAA;AACnC,EAAA,MAAM,IAAA,GAAO,MAAM,CAAC,CAAA;AAEpB,EAAA,QAAQ,IAAA;AAAM,IACZ,KAAK,IAAA;AACH,MAAA,OAAO,KAAA;AAAA,IACT,KAAK,GAAA;AACH,MAAA,OAAO,KAAA,GAAQ,GAAA;AAAA,IACjB,KAAK,GAAA;AACH,MAAA,OAAO,QAAQ,EAAA,GAAK,GAAA;AAAA,IACtB;AACE,MAAA,OAAO,IAAA;AAAA;AAEb","file":"index.cjs","sourcesContent":["import type { Contract } from '@reactive-contracts/core';\nimport type { ContractImplementation, ContractResolver, ResolverContext } from './types.js';\n\n/**\n * Implement a contract resolver on the server side\n *\n * @example\n * ```typescript\n * const UserProfileResolver = implementContract<\n * { userId: string },\n * { user: { id: string; name: string } }\n * >(UserProfileContract, {\n * async resolve({ userId }, context) {\n * const user = await db.users.findById(userId);\n * return {\n * user: {\n * id: user.id,\n * name: user.name,\n * avatar: user.avatarUrl,\n * },\n * };\n * },\n * cache: {\n * ttl: '5m',\n * tags: (params) => [`user:${params.userId}`],\n * },\n * });\n * ```\n */\nexport function implementContract<TParams, TData>(\n contract: Contract,\n implementation: ContractImplementation<TParams, TData>\n): ContractResolver<TParams, TData> {\n // Validate implementation\n if (!implementation.resolve || typeof implementation.resolve !== 'function') {\n throw new Error(`Contract ${contract.definition.name} must have a resolve function`);\n }\n\n const execute = async (params: TParams, context: ResolverContext = {}): Promise<TData> => {\n try {\n // Validate params if validator provided\n if (implementation.validate) {\n const isValid = await implementation.validate(params);\n if (!isValid) {\n throw new Error('Parameter validation failed');\n }\n }\n\n // Execute the resolver\n const startTime = Date.now();\n const result = await implementation.resolve(params, context);\n const executionTime = Date.now() - startTime;\n\n // Check latency constraints\n if (contract.definition.constraints?.latency) {\n const maxLatency = parseLatency(contract.definition.constraints.latency.max);\n if (executionTime > maxLatency) {\n console.warn(\n `Contract ${contract.definition.name} exceeded latency constraint: ${executionTime}ms > ${maxLatency}ms`\n );\n }\n }\n\n return result;\n } catch (error) {\n const err = error instanceof Error ? error : new Error('Unknown error');\n\n // Call error handler if provided\n implementation.onError?.(err, params, context);\n\n throw err;\n }\n };\n\n return {\n contract,\n implementation,\n execute,\n };\n}\n\n/**\n * Parse latency string to milliseconds\n */\nfunction parseLatency(latency: string): number {\n const match = latency.match(/^(\\d+)(ms|s|m)$/);\n if (!match || !match[1] || !match[2]) {\n throw new Error(`Invalid latency format: ${latency}`);\n }\n\n const value = parseInt(match[1], 10);\n const unit = match[2];\n\n switch (unit) {\n case 'ms':\n return value;\n case 's':\n return value * 1000;\n case 'm':\n return value * 60 * 1000;\n default:\n throw new Error(`Unknown latency unit: ${unit}`);\n }\n}\n","import type { Request, Response, NextFunction } from 'express';\nimport type { Contract } from '@reactive-contracts/core';\nimport type { ContractResolver } from './types.js';\n\n/**\n * Create an Express handler for a contract resolver\n */\nexport function createContractHandler<TParams, TData>(\n resolver: ContractResolver<TParams, TData>\n): (req: Request, res: Response, next: NextFunction) => Promise<void> {\n return async (req: Request, res: Response, next: NextFunction) => {\n try {\n const startTime = Date.now();\n\n // Extract params from request body\n const { params = {}, contract: contractName } = req.body;\n\n // Validate contract name matches\n if (contractName && contractName !== resolver.contract.definition.name) {\n res.status(400).json({\n error: 'Contract name mismatch',\n expected: resolver.contract.definition.name,\n received: contractName,\n });\n return;\n }\n\n // Execute the contract\n const reqWithUser = req as unknown as Record<string, unknown>;\n const userValue = reqWithUser.user;\n const result = await resolver.execute(params, {\n user:\n typeof userValue === 'object' && userValue !== null\n ? (userValue as Record<string, unknown>)\n : undefined, // If using auth middleware\n headers: req.headers as Record<string, string>,\n ip: req.ip,\n });\n\n const executionTime = Date.now() - startTime;\n\n // Evaluate latency status\n const latencyStatus = evaluateLatencyStatus(resolver.contract, executionTime);\n\n // Send response\n res.json({\n data: result,\n status: {\n latency: latencyStatus,\n freshness: 'fresh',\n availability: 'available',\n },\n metadata: {\n executionTime,\n cacheHit: false,\n derivedAt: 'origin',\n },\n });\n } catch (error) {\n next(error);\n }\n };\n}\n\n/**\n * Create an Express router for multiple contract resolvers\n * Uses Record with generic resolver type for flexibility\n */\nexport function createContractRouter<\n TResolvers extends Record<string, ContractResolver<unknown, unknown>>,\n>(resolvers: TResolvers): (req: Request, res: Response, next: NextFunction) => Promise<void> {\n return async (req: Request, res: Response, next: NextFunction) => {\n try {\n // Extract contract name from URL or body\n const contractName = req.params.contract || req.body.contract;\n\n if (!contractName) {\n res.status(400).json({\n error: 'Contract name is required',\n });\n return;\n }\n\n const resolver = resolvers[contractName];\n\n if (!resolver) {\n res.status(404).json({\n error: 'Contract not found',\n contract: contractName,\n available: Object.keys(resolvers),\n });\n return;\n }\n\n // Delegate to contract handler\n const handler = createContractHandler(resolver);\n await handler(req, res, next);\n } catch (error) {\n next(error);\n }\n };\n}\n\n/**\n * Evaluate latency status based on contract constraints\n */\nfunction evaluateLatencyStatus(\n contract: Contract,\n actualLatency: number\n): 'normal' | 'degraded' | 'violated' {\n const latencyConstraint = contract.definition.constraints?.latency;\n\n if (!latencyConstraint) {\n return 'normal';\n }\n\n const maxLatency = parseLatencyToMs(latencyConstraint.max);\n if (maxLatency === null) {\n return 'normal';\n }\n\n if (actualLatency <= maxLatency) {\n return 'normal';\n } else if (actualLatency <= maxLatency * 1.5) {\n return 'degraded';\n } else {\n return 'violated';\n }\n}\n\n/**\n * Parse latency string to milliseconds\n */\nfunction parseLatencyToMs(latency: string): number | null {\n const match = latency.match(/^(\\d+)(ms|s|m)$/);\n if (!match || !match[1] || !match[2]) {\n return null;\n }\n\n const value = parseInt(match[1], 10);\n const unit = match[2];\n\n switch (unit) {\n case 'ms':\n return value;\n case 's':\n return value * 1000;\n case 'm':\n return value * 60 * 1000;\n default:\n return null;\n }\n}\n"]}
package/dist/index.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/implementContract.ts","../src/express.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import type { Contract } from '@reactive-contracts/core';\nimport type { ContractImplementation, ContractResolver, ResolverContext } from './types.js';\n\n/**\n * Implement a contract resolver on the server side\n *\n * @example\n * ```typescript\n * const UserProfileResolver = implementContract<\n * { userId: string },\n * { user: { id: string; name: string } }\n * >(UserProfileContract, {\n * async resolve({ userId }, context) {\n * const user = await db.users.findById(userId);\n * return {\n * user: {\n * id: user.id,\n * name: user.name,\n * avatar: user.avatarUrl,\n * },\n * };\n * },\n * cache: {\n * ttl: '5m',\n * tags: (params) => [`user:${params.userId}`],\n * },\n * });\n * ```\n */\nexport function implementContract<TParams, TData>(\n contract: Contract,\n implementation: ContractImplementation<TParams, TData>\n): ContractResolver<TParams, TData> {\n // Validate implementation\n if (!implementation.resolve || typeof implementation.resolve !== 'function') {\n throw new Error(`Contract ${contract.definition.name} must have a resolve function`);\n }\n\n const execute = async (params: TParams, context: ResolverContext = {}): Promise<TData> => {\n try {\n // Validate params if validator provided\n if (implementation.validate) {\n const isValid = await implementation.validate(params);\n if (!isValid) {\n throw new Error('Parameter validation failed');\n }\n }\n\n // Execute the resolver\n const startTime = Date.now();\n const result = await implementation.resolve(params, context);\n const executionTime = Date.now() - startTime;\n\n // Check latency constraints\n if (contract.definition.constraints?.latency) {\n const maxLatency = parseLatency(contract.definition.constraints.latency.max);\n if (executionTime > maxLatency) {\n console.warn(\n `Contract ${contract.definition.name} exceeded latency constraint: ${executionTime}ms > ${maxLatency}ms`\n );\n }\n }\n\n return result;\n } catch (error) {\n const err = error instanceof Error ? error : new Error('Unknown error');\n\n // Call error handler if provided\n implementation.onError?.(err, params, context);\n\n throw err;\n }\n };\n\n return {\n contract,\n implementation,\n execute,\n };\n}\n\n/**\n * Parse latency string to milliseconds\n */\nfunction parseLatency(latency: string): number {\n const match = latency.match(/^(\\d+)(ms|s|m)$/);\n if (!match || !match[1] || !match[2]) {\n throw new Error(`Invalid latency format: ${latency}`);\n }\n\n const value = parseInt(match[1], 10);\n const unit = match[2];\n\n switch (unit) {\n case 'ms':\n return value;\n case 's':\n return value * 1000;\n case 'm':\n return value * 60 * 1000;\n default:\n throw new Error(`Unknown latency unit: ${unit}`);\n }\n}\n","import type { Request, Response, NextFunction } from 'express';\nimport type { Contract } from '@reactive-contracts/core';\nimport type { ContractResolver } from './types.js';\n\n/**\n * Create an Express handler for a contract resolver\n */\nexport function createContractHandler<TParams, TData>(\n resolver: ContractResolver<TParams, TData>\n): (req: Request, res: Response, next: NextFunction) => Promise<void> {\n return async (req: Request, res: Response, next: NextFunction) => {\n try {\n const startTime = Date.now();\n\n // Extract params from request body\n const { params = {}, contract: contractName } = req.body;\n\n // Validate contract name matches\n if (contractName && contractName !== resolver.contract.definition.name) {\n res.status(400).json({\n error: 'Contract name mismatch',\n expected: resolver.contract.definition.name,\n received: contractName,\n });\n return;\n }\n\n // Execute the contract\n const reqWithUser = req as unknown as Record<string, unknown>;\n const userValue = reqWithUser.user;\n const result = await resolver.execute(params, {\n user:\n typeof userValue === 'object' && userValue !== null\n ? (userValue as Record<string, unknown>)\n : undefined, // If using auth middleware\n headers: req.headers as Record<string, string>,\n ip: req.ip,\n });\n\n const executionTime = Date.now() - startTime;\n\n // Evaluate latency status\n const latencyStatus = evaluateLatencyStatus(resolver.contract, executionTime);\n\n // Send response\n res.json({\n data: result,\n status: {\n latency: latencyStatus,\n freshness: 'fresh',\n availability: 'available',\n },\n metadata: {\n executionTime,\n cacheHit: false,\n derivedAt: 'origin',\n },\n });\n } catch (error) {\n next(error);\n }\n };\n}\n\n/**\n * Create an Express router for multiple contract resolvers\n * Uses Record with generic resolver type for flexibility\n */\nexport function createContractRouter<\n TResolvers extends Record<string, ContractResolver<unknown, unknown>>,\n>(resolvers: TResolvers): (req: Request, res: Response, next: NextFunction) => Promise<void> {\n return async (req: Request, res: Response, next: NextFunction) => {\n try {\n // Extract contract name from URL or body\n const contractName = req.params.contract || req.body.contract;\n\n if (!contractName) {\n res.status(400).json({\n error: 'Contract name is required',\n });\n return;\n }\n\n const resolver = resolvers[contractName];\n\n if (!resolver) {\n res.status(404).json({\n error: 'Contract not found',\n contract: contractName,\n available: Object.keys(resolvers),\n });\n return;\n }\n\n // Delegate to contract handler\n const handler = createContractHandler(resolver);\n await handler(req, res, next);\n } catch (error) {\n next(error);\n }\n };\n}\n\n/**\n * Evaluate latency status based on contract constraints\n */\nfunction evaluateLatencyStatus(\n contract: Contract,\n actualLatency: number\n): 'normal' | 'degraded' | 'violated' {\n const latencyConstraint = contract.definition.constraints?.latency;\n\n if (!latencyConstraint) {\n return 'normal';\n }\n\n const maxLatency = parseLatencyToMs(latencyConstraint.max);\n if (maxLatency === null) {\n return 'normal';\n }\n\n if (actualLatency <= maxLatency) {\n return 'normal';\n } else if (actualLatency <= maxLatency * 1.5) {\n return 'degraded';\n } else {\n return 'violated';\n }\n}\n\n/**\n * Parse latency string to milliseconds\n */\nfunction parseLatencyToMs(latency: string): number | null {\n const match = latency.match(/^(\\d+)(ms|s|m)$/);\n if (!match || !match[1] || !match[2]) {\n return null;\n }\n\n const value = parseInt(match[1], 10);\n const unit = match[2];\n\n switch (unit) {\n case 'ms':\n return value;\n case 's':\n return value * 1000;\n case 'm':\n return value * 60 * 1000;\n default:\n return null;\n }\n}\n"]}