autotel-hono 0.4.43 → 0.4.45

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/dist/index.cjs CHANGED
@@ -166,5 +166,3 @@ function otel(userConfig = {}) {
166
166
  }
167
167
  //#endregion
168
168
  exports.otel = otel;
169
-
170
- //# sourceMappingURL=index.cjs.map
package/dist/index.d.cts CHANGED
@@ -59,5 +59,4 @@ type OtelConfig = {
59
59
  };
60
60
  declare function otel(userConfig?: OtelConfig): MiddlewareHandler;
61
61
  //#endregion
62
- export { OtelConfig, otel };
63
- //# sourceMappingURL=index.d.cts.map
62
+ export { OtelConfig, otel };
package/dist/index.d.ts CHANGED
@@ -59,5 +59,4 @@ type OtelConfig = {
59
59
  };
60
60
  declare function otel(userConfig?: OtelConfig): MiddlewareHandler;
61
61
  //#endregion
62
- export { OtelConfig, otel };
63
- //# sourceMappingURL=index.d.ts.map
62
+ export { OtelConfig, otel };
package/dist/index.js CHANGED
@@ -165,5 +165,3 @@ function otel(userConfig = {}) {
165
165
  }
166
166
  //#endregion
167
167
  export { otel };
168
-
169
- //# sourceMappingURL=index.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "autotel-hono",
3
- "version": "0.4.43",
3
+ "version": "0.4.45",
4
4
  "description": "OpenTelemetry Hono middleware powered by autotel",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -19,8 +19,8 @@
19
19
  "README.md"
20
20
  ],
21
21
  "dependencies": {
22
- "autotel": "6.0.0",
23
- "autotel-adapters": "2.0.1"
22
+ "autotel": "6.2.0",
23
+ "autotel-adapters": "2.0.3"
24
24
  },
25
25
  "peerDependencies": {
26
26
  "hono": ">=4.12.31"
@@ -31,9 +31,9 @@
31
31
  }
32
32
  },
33
33
  "devDependencies": {
34
- "@types/node": "^26.1.1",
35
- "hono": "^4.12.28",
36
- "tsdown": "^0.22.12",
34
+ "@types/node": "^26.1.2",
35
+ "hono": "^4.12.32",
36
+ "tsdown": "^0.22.14",
37
37
  "typescript": "^6.0.3",
38
38
  "vitest": "^4.1.10"
39
39
  },
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.cjs","names":["HTTPAttributes","ServiceAttributes","SpanStatusCode","propagation","otelContext","SpanKind","URLAttributes"],"sources":["../src/metrics.ts","../src/index.ts"],"sourcesContent":["/**\n * HTTP server metrics per OTel semantic conventions.\n *\n * Lazy-initialized request duration histogram and active-requests up/down counter.\n */\n\ntype AttributeValue = string | number | boolean | undefined;\nexport type Attributes = Record<string, AttributeValue>;\n\ntype Histogram = {\n record: (value: number, attrs?: Attributes) => void;\n};\n\ntype UpDownCounter = {\n add: (value: number, attrs?: Attributes) => void;\n};\n\nexport type Meter = {\n createHistogram: (\n name: string,\n options?: {\n description?: string;\n unit?: string;\n advice?: { explicitBucketBoundaries?: number[] };\n },\n ) => Histogram;\n createUpDownCounter: (\n name: string,\n options?: {\n description?: string;\n },\n ) => UpDownCounter;\n};\n\n/** OTel HTTP server metric names (stable convention names) */\nconst METRIC_HTTP_SERVER_REQUEST_DURATION = 'http.server.request.duration';\nconst METRIC_HTTP_SERVER_ACTIVE_REQUESTS = 'http.server.active_requests';\n\n/** Recommended bucket boundaries for request duration (seconds) */\nconst HTTP_DURATION_BUCKETS = [\n 0.005, 0.01, 0.025, 0.05, 0.075, 0.1, 0.25, 0.5, 0.75, 1, 2.5, 5, 7.5, 10,\n];\n\nexport type HttpMetricsConfig = {\n meter: Meter;\n captureRequestDuration?: boolean;\n captureActiveRequests?: boolean;\n};\n\nexport function createRequestDurationTracker(config: HttpMetricsConfig): {\n record: (durationSeconds: number, attrs: Attributes) => void;\n} {\n if (config.captureRequestDuration === false) {\n return { record: () => {} };\n }\n const histogram = config.meter.createHistogram(\n METRIC_HTTP_SERVER_REQUEST_DURATION,\n {\n description: 'Duration of HTTP server requests in seconds',\n unit: 's',\n advice: { explicitBucketBoundaries: HTTP_DURATION_BUCKETS },\n },\n );\n return {\n record(durationSeconds: number, attrs: Attributes) {\n histogram.record(durationSeconds, attrs);\n },\n };\n}\n\nexport function createActiveRequestsTracker(config: HttpMetricsConfig):\n | {\n increment: (attrs: Attributes) => void;\n decrement: (attrs: Attributes) => void;\n }\n | undefined {\n if (config.captureActiveRequests === false) {\n return undefined;\n }\n const counter = config.meter.createUpDownCounter(\n METRIC_HTTP_SERVER_ACTIVE_REQUESTS,\n {\n description: 'Number of active (in-flight) HTTP server requests',\n },\n );\n return {\n increment(attrs: Attributes) {\n counter.add(1, attrs);\n },\n decrement(attrs: Attributes) {\n counter.add(-1, attrs);\n },\n };\n}\n","import type { Span, Tracer } from 'autotel';\nimport {\n getTracer,\n getMeter,\n context as otelContext,\n propagation,\n SpanKind,\n SpanStatusCode,\n HTTPAttributes,\n URLAttributes,\n ServiceAttributes,\n httpRequestHeaderAttribute,\n httpResponseHeaderAttribute,\n} from 'autotel';\nimport type { MiddlewareHandler, Context } from 'hono';\nimport { createMiddleware } from 'hono/factory';\nimport { routePath } from 'hono/route';\nimport {\n createRequestDurationTracker,\n createActiveRequestsTracker,\n type HttpMetricsConfig,\n} from './metrics';\n\nconst INSTRUMENTATION_SCOPE_NAME = 'autotel-hono';\n\ntype TimeInput = number | [number, number];\ntype TracerProvider = { getTracer(name: string, version?: string): Tracer };\ntype Meter = HttpMetricsConfig['meter'];\ntype MeterProvider = { getMeter(name: string, version?: string): Meter };\n\nfunction now(): number {\n const p = (globalThis as unknown as { performance?: { now(): number } })\n .performance;\n return p?.now() ?? Date.now();\n}\n\nexport type OtelConfig = {\n tracer?: Tracer;\n tracerProvider?: TracerProvider;\n meter?: Meter;\n meterProvider?: MeterProvider;\n tracerName?: string;\n spanNameFactory?: (c: Context) => string;\n captureRequestHeaders?: string[];\n captureResponseHeaders?: string[];\n captureActiveRequests?: boolean;\n captureRequestDuration?: boolean;\n serviceName?: string;\n serviceVersion?: string;\n disableTracing?: boolean;\n getTime?(): TimeInput;\n};\n\ntype NormalizedOtelConfig = OtelConfig & {\n requestHeaderSet: Set<string>;\n responseHeaderSet: Set<string>;\n captureActiveRequests: boolean;\n captureRequestDuration: boolean;\n};\n\nfunction normalizeConfig(config: OtelConfig = {}): NormalizedOtelConfig {\n const reqHeadersSrc = [...(config.captureRequestHeaders ?? [])];\n const resHeadersSrc = [...(config.captureResponseHeaders ?? [])];\n const requestHeaderSet = new Set(reqHeadersSrc.map((h) => h.toLowerCase()));\n const responseHeaderSet = new Set(resHeadersSrc.map((h) => h.toLowerCase()));\n return {\n ...config,\n requestHeaderSet,\n responseHeaderSet,\n captureActiveRequests: config.captureActiveRequests ?? true,\n captureRequestDuration: config.captureRequestDuration ?? true,\n };\n}\n\nfunction resolveTracer(config: NormalizedOtelConfig): Tracer | undefined {\n if (config.disableTracing) return undefined;\n if (config.tracer) return config.tracer;\n if (config.tracerProvider) {\n return config.tracerProvider.getTracer(\n config.tracerName ?? INSTRUMENTATION_SCOPE_NAME,\n config.serviceVersion,\n );\n }\n return getTracer(\n config.tracerName ?? INSTRUMENTATION_SCOPE_NAME,\n config.serviceVersion,\n );\n}\n\nfunction resolveMeter(config: NormalizedOtelConfig): Meter {\n if (config.meter) return config.meter;\n if (config.meterProvider) {\n return config.meterProvider.getMeter(\n INSTRUMENTATION_SCOPE_NAME,\n config.serviceVersion,\n );\n }\n return getMeter();\n}\n\nexport function otel(userConfig: OtelConfig = {}): MiddlewareHandler {\n const config = normalizeConfig(userConfig);\n const tracer = resolveTracer(config);\n const meter = resolveMeter(config);\n\n const metricsConfig: HttpMetricsConfig = {\n meter,\n captureRequestDuration: config.captureRequestDuration,\n captureActiveRequests: config.captureActiveRequests,\n };\n const requestDuration = createRequestDurationTracker(metricsConfig);\n const activeReqs = createActiveRequestsTracker(metricsConfig);\n\n const spanName = (c: Context) =>\n config.spanNameFactory?.(c) ?? `${c.req.method} ${routePath(c)}`;\n\n return createMiddleware(async (c, next) => {\n const method = c.req.method;\n\n const stableAttrs: Record<string, string | number | undefined> = {\n [HTTPAttributes.requestMethod]: method,\n [ServiceAttributes.name]: config.serviceName,\n [ServiceAttributes.version]: config.serviceVersion,\n };\n\n activeReqs?.increment(stableAttrs);\n const startTime = now();\n\n const deferredRequestHeaderAttributes: Record<string, string> = {};\n const reqHeaders = c.req.raw.headers;\n for (const [rawName, value] of reqHeaders.entries()) {\n const name = rawName.toLowerCase();\n if (config.requestHeaderSet.has(name)) {\n deferredRequestHeaderAttributes[httpRequestHeaderAttribute(name)] =\n typeof value === 'string' ? value : (value[0] ?? '');\n }\n }\n\n const finalize = (span?: Span, error?: unknown) => {\n try {\n const status = c.res.status;\n\n if (span) {\n for (const [name, value] of c.res.headers.entries()) {\n const lower = name.toLowerCase();\n if (config.responseHeaderSet.has(lower)) {\n span.setAttribute(httpResponseHeaderAttribute(lower), value);\n }\n }\n span.setAttribute(HTTPAttributes.responseStatusCode, status);\n if (status >= 500) {\n span.setStatus({ code: SpanStatusCode.ERROR });\n }\n if (error) {\n try {\n span.recordException(error as Error);\n } catch {\n // Ignore errors when recording exception\n }\n span.setStatus({ code: SpanStatusCode.ERROR });\n }\n }\n } finally {\n activeReqs?.decrement(stableAttrs);\n span?.setAttribute(HTTPAttributes.route, routePath(c));\n span?.updateName(spanName(c));\n const durationSeconds = (now() - startTime) / 1000;\n requestDuration.record(durationSeconds, {\n ...stableAttrs,\n [HTTPAttributes.route]: routePath(c),\n [HTTPAttributes.responseStatusCode]: c.res.status,\n });\n }\n };\n\n if (!tracer) {\n try {\n await next();\n finalize();\n } catch (error) {\n finalize(undefined, error);\n throw error;\n }\n return;\n }\n\n const parent = propagation.extract(otelContext.active(), c.req.header());\n return tracer.startActiveSpan(\n spanName(c),\n {\n kind: SpanKind.SERVER,\n startTime: config.getTime?.(),\n attributes: {\n ...stableAttrs,\n [URLAttributes.full]: c.req.url,\n [HTTPAttributes.route]: routePath(c),\n },\n },\n parent,\n async (span) => {\n try {\n for (const [k, v] of Object.entries(\n deferredRequestHeaderAttributes,\n )) {\n span.setAttribute(k, v);\n }\n await next();\n finalize(span, c.error);\n } catch (error) {\n finalize(span, error);\n throw error;\n } finally {\n span.end(config.getTime?.());\n }\n },\n );\n });\n}\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.cts","names":[],"sources":["../src/metrics.ts","../src/index.ts"],"mappings":";;;;;;;;KAMK;KACO,aAAa,eAAe;KAEnC;EACH,SAAS,eAAe,QAAQ;;KAG7B;EACH,MAAM,eAAe,QAAQ;;KAGnB;EACV,kBACE,cACA;IACE;IACA;IACA;MAAW;;QAEV;EACL,sBACE,cACA;IACE;QAEC;;KAYK;EACV,OAAO;EACP;EACA;;;;KCrBG;KACA;EAAmB,UAAU,cAAc,mBAAmB;;KAC9D,QAAQ;KACR;EAAkB,SAAS,cAAc,mBAAmB;;KAQrD;EACV,SAAS;EACT,iBAAiB;EACjB,QAAQ;EACR,gBAAgB;EAChB;EACA,mBAAmB,GAAG;EACtB;EACA;EACA;EACA;EACA;EACA;EACA;EACA,YAAY;;iBAkDE,KAAK,aAAY,aAAkB"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../src/metrics.ts","../src/index.ts"],"mappings":";;;;;;;;KAMK;KACO,aAAa,eAAe;KAEnC;EACH,SAAS,eAAe,QAAQ;;KAG7B;EACH,MAAM,eAAe,QAAQ;;KAGnB;EACV,kBACE,cACA;IACE;IACA;IACA;MAAW;;QAEV;EACL,sBACE,cACA;IACE;QAEC;;KAYK;EACV,OAAO;EACP;EACA;;;;KCrBG;KACA;EAAmB,UAAU,cAAc,mBAAmB;;KAC9D,QAAQ;KACR;EAAkB,SAAS,cAAc,mBAAmB;;KAQrD;EACV,SAAS;EACT,iBAAiB;EACjB,QAAQ;EACR,gBAAgB;EAChB;EACA,mBAAmB,GAAG;EACtB;EACA;EACA;EACA;EACA;EACA;EACA;EACA,YAAY;;iBAkDE,KAAK,aAAY,aAAkB"}
package/dist/index.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","names":["otelContext"],"sources":["../src/metrics.ts","../src/index.ts"],"sourcesContent":["/**\n * HTTP server metrics per OTel semantic conventions.\n *\n * Lazy-initialized request duration histogram and active-requests up/down counter.\n */\n\ntype AttributeValue = string | number | boolean | undefined;\nexport type Attributes = Record<string, AttributeValue>;\n\ntype Histogram = {\n record: (value: number, attrs?: Attributes) => void;\n};\n\ntype UpDownCounter = {\n add: (value: number, attrs?: Attributes) => void;\n};\n\nexport type Meter = {\n createHistogram: (\n name: string,\n options?: {\n description?: string;\n unit?: string;\n advice?: { explicitBucketBoundaries?: number[] };\n },\n ) => Histogram;\n createUpDownCounter: (\n name: string,\n options?: {\n description?: string;\n },\n ) => UpDownCounter;\n};\n\n/** OTel HTTP server metric names (stable convention names) */\nconst METRIC_HTTP_SERVER_REQUEST_DURATION = 'http.server.request.duration';\nconst METRIC_HTTP_SERVER_ACTIVE_REQUESTS = 'http.server.active_requests';\n\n/** Recommended bucket boundaries for request duration (seconds) */\nconst HTTP_DURATION_BUCKETS = [\n 0.005, 0.01, 0.025, 0.05, 0.075, 0.1, 0.25, 0.5, 0.75, 1, 2.5, 5, 7.5, 10,\n];\n\nexport type HttpMetricsConfig = {\n meter: Meter;\n captureRequestDuration?: boolean;\n captureActiveRequests?: boolean;\n};\n\nexport function createRequestDurationTracker(config: HttpMetricsConfig): {\n record: (durationSeconds: number, attrs: Attributes) => void;\n} {\n if (config.captureRequestDuration === false) {\n return { record: () => {} };\n }\n const histogram = config.meter.createHistogram(\n METRIC_HTTP_SERVER_REQUEST_DURATION,\n {\n description: 'Duration of HTTP server requests in seconds',\n unit: 's',\n advice: { explicitBucketBoundaries: HTTP_DURATION_BUCKETS },\n },\n );\n return {\n record(durationSeconds: number, attrs: Attributes) {\n histogram.record(durationSeconds, attrs);\n },\n };\n}\n\nexport function createActiveRequestsTracker(config: HttpMetricsConfig):\n | {\n increment: (attrs: Attributes) => void;\n decrement: (attrs: Attributes) => void;\n }\n | undefined {\n if (config.captureActiveRequests === false) {\n return undefined;\n }\n const counter = config.meter.createUpDownCounter(\n METRIC_HTTP_SERVER_ACTIVE_REQUESTS,\n {\n description: 'Number of active (in-flight) HTTP server requests',\n },\n );\n return {\n increment(attrs: Attributes) {\n counter.add(1, attrs);\n },\n decrement(attrs: Attributes) {\n counter.add(-1, attrs);\n },\n };\n}\n","import type { Span, Tracer } from 'autotel';\nimport {\n getTracer,\n getMeter,\n context as otelContext,\n propagation,\n SpanKind,\n SpanStatusCode,\n HTTPAttributes,\n URLAttributes,\n ServiceAttributes,\n httpRequestHeaderAttribute,\n httpResponseHeaderAttribute,\n} from 'autotel';\nimport type { MiddlewareHandler, Context } from 'hono';\nimport { createMiddleware } from 'hono/factory';\nimport { routePath } from 'hono/route';\nimport {\n createRequestDurationTracker,\n createActiveRequestsTracker,\n type HttpMetricsConfig,\n} from './metrics';\n\nconst INSTRUMENTATION_SCOPE_NAME = 'autotel-hono';\n\ntype TimeInput = number | [number, number];\ntype TracerProvider = { getTracer(name: string, version?: string): Tracer };\ntype Meter = HttpMetricsConfig['meter'];\ntype MeterProvider = { getMeter(name: string, version?: string): Meter };\n\nfunction now(): number {\n const p = (globalThis as unknown as { performance?: { now(): number } })\n .performance;\n return p?.now() ?? Date.now();\n}\n\nexport type OtelConfig = {\n tracer?: Tracer;\n tracerProvider?: TracerProvider;\n meter?: Meter;\n meterProvider?: MeterProvider;\n tracerName?: string;\n spanNameFactory?: (c: Context) => string;\n captureRequestHeaders?: string[];\n captureResponseHeaders?: string[];\n captureActiveRequests?: boolean;\n captureRequestDuration?: boolean;\n serviceName?: string;\n serviceVersion?: string;\n disableTracing?: boolean;\n getTime?(): TimeInput;\n};\n\ntype NormalizedOtelConfig = OtelConfig & {\n requestHeaderSet: Set<string>;\n responseHeaderSet: Set<string>;\n captureActiveRequests: boolean;\n captureRequestDuration: boolean;\n};\n\nfunction normalizeConfig(config: OtelConfig = {}): NormalizedOtelConfig {\n const reqHeadersSrc = [...(config.captureRequestHeaders ?? [])];\n const resHeadersSrc = [...(config.captureResponseHeaders ?? [])];\n const requestHeaderSet = new Set(reqHeadersSrc.map((h) => h.toLowerCase()));\n const responseHeaderSet = new Set(resHeadersSrc.map((h) => h.toLowerCase()));\n return {\n ...config,\n requestHeaderSet,\n responseHeaderSet,\n captureActiveRequests: config.captureActiveRequests ?? true,\n captureRequestDuration: config.captureRequestDuration ?? true,\n };\n}\n\nfunction resolveTracer(config: NormalizedOtelConfig): Tracer | undefined {\n if (config.disableTracing) return undefined;\n if (config.tracer) return config.tracer;\n if (config.tracerProvider) {\n return config.tracerProvider.getTracer(\n config.tracerName ?? INSTRUMENTATION_SCOPE_NAME,\n config.serviceVersion,\n );\n }\n return getTracer(\n config.tracerName ?? INSTRUMENTATION_SCOPE_NAME,\n config.serviceVersion,\n );\n}\n\nfunction resolveMeter(config: NormalizedOtelConfig): Meter {\n if (config.meter) return config.meter;\n if (config.meterProvider) {\n return config.meterProvider.getMeter(\n INSTRUMENTATION_SCOPE_NAME,\n config.serviceVersion,\n );\n }\n return getMeter();\n}\n\nexport function otel(userConfig: OtelConfig = {}): MiddlewareHandler {\n const config = normalizeConfig(userConfig);\n const tracer = resolveTracer(config);\n const meter = resolveMeter(config);\n\n const metricsConfig: HttpMetricsConfig = {\n meter,\n captureRequestDuration: config.captureRequestDuration,\n captureActiveRequests: config.captureActiveRequests,\n };\n const requestDuration = createRequestDurationTracker(metricsConfig);\n const activeReqs = createActiveRequestsTracker(metricsConfig);\n\n const spanName = (c: Context) =>\n config.spanNameFactory?.(c) ?? `${c.req.method} ${routePath(c)}`;\n\n return createMiddleware(async (c, next) => {\n const method = c.req.method;\n\n const stableAttrs: Record<string, string | number | undefined> = {\n [HTTPAttributes.requestMethod]: method,\n [ServiceAttributes.name]: config.serviceName,\n [ServiceAttributes.version]: config.serviceVersion,\n };\n\n activeReqs?.increment(stableAttrs);\n const startTime = now();\n\n const deferredRequestHeaderAttributes: Record<string, string> = {};\n const reqHeaders = c.req.raw.headers;\n for (const [rawName, value] of reqHeaders.entries()) {\n const name = rawName.toLowerCase();\n if (config.requestHeaderSet.has(name)) {\n deferredRequestHeaderAttributes[httpRequestHeaderAttribute(name)] =\n typeof value === 'string' ? value : (value[0] ?? '');\n }\n }\n\n const finalize = (span?: Span, error?: unknown) => {\n try {\n const status = c.res.status;\n\n if (span) {\n for (const [name, value] of c.res.headers.entries()) {\n const lower = name.toLowerCase();\n if (config.responseHeaderSet.has(lower)) {\n span.setAttribute(httpResponseHeaderAttribute(lower), value);\n }\n }\n span.setAttribute(HTTPAttributes.responseStatusCode, status);\n if (status >= 500) {\n span.setStatus({ code: SpanStatusCode.ERROR });\n }\n if (error) {\n try {\n span.recordException(error as Error);\n } catch {\n // Ignore errors when recording exception\n }\n span.setStatus({ code: SpanStatusCode.ERROR });\n }\n }\n } finally {\n activeReqs?.decrement(stableAttrs);\n span?.setAttribute(HTTPAttributes.route, routePath(c));\n span?.updateName(spanName(c));\n const durationSeconds = (now() - startTime) / 1000;\n requestDuration.record(durationSeconds, {\n ...stableAttrs,\n [HTTPAttributes.route]: routePath(c),\n [HTTPAttributes.responseStatusCode]: c.res.status,\n });\n }\n };\n\n if (!tracer) {\n try {\n await next();\n finalize();\n } catch (error) {\n finalize(undefined, error);\n throw error;\n }\n return;\n }\n\n const parent = propagation.extract(otelContext.active(), c.req.header());\n return tracer.startActiveSpan(\n spanName(c),\n {\n kind: SpanKind.SERVER,\n startTime: config.getTime?.(),\n attributes: {\n ...stableAttrs,\n [URLAttributes.full]: c.req.url,\n [HTTPAttributes.route]: routePath(c),\n },\n },\n parent,\n async (span) => {\n try {\n for (const [k, v] of Object.entries(\n deferredRequestHeaderAttributes,\n )) {\n span.setAttribute(k, v);\n }\n await next();\n finalize(span, c.error);\n } catch (error) {\n finalize(span, error);\n throw error;\n } finally {\n span.end(config.getTime?.());\n }\n },\n );\n });\n}\n"],"mappings":"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"}