@shware/http 3.1.1 → 3.2.1
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/hono/validator.cjs.map +1 -1
- package/dist/hono/validator.mjs.map +1 -1
- package/dist/index.cjs +2 -0
- package/dist/index.d.cts +2 -1
- package/dist/index.d.mts +2 -1
- package/dist/index.mjs +2 -1
- package/dist/utils/google-tag-gateway.cjs +10 -7
- package/dist/utils/google-tag-gateway.cjs.map +1 -1
- package/dist/utils/google-tag-gateway.mjs +10 -7
- package/dist/utils/google-tag-gateway.mjs.map +1 -1
- package/dist/utils/timing.cjs +1 -1
- package/dist/utils/timing.cjs.map +1 -1
- package/dist/utils/timing.mjs +1 -1
- package/dist/utils/timing.mjs.map +1 -1
- package/dist/webhook/index.cjs +3 -3
- package/dist/webhook/index.cjs.map +1 -1
- package/dist/webhook/index.mjs +3 -3
- package/dist/webhook/index.mjs.map +1 -1
- package/package.json +9 -9
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validator.cjs","names":["Details","Status","NEVER"],"sources":["../../src/hono/validator.ts"],"sourcesContent":["import type { ValidationTargets } from 'hono';\nimport { validator } from 'hono/validator';\nimport type { ZodType, output as outputV4 } from 'zod';\nimport {\n NEVER,\n type ZodMiniType,\n type output as outputMini,\n pipe,\n string,\n transform,\n} from 'zod/mini';\nimport { type BadRequest, Details } from '../error/detail';\nimport { Status } from '../error/status';\n\nexport function zValidator<S extends ZodType | ZodMiniType>(\n target: keyof ValidationTargets,\n schema: S\n) {\n return validator(target, async (value) => {\n const result = await schema.safeParseAsync(value);\n if (result.success) return result.data as S extends ZodType ? outputV4<S> : outputMini<S>;\n\n const fieldViolations: BadRequest['fieldViolations'] = result.error.issues.map(\n ({ code, path, message }) => ({\n field: path.join('.'),\n description: message,\n reason: code.toUpperCase(),\n localizedMessage: { locale: 'en-US', message
|
|
1
|
+
{"version":3,"file":"validator.cjs","names":["Details","Status","NEVER"],"sources":["../../src/hono/validator.ts"],"sourcesContent":["import type { ValidationTargets } from 'hono';\nimport { validator } from 'hono/validator';\nimport type { ZodType, output as outputV4 } from 'zod';\nimport {\n NEVER,\n type ZodMiniType,\n type output as outputMini,\n pipe,\n string,\n transform,\n} from 'zod/mini';\nimport { type BadRequest, Details } from '../error/detail';\nimport { Status } from '../error/status';\n\nexport function zValidator<S extends ZodType | ZodMiniType>(\n target: keyof ValidationTargets,\n schema: S\n) {\n return validator(target, async (value) => {\n const result = await schema.safeParseAsync(value);\n if (result.success) return result.data as S extends ZodType ? outputV4<S> : outputMini<S>;\n\n const fieldViolations: BadRequest['fieldViolations'] = result.error.issues.map(\n ({ code, path, message }) => ({\n field: path.join('.'),\n description: message,\n reason: code.toUpperCase(),\n localizedMessage: { locale: 'en-US', message },\n })\n );\n const details = Details.new().badRequest({ fieldViolations });\n throw Status.invalidArgument().error(details);\n });\n}\n\nexport const bigintId = pipe(\n string(),\n transform((input, ctx) => {\n if (!/^(0|[1-9]\\d{0,19})$/.test(input)) {\n const message = `Invalid bigint id: ${input}`;\n ctx.issues.push({ code: 'custom', input, message });\n return NEVER;\n }\n try {\n return BigInt(input);\n } catch {\n const message = `Parse bigint id: ${input} failed`;\n ctx.issues.push({ code: 'custom', input, message });\n return NEVER;\n }\n })\n);\n"],"mappings":";;;;;;AAcA,SAAgB,WACd,QACA,QACA;CACA,QAAA,GAAA,eAAA,UAAA,CAAiB,QAAQ,OAAO,UAAU;EACxC,MAAM,SAAS,MAAM,OAAO,eAAe,KAAK;EAChD,IAAI,OAAO,SAAS,OAAO,OAAO;EAElC,MAAM,kBAAiD,OAAO,MAAM,OAAO,KACxE,EAAE,MAAM,MAAM,eAAe;GAC5B,OAAO,KAAK,KAAK,GAAG;GACpB,aAAa;GACb,QAAQ,KAAK,YAAY;GACzB,kBAAkB;IAAE,QAAQ;IAAS;GAAQ;EAC/C,EACF;EACA,MAAM,UAAUA,qBAAAA,QAAQ,IAAI,CAAC,CAAC,WAAW,EAAE,gBAAgB,CAAC;EAC5D,MAAMC,qBAAAA,OAAO,gBAAgB,CAAC,CAAC,MAAM,OAAO;CAC9C,CAAC;AACH;AAEA,MAAa,YAAA,GAAA,SAAA,KAAA,EAAA,GAAA,SAAA,OAAA,CACJ,IAAA,GAAA,SAAA,UAAA,EACI,OAAO,QAAQ;CACxB,IAAI,CAAC,sBAAsB,KAAK,KAAK,GAAG;EACtC,MAAM,UAAU,sBAAsB;EACtC,IAAI,OAAO,KAAK;GAAE,MAAM;GAAU;GAAO;EAAQ,CAAC;EAClD,OAAOC,SAAAA;CACT;CACA,IAAI;EACF,OAAO,OAAO,KAAK;CACrB,QAAQ;EACN,MAAM,UAAU,oBAAoB,MAAM;EAC1C,IAAI,OAAO,KAAK;GAAE,MAAM;GAAU;GAAO;EAAQ,CAAC;EAClD,OAAOA,SAAAA;CACT;AACF,CAAC,CACH"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validator.mjs","names":[],"sources":["../../src/hono/validator.ts"],"sourcesContent":["import type { ValidationTargets } from 'hono';\nimport { validator } from 'hono/validator';\nimport type { ZodType, output as outputV4 } from 'zod';\nimport {\n NEVER,\n type ZodMiniType,\n type output as outputMini,\n pipe,\n string,\n transform,\n} from 'zod/mini';\nimport { type BadRequest, Details } from '../error/detail';\nimport { Status } from '../error/status';\n\nexport function zValidator<S extends ZodType | ZodMiniType>(\n target: keyof ValidationTargets,\n schema: S\n) {\n return validator(target, async (value) => {\n const result = await schema.safeParseAsync(value);\n if (result.success) return result.data as S extends ZodType ? outputV4<S> : outputMini<S>;\n\n const fieldViolations: BadRequest['fieldViolations'] = result.error.issues.map(\n ({ code, path, message }) => ({\n field: path.join('.'),\n description: message,\n reason: code.toUpperCase(),\n localizedMessage: { locale: 'en-US', message
|
|
1
|
+
{"version":3,"file":"validator.mjs","names":[],"sources":["../../src/hono/validator.ts"],"sourcesContent":["import type { ValidationTargets } from 'hono';\nimport { validator } from 'hono/validator';\nimport type { ZodType, output as outputV4 } from 'zod';\nimport {\n NEVER,\n type ZodMiniType,\n type output as outputMini,\n pipe,\n string,\n transform,\n} from 'zod/mini';\nimport { type BadRequest, Details } from '../error/detail';\nimport { Status } from '../error/status';\n\nexport function zValidator<S extends ZodType | ZodMiniType>(\n target: keyof ValidationTargets,\n schema: S\n) {\n return validator(target, async (value) => {\n const result = await schema.safeParseAsync(value);\n if (result.success) return result.data as S extends ZodType ? outputV4<S> : outputMini<S>;\n\n const fieldViolations: BadRequest['fieldViolations'] = result.error.issues.map(\n ({ code, path, message }) => ({\n field: path.join('.'),\n description: message,\n reason: code.toUpperCase(),\n localizedMessage: { locale: 'en-US', message },\n })\n );\n const details = Details.new().badRequest({ fieldViolations });\n throw Status.invalidArgument().error(details);\n });\n}\n\nexport const bigintId = pipe(\n string(),\n transform((input, ctx) => {\n if (!/^(0|[1-9]\\d{0,19})$/.test(input)) {\n const message = `Invalid bigint id: ${input}`;\n ctx.issues.push({ code: 'custom', input, message });\n return NEVER;\n }\n try {\n return BigInt(input);\n } catch {\n const message = `Parse bigint id: ${input} failed`;\n ctx.issues.push({ code: 'custom', input, message });\n return NEVER;\n }\n })\n);\n"],"mappings":";;;;;AAcA,SAAgB,WACd,QACA,QACA;CACA,OAAO,UAAU,QAAQ,OAAO,UAAU;EACxC,MAAM,SAAS,MAAM,OAAO,eAAe,KAAK;EAChD,IAAI,OAAO,SAAS,OAAO,OAAO;EAElC,MAAM,kBAAiD,OAAO,MAAM,OAAO,KACxE,EAAE,MAAM,MAAM,eAAe;GAC5B,OAAO,KAAK,KAAK,GAAG;GACpB,aAAa;GACb,QAAQ,KAAK,YAAY;GACzB,kBAAkB;IAAE,QAAQ;IAAS;GAAQ;EAC/C,EACF;EACA,MAAM,UAAU,QAAQ,IAAI,CAAC,CAAC,WAAW,EAAE,gBAAgB,CAAC;EAC5D,MAAM,OAAO,gBAAgB,CAAC,CAAC,MAAM,OAAO;CAC9C,CAAC;AACH;AAEA,MAAa,WAAW,KACtB,OAAO,GACP,WAAW,OAAO,QAAQ;CACxB,IAAI,CAAC,sBAAsB,KAAK,KAAK,GAAG;EACtC,MAAM,UAAU,sBAAsB;EACtC,IAAI,OAAO,KAAK;GAAE,MAAM;GAAU;GAAO;EAAQ,CAAC;EAClD,OAAO;CACT;CACA,IAAI;EACF,OAAO,OAAO,KAAK;CACrB,QAAQ;EACN,MAAM,UAAU,oBAAoB,MAAM;EAC1C,IAAI,OAAO,KAAK;GAAE,MAAM;GAAU;GAAO;EAAQ,CAAC;EAClD,OAAO;CACT;AACF,CAAC,CACH"}
|
package/dist/index.cjs
CHANGED
|
@@ -7,6 +7,7 @@ const require_response = require("./response.cjs");
|
|
|
7
7
|
const require_snowflake = require("./snowflake.cjs");
|
|
8
8
|
const require_max_length_index = require("./max-length/index.cjs");
|
|
9
9
|
const require_utils_timing = require("./utils/timing.cjs");
|
|
10
|
+
const require_utils_geolocation = require("./utils/geolocation.cjs");
|
|
10
11
|
const require_utils_google_tag_gateway = require("./utils/google-tag-gateway.cjs");
|
|
11
12
|
const require_iso_iso_3601_1 = require("./iso/iso-3601-1.cjs");
|
|
12
13
|
exports.CheckoutCreateError = require_error_index.CheckoutCreateError;
|
|
@@ -32,6 +33,7 @@ exports.forwardToGoogleTagGateway = require_utils_google_tag_gateway.forwardToGo
|
|
|
32
33
|
exports.getErrorInfo = require_error_parse.getErrorInfo;
|
|
33
34
|
exports.getErrorMessage = require_error_parse.getErrorMessage;
|
|
34
35
|
exports.getFieldViolations = require_error_parse.getFieldViolations;
|
|
36
|
+
exports.getGeolocation = require_utils_geolocation.getGeolocation;
|
|
35
37
|
exports.getNextPageParam = require_response.getNextPageParam;
|
|
36
38
|
exports.getPreviousPageParam = require_response.getPreviousPageParam;
|
|
37
39
|
exports.initialPageParam = require_response.initialPageParam;
|
package/dist/index.d.cts
CHANGED
|
@@ -3,10 +3,11 @@ import { BadRequest, DebugInfo, Detail, DetailType, Details, ErrorInfo, Help, Lo
|
|
|
3
3
|
import { CheckoutCreateError, LoginCanceledError, LoginTimeoutError, PurchaseError } from "./error/index.cjs";
|
|
4
4
|
import { getErrorInfo, getErrorMessage, getFieldViolations } from "./error/parse.cjs";
|
|
5
5
|
import { ErrorBody, Status, StatusError } from "./error/status.cjs";
|
|
6
|
+
import { getGeolocation } from "./utils/geolocation.cjs";
|
|
6
7
|
import { Cursor, Empty, Entity, EntityId, InfinitePageData, InitParams, Items, NextParams, Page, PageParams, Pages, ParentPageParams, PrevParams, Response, getNextPageParam, getPreviousPageParam, initialPageParam, pageParamsSchema } from "./response.cjs";
|
|
7
8
|
import { UidGenerator, uid } from "./snowflake.cjs";
|
|
8
9
|
import { index_d_exports } from "./max-length/index.cjs";
|
|
9
10
|
import { timing } from "./utils/timing.cjs";
|
|
10
11
|
import { forwardToGoogleTagGateway } from "./utils/google-tag-gateway.cjs";
|
|
11
12
|
import { ISO3166CountryCode, ISO_3601_1 } from "./iso/iso-3601-1.cjs";
|
|
12
|
-
export { type BadRequest, CheckoutCreateError, Cursor, type DebugInfo, type Detail, DetailType, Details, type Empty, type Entity, type EntityId, type ErrorBody, type ErrorInfo, type ErrorReason, type Help, type ISO3166CountryCode, ISO_3601_1, type InfinitePageData, type InitParams, Items, type LocalizedMessage, LoginCanceledError, LoginTimeoutError, index_d_exports as MAX_LENGTH, type NextParams, type Page, type PageParams, Pages, type ParentPageParams, type PreconditionFailure, type PrevParams, PurchaseError, type QuotaFailure, type RequestInfo, type ResolvedErrorReason, type ResourceInfo, type Response, type RetryInfo, Status, StatusError, UidGenerator, forwardToGoogleTagGateway, getErrorInfo, getErrorMessage, getFieldViolations, getNextPageParam, getPreviousPageParam, initialPageParam, pageParamsSchema, timing, uid };
|
|
13
|
+
export { type BadRequest, CheckoutCreateError, Cursor, type DebugInfo, type Detail, DetailType, Details, type Empty, type Entity, type EntityId, type ErrorBody, type ErrorInfo, type ErrorReason, type Help, type ISO3166CountryCode, ISO_3601_1, type InfinitePageData, type InitParams, Items, type LocalizedMessage, LoginCanceledError, LoginTimeoutError, index_d_exports as MAX_LENGTH, type NextParams, type Page, type PageParams, Pages, type ParentPageParams, type PreconditionFailure, type PrevParams, PurchaseError, type QuotaFailure, type RequestInfo, type ResolvedErrorReason, type ResourceInfo, type Response, type RetryInfo, Status, StatusError, UidGenerator, forwardToGoogleTagGateway, getErrorInfo, getErrorMessage, getFieldViolations, getGeolocation, getNextPageParam, getPreviousPageParam, initialPageParam, pageParamsSchema, timing, uid };
|
package/dist/index.d.mts
CHANGED
|
@@ -3,10 +3,11 @@ import { BadRequest, DebugInfo, Detail, DetailType, Details, ErrorInfo, Help, Lo
|
|
|
3
3
|
import { CheckoutCreateError, LoginCanceledError, LoginTimeoutError, PurchaseError } from "./error/index.mjs";
|
|
4
4
|
import { getErrorInfo, getErrorMessage, getFieldViolations } from "./error/parse.mjs";
|
|
5
5
|
import { ErrorBody, Status, StatusError } from "./error/status.mjs";
|
|
6
|
+
import { getGeolocation } from "./utils/geolocation.mjs";
|
|
6
7
|
import { Cursor, Empty, Entity, EntityId, InfinitePageData, InitParams, Items, NextParams, Page, PageParams, Pages, ParentPageParams, PrevParams, Response, getNextPageParam, getPreviousPageParam, initialPageParam, pageParamsSchema } from "./response.mjs";
|
|
7
8
|
import { UidGenerator, uid } from "./snowflake.mjs";
|
|
8
9
|
import { index_d_exports } from "./max-length/index.mjs";
|
|
9
10
|
import { timing } from "./utils/timing.mjs";
|
|
10
11
|
import { forwardToGoogleTagGateway } from "./utils/google-tag-gateway.mjs";
|
|
11
12
|
import { ISO3166CountryCode, ISO_3601_1 } from "./iso/iso-3601-1.mjs";
|
|
12
|
-
export { type BadRequest, CheckoutCreateError, Cursor, type DebugInfo, type Detail, DetailType, Details, type Empty, type Entity, type EntityId, type ErrorBody, type ErrorInfo, type ErrorReason, type Help, type ISO3166CountryCode, ISO_3601_1, type InfinitePageData, type InitParams, Items, type LocalizedMessage, LoginCanceledError, LoginTimeoutError, index_d_exports as MAX_LENGTH, type NextParams, type Page, type PageParams, Pages, type ParentPageParams, type PreconditionFailure, type PrevParams, PurchaseError, type QuotaFailure, type RequestInfo, type ResolvedErrorReason, type ResourceInfo, type Response, type RetryInfo, Status, StatusError, UidGenerator, forwardToGoogleTagGateway, getErrorInfo, getErrorMessage, getFieldViolations, getNextPageParam, getPreviousPageParam, initialPageParam, pageParamsSchema, timing, uid };
|
|
13
|
+
export { type BadRequest, CheckoutCreateError, Cursor, type DebugInfo, type Detail, DetailType, Details, type Empty, type Entity, type EntityId, type ErrorBody, type ErrorInfo, type ErrorReason, type Help, type ISO3166CountryCode, ISO_3601_1, type InfinitePageData, type InitParams, Items, type LocalizedMessage, LoginCanceledError, LoginTimeoutError, index_d_exports as MAX_LENGTH, type NextParams, type Page, type PageParams, Pages, type ParentPageParams, type PreconditionFailure, type PrevParams, PurchaseError, type QuotaFailure, type RequestInfo, type ResolvedErrorReason, type ResourceInfo, type Response, type RetryInfo, Status, StatusError, UidGenerator, forwardToGoogleTagGateway, getErrorInfo, getErrorMessage, getFieldViolations, getGeolocation, getNextPageParam, getPreviousPageParam, initialPageParam, pageParamsSchema, timing, uid };
|
package/dist/index.mjs
CHANGED
|
@@ -6,6 +6,7 @@ import { Cursor, Items, Pages, getNextPageParam, getPreviousPageParam, initialPa
|
|
|
6
6
|
import { UidGenerator, uid } from "./snowflake.mjs";
|
|
7
7
|
import { max_length_exports } from "./max-length/index.mjs";
|
|
8
8
|
import { timing } from "./utils/timing.mjs";
|
|
9
|
+
import { getGeolocation } from "./utils/geolocation.mjs";
|
|
9
10
|
import { forwardToGoogleTagGateway } from "./utils/google-tag-gateway.mjs";
|
|
10
11
|
import { ISO_3601_1 } from "./iso/iso-3601-1.mjs";
|
|
11
|
-
export { CheckoutCreateError, Cursor, DetailType, Details, ISO_3601_1, Items, LoginCanceledError, LoginTimeoutError, max_length_exports as MAX_LENGTH, Pages, PurchaseError, Status, StatusError, UidGenerator, forwardToGoogleTagGateway, getErrorInfo, getErrorMessage, getFieldViolations, getNextPageParam, getPreviousPageParam, initialPageParam, pageParamsSchema, timing, uid };
|
|
12
|
+
export { CheckoutCreateError, Cursor, DetailType, Details, ISO_3601_1, Items, LoginCanceledError, LoginTimeoutError, max_length_exports as MAX_LENGTH, Pages, PurchaseError, Status, StatusError, UidGenerator, forwardToGoogleTagGateway, getErrorInfo, getErrorMessage, getFieldViolations, getGeolocation, getNextPageParam, getPreviousPageParam, initialPageParam, pageParamsSchema, timing, uid };
|
|
@@ -11,18 +11,21 @@ async function forwardToGoogleTagGateway(request, gaId) {
|
|
|
11
11
|
const { pathname, search } = new URL(request.url);
|
|
12
12
|
const target = `https://${GATEWAY_HOST}${pathname}${search}`;
|
|
13
13
|
const headers = new Headers();
|
|
14
|
-
headers.
|
|
15
|
-
const
|
|
16
|
-
if (
|
|
17
|
-
const { country, region } = require_utils_geolocation.getGeolocation(request);
|
|
14
|
+
for (const [name, value] of request.headers) if (name === "cookie" || name === "user-agent" || name === "accept-language" || name === "referer" || name === "x-forwarded-for" || name.startsWith("sec-ch-")) headers.set(name, value);
|
|
15
|
+
const { country, region, city, latitude, longitude, ip_address } = require_utils_geolocation.getGeolocation(request);
|
|
16
|
+
if (ip_address && !headers.has("x-forwarded-for")) headers.set("x-forwarded-for", ip_address);
|
|
18
17
|
if (country && region) headers.set("x-forwarded-countryregion", `${country}-${region}`);
|
|
19
18
|
else if (country) headers.set("x-forwarded-country", country);
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
const geolocation = [latitude !== void 0 && longitude !== void 0 && `latlong=${latitude},${longitude}`, city && `city=${city}`].filter(Boolean);
|
|
20
|
+
if (geolocation.length > 0) headers.set("x-forwarded-geolocation", geolocation.join(";"));
|
|
21
|
+
const hasBody = request.method !== "GET" && request.method !== "HEAD";
|
|
22
22
|
const response = await fetch(target, {
|
|
23
23
|
method: request.method,
|
|
24
24
|
headers,
|
|
25
|
-
body
|
|
25
|
+
body: hasBody ? request.body : void 0,
|
|
26
|
+
redirect: "manual",
|
|
27
|
+
cache: "no-store",
|
|
28
|
+
...hasBody && { duplex: "half" }
|
|
26
29
|
});
|
|
27
30
|
const responseHeaders = new Headers(response.headers);
|
|
28
31
|
responseHeaders.delete("content-encoding");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"google-tag-gateway.cjs","names":["getGeolocation"],"sources":["../../src/utils/google-tag-gateway.ts"],"sourcesContent":["import { getGeolocation } from './geolocation';\n\n/**\n * In a browser, verify the setup by navigating to: https://example.com/metrics/healthy. The page\n * should read ok. Verify that geographical information is being included by navigating to:\n * https://example.com/metrics/?validate_geo=healthy. The page should read ok.\n */\nexport async function forwardToGoogleTagGateway(request: Request, gaId: string) {\n const GATEWAY_HOST = `${gaId}.fps.goog`;\n const { pathname, search } = new URL(request.url);\n\n const target = `https://${GATEWAY_HOST}${pathname}${search}`;\n\n const headers = new Headers();\n headers
|
|
1
|
+
{"version":3,"file":"google-tag-gateway.cjs","names":["getGeolocation"],"sources":["../../src/utils/google-tag-gateway.ts"],"sourcesContent":["import { getGeolocation } from './geolocation';\n\n/**\n * In a browser, verify the setup by navigating to: https://example.com/metrics/healthy. The page\n * should read ok. Verify that geographical information is being included by navigating to:\n * https://example.com/metrics/?validate_geo=healthy. The page should read ok.\n */\nexport async function forwardToGoogleTagGateway(request: Request, gaId: string) {\n const GATEWAY_HOST = `${gaId}.fps.goog`;\n const { pathname, search } = new URL(request.url);\n\n const target = `https://${GATEWAY_HOST}${pathname}${search}`;\n\n const headers = new Headers();\n\n // Forward the browser-identifying headers a load balancer would pass through:\n // cookies for first-party measurement, user-agent + client hints so GA doesn't\n // classify hits as bot traffic and the gateway can serve UA-appropriate scripts.\n for (const [name, value] of request.headers) {\n if (\n name === 'cookie' ||\n name === 'user-agent' ||\n name === 'accept-language' ||\n name === 'referer' ||\n name === 'x-forwarded-for' ||\n name.startsWith('sec-ch-')\n ) {\n headers.set(name, value);\n }\n }\n\n // Convert geo headers to Google Tag Gateway format\n // https://developers.google.com/tag-platform/tag-manager/gateway/setup-guide\n const { country, region, city, latitude, longitude, ip_address } = getGeolocation(request);\n\n // A CDN in front (Vercel/Cloudflare/CloudFront) normally sets x-forwarded-for already;\n // fall back to the platform-specific client-IP header if it was stripped.\n if (ip_address && !headers.has('x-forwarded-for')) {\n headers.set('x-forwarded-for', ip_address);\n }\n\n if (country && region) {\n headers.set('x-forwarded-countryregion', `${country}-${region}`);\n } else if (country) {\n headers.set('x-forwarded-country', country);\n }\n\n // City-level geolocation, format per the official Fastly/Google Cloud configs:\n // latlong=<lat>,<lng>;city=<city>\n const geolocation = [\n latitude !== undefined && longitude !== undefined && `latlong=${latitude},${longitude}`,\n city && `city=${city}`,\n ].filter(Boolean);\n\n if (geolocation.length > 0) {\n headers.set('x-forwarded-geolocation', geolocation.join(';'));\n }\n\n const hasBody = request.method !== 'GET' && request.method !== 'HEAD';\n\n // The conversion endpoint (g/measurement/conversion) answers POSTs with a 302 to\n // www.google.com. Pass that redirect through to the browser instead of following it\n // here: the hop only carries signal when the browser makes it with its own google.com\n // cookies (Google Signals / cross-domain conversion linking). Not following redirects\n // also means the one-shot body stream never needs to be replayed, so it can be\n // forwarded as-is.\n const response = await fetch(target, {\n method: request.method,\n headers,\n body: hasBody ? request.body : undefined,\n redirect: 'manual',\n // Opt out of Next.js fetch caching (the official CloudFront config likewise\n // requires CachingDisabled): measurement hits must never be served from cache.\n cache: 'no-store',\n ...(hasBody && { duplex: 'half' as const }),\n });\n\n // Strip content-encoding/content-length because fetch() auto-decompresses\n // but keeps the original headers, causing ERR_CONTENT_DECODING_FAILED\n const responseHeaders = new Headers(response.headers);\n responseHeaders.delete('content-encoding');\n responseHeaders.delete('content-length');\n\n return new Response(response.body, { status: response.status, headers: responseHeaders });\n}\n"],"mappings":";;;;;;;;AAOA,eAAsB,0BAA0B,SAAkB,MAAc;CAC9E,MAAM,eAAe,GAAG,KAAK;CAC7B,MAAM,EAAE,UAAU,WAAW,IAAI,IAAI,QAAQ,GAAG;CAEhD,MAAM,SAAS,WAAW,eAAe,WAAW;CAEpD,MAAM,UAAU,IAAI,QAAQ;CAK5B,KAAK,MAAM,CAAC,MAAM,UAAU,QAAQ,SAClC,IACE,SAAS,YACT,SAAS,gBACT,SAAS,qBACT,SAAS,aACT,SAAS,qBACT,KAAK,WAAW,SAAS,GAEzB,QAAQ,IAAI,MAAM,KAAK;CAM3B,MAAM,EAAE,SAAS,QAAQ,MAAM,UAAU,WAAW,eAAeA,0BAAAA,eAAe,OAAO;CAIzF,IAAI,cAAc,CAAC,QAAQ,IAAI,iBAAiB,GAC9C,QAAQ,IAAI,mBAAmB,UAAU;CAG3C,IAAI,WAAW,QACb,QAAQ,IAAI,6BAA6B,GAAG,QAAQ,GAAG,QAAQ;MAC1D,IAAI,SACT,QAAQ,IAAI,uBAAuB,OAAO;CAK5C,MAAM,cAAc,CAClB,aAAa,KAAA,KAAa,cAAc,KAAA,KAAa,WAAW,SAAS,GAAG,aAC5E,QAAQ,QAAQ,MAClB,CAAC,CAAC,OAAO,OAAO;CAEhB,IAAI,YAAY,SAAS,GACvB,QAAQ,IAAI,2BAA2B,YAAY,KAAK,GAAG,CAAC;CAG9D,MAAM,UAAU,QAAQ,WAAW,SAAS,QAAQ,WAAW;CAQ/D,MAAM,WAAW,MAAM,MAAM,QAAQ;EACnC,QAAQ,QAAQ;EAChB;EACA,MAAM,UAAU,QAAQ,OAAO,KAAA;EAC/B,UAAU;EAGV,OAAO;EACP,GAAI,WAAW,EAAE,QAAQ,OAAgB;CAC3C,CAAC;CAID,MAAM,kBAAkB,IAAI,QAAQ,SAAS,OAAO;CACpD,gBAAgB,OAAO,kBAAkB;CACzC,gBAAgB,OAAO,gBAAgB;CAEvC,OAAO,IAAI,SAAS,SAAS,MAAM;EAAE,QAAQ,SAAS;EAAQ,SAAS;CAAgB,CAAC;AAC1F"}
|
|
@@ -10,18 +10,21 @@ async function forwardToGoogleTagGateway(request, gaId) {
|
|
|
10
10
|
const { pathname, search } = new URL(request.url);
|
|
11
11
|
const target = `https://${GATEWAY_HOST}${pathname}${search}`;
|
|
12
12
|
const headers = new Headers();
|
|
13
|
-
headers.
|
|
14
|
-
const
|
|
15
|
-
if (
|
|
16
|
-
const { country, region } = getGeolocation(request);
|
|
13
|
+
for (const [name, value] of request.headers) if (name === "cookie" || name === "user-agent" || name === "accept-language" || name === "referer" || name === "x-forwarded-for" || name.startsWith("sec-ch-")) headers.set(name, value);
|
|
14
|
+
const { country, region, city, latitude, longitude, ip_address } = getGeolocation(request);
|
|
15
|
+
if (ip_address && !headers.has("x-forwarded-for")) headers.set("x-forwarded-for", ip_address);
|
|
17
16
|
if (country && region) headers.set("x-forwarded-countryregion", `${country}-${region}`);
|
|
18
17
|
else if (country) headers.set("x-forwarded-country", country);
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
const geolocation = [latitude !== void 0 && longitude !== void 0 && `latlong=${latitude},${longitude}`, city && `city=${city}`].filter(Boolean);
|
|
19
|
+
if (geolocation.length > 0) headers.set("x-forwarded-geolocation", geolocation.join(";"));
|
|
20
|
+
const hasBody = request.method !== "GET" && request.method !== "HEAD";
|
|
21
21
|
const response = await fetch(target, {
|
|
22
22
|
method: request.method,
|
|
23
23
|
headers,
|
|
24
|
-
body
|
|
24
|
+
body: hasBody ? request.body : void 0,
|
|
25
|
+
redirect: "manual",
|
|
26
|
+
cache: "no-store",
|
|
27
|
+
...hasBody && { duplex: "half" }
|
|
25
28
|
});
|
|
26
29
|
const responseHeaders = new Headers(response.headers);
|
|
27
30
|
responseHeaders.delete("content-encoding");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"google-tag-gateway.mjs","names":[],"sources":["../../src/utils/google-tag-gateway.ts"],"sourcesContent":["import { getGeolocation } from './geolocation';\n\n/**\n * In a browser, verify the setup by navigating to: https://example.com/metrics/healthy. The page\n * should read ok. Verify that geographical information is being included by navigating to:\n * https://example.com/metrics/?validate_geo=healthy. The page should read ok.\n */\nexport async function forwardToGoogleTagGateway(request: Request, gaId: string) {\n const GATEWAY_HOST = `${gaId}.fps.goog`;\n const { pathname, search } = new URL(request.url);\n\n const target = `https://${GATEWAY_HOST}${pathname}${search}`;\n\n const headers = new Headers();\n headers
|
|
1
|
+
{"version":3,"file":"google-tag-gateway.mjs","names":[],"sources":["../../src/utils/google-tag-gateway.ts"],"sourcesContent":["import { getGeolocation } from './geolocation';\n\n/**\n * In a browser, verify the setup by navigating to: https://example.com/metrics/healthy. The page\n * should read ok. Verify that geographical information is being included by navigating to:\n * https://example.com/metrics/?validate_geo=healthy. The page should read ok.\n */\nexport async function forwardToGoogleTagGateway(request: Request, gaId: string) {\n const GATEWAY_HOST = `${gaId}.fps.goog`;\n const { pathname, search } = new URL(request.url);\n\n const target = `https://${GATEWAY_HOST}${pathname}${search}`;\n\n const headers = new Headers();\n\n // Forward the browser-identifying headers a load balancer would pass through:\n // cookies for first-party measurement, user-agent + client hints so GA doesn't\n // classify hits as bot traffic and the gateway can serve UA-appropriate scripts.\n for (const [name, value] of request.headers) {\n if (\n name === 'cookie' ||\n name === 'user-agent' ||\n name === 'accept-language' ||\n name === 'referer' ||\n name === 'x-forwarded-for' ||\n name.startsWith('sec-ch-')\n ) {\n headers.set(name, value);\n }\n }\n\n // Convert geo headers to Google Tag Gateway format\n // https://developers.google.com/tag-platform/tag-manager/gateway/setup-guide\n const { country, region, city, latitude, longitude, ip_address } = getGeolocation(request);\n\n // A CDN in front (Vercel/Cloudflare/CloudFront) normally sets x-forwarded-for already;\n // fall back to the platform-specific client-IP header if it was stripped.\n if (ip_address && !headers.has('x-forwarded-for')) {\n headers.set('x-forwarded-for', ip_address);\n }\n\n if (country && region) {\n headers.set('x-forwarded-countryregion', `${country}-${region}`);\n } else if (country) {\n headers.set('x-forwarded-country', country);\n }\n\n // City-level geolocation, format per the official Fastly/Google Cloud configs:\n // latlong=<lat>,<lng>;city=<city>\n const geolocation = [\n latitude !== undefined && longitude !== undefined && `latlong=${latitude},${longitude}`,\n city && `city=${city}`,\n ].filter(Boolean);\n\n if (geolocation.length > 0) {\n headers.set('x-forwarded-geolocation', geolocation.join(';'));\n }\n\n const hasBody = request.method !== 'GET' && request.method !== 'HEAD';\n\n // The conversion endpoint (g/measurement/conversion) answers POSTs with a 302 to\n // www.google.com. Pass that redirect through to the browser instead of following it\n // here: the hop only carries signal when the browser makes it with its own google.com\n // cookies (Google Signals / cross-domain conversion linking). Not following redirects\n // also means the one-shot body stream never needs to be replayed, so it can be\n // forwarded as-is.\n const response = await fetch(target, {\n method: request.method,\n headers,\n body: hasBody ? request.body : undefined,\n redirect: 'manual',\n // Opt out of Next.js fetch caching (the official CloudFront config likewise\n // requires CachingDisabled): measurement hits must never be served from cache.\n cache: 'no-store',\n ...(hasBody && { duplex: 'half' as const }),\n });\n\n // Strip content-encoding/content-length because fetch() auto-decompresses\n // but keeps the original headers, causing ERR_CONTENT_DECODING_FAILED\n const responseHeaders = new Headers(response.headers);\n responseHeaders.delete('content-encoding');\n responseHeaders.delete('content-length');\n\n return new Response(response.body, { status: response.status, headers: responseHeaders });\n}\n"],"mappings":";;;;;;;AAOA,eAAsB,0BAA0B,SAAkB,MAAc;CAC9E,MAAM,eAAe,GAAG,KAAK;CAC7B,MAAM,EAAE,UAAU,WAAW,IAAI,IAAI,QAAQ,GAAG;CAEhD,MAAM,SAAS,WAAW,eAAe,WAAW;CAEpD,MAAM,UAAU,IAAI,QAAQ;CAK5B,KAAK,MAAM,CAAC,MAAM,UAAU,QAAQ,SAClC,IACE,SAAS,YACT,SAAS,gBACT,SAAS,qBACT,SAAS,aACT,SAAS,qBACT,KAAK,WAAW,SAAS,GAEzB,QAAQ,IAAI,MAAM,KAAK;CAM3B,MAAM,EAAE,SAAS,QAAQ,MAAM,UAAU,WAAW,eAAe,eAAe,OAAO;CAIzF,IAAI,cAAc,CAAC,QAAQ,IAAI,iBAAiB,GAC9C,QAAQ,IAAI,mBAAmB,UAAU;CAG3C,IAAI,WAAW,QACb,QAAQ,IAAI,6BAA6B,GAAG,QAAQ,GAAG,QAAQ;MAC1D,IAAI,SACT,QAAQ,IAAI,uBAAuB,OAAO;CAK5C,MAAM,cAAc,CAClB,aAAa,KAAA,KAAa,cAAc,KAAA,KAAa,WAAW,SAAS,GAAG,aAC5E,QAAQ,QAAQ,MAClB,CAAC,CAAC,OAAO,OAAO;CAEhB,IAAI,YAAY,SAAS,GACvB,QAAQ,IAAI,2BAA2B,YAAY,KAAK,GAAG,CAAC;CAG9D,MAAM,UAAU,QAAQ,WAAW,SAAS,QAAQ,WAAW;CAQ/D,MAAM,WAAW,MAAM,MAAM,QAAQ;EACnC,QAAQ,QAAQ;EAChB;EACA,MAAM,UAAU,QAAQ,OAAO,KAAA;EAC/B,UAAU;EAGV,OAAO;EACP,GAAI,WAAW,EAAE,QAAQ,OAAgB;CAC3C,CAAC;CAID,MAAM,kBAAkB,IAAI,QAAQ,SAAS,OAAO;CACpD,gBAAgB,OAAO,kBAAkB;CACzC,gBAAgB,OAAO,gBAAgB;CAEvC,OAAO,IAAI,SAAS,SAAS,MAAM;EAAE,QAAQ,SAAS;EAAQ,SAAS;CAAgB,CAAC;AAC1F"}
|
package/dist/utils/timing.cjs
CHANGED
|
@@ -36,7 +36,7 @@ function timing(options = { enabled: true }) {
|
|
|
36
36
|
const metric = description ? `${name};dur=${dur};desc="${description}"` : `${name};dur=${dur}`;
|
|
37
37
|
headers.push(metric);
|
|
38
38
|
} else {
|
|
39
|
-
const metric = value ? `${name};desc="${value}"` :
|
|
39
|
+
const metric = value ? `${name};desc="${value}"` : name;
|
|
40
40
|
headers.push(metric);
|
|
41
41
|
}
|
|
42
42
|
},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"timing.cjs","names":[],"sources":["../../src/utils/timing.ts"],"sourcesContent":["interface Timer {\n description?: string;\n start: number;\n}\n\nexport interface Options {\n enabled: boolean;\n crossOrigin?: string;\n}\n\nexport function timing(options: Options = { enabled: true }) {\n const { enabled, crossOrigin } = options;\n let start = performance.now();\n const headers: string[] = [];\n const timers = new Map<string, Timer>();\n\n return {\n startTime: (name: Lowercase<string>, description?: string) => {\n timers.set(name, { description, start: performance.now() });\n },\n endTime: (name: Lowercase<string>, precision?: number) => {\n const timer = timers.get(name);\n if (!timer) {\n console.warn(`timing: ${name} not found`);\n return;\n }\n const { start, description } = timer;\n const dur = (performance.now() - start).toFixed(precision ?? 1);\n timers.delete(name);\n const metric = description\n ? `${name};dur=${dur};desc=\"${description}\"`\n : `${name};dur=${dur}`;\n headers.push(metric);\n },\n mark: (name: Lowercase<string>, description?: string, precision?: number) => {\n const dur = (performance.now() - start).toFixed(precision ?? 1);\n const metric = description\n ? `${name};dur=${dur};desc=\"${description}\"`\n : `${name};dur=${dur}`;\n headers.push(metric);\n start = performance.now();\n },\n setMetric: (\n name: Lowercase<string>,\n value: number | string | undefined,\n description?: string,\n precision?: number\n ) => {\n if (typeof value === 'number') {\n const dur = value.toFixed(precision ?? 1);\n const metric = description\n ? `${name};dur=${dur};desc=\"${description}\"`\n : `${name};dur=${dur}`;\n headers.push(metric);\n } else {\n const metric = value ? `${name};desc=\"${value}\"` :
|
|
1
|
+
{"version":3,"file":"timing.cjs","names":[],"sources":["../../src/utils/timing.ts"],"sourcesContent":["interface Timer {\n description?: string;\n start: number;\n}\n\nexport interface Options {\n enabled: boolean;\n crossOrigin?: string;\n}\n\nexport function timing(options: Options = { enabled: true }) {\n const { enabled, crossOrigin } = options;\n let start = performance.now();\n const headers: string[] = [];\n const timers = new Map<string, Timer>();\n\n return {\n startTime: (name: Lowercase<string>, description?: string) => {\n timers.set(name, { description, start: performance.now() });\n },\n endTime: (name: Lowercase<string>, precision?: number) => {\n const timer = timers.get(name);\n if (!timer) {\n console.warn(`timing: ${name} not found`);\n return;\n }\n const { start, description } = timer;\n const dur = (performance.now() - start).toFixed(precision ?? 1);\n timers.delete(name);\n const metric = description\n ? `${name};dur=${dur};desc=\"${description}\"`\n : `${name};dur=${dur}`;\n headers.push(metric);\n },\n mark: (name: Lowercase<string>, description?: string, precision?: number) => {\n const dur = (performance.now() - start).toFixed(precision ?? 1);\n const metric = description\n ? `${name};dur=${dur};desc=\"${description}\"`\n : `${name};dur=${dur}`;\n headers.push(metric);\n start = performance.now();\n },\n setMetric: (\n name: Lowercase<string>,\n value: number | string | undefined,\n description?: string,\n precision?: number\n ) => {\n if (typeof value === 'number') {\n const dur = value.toFixed(precision ?? 1);\n const metric = description\n ? `${name};dur=${dur};desc=\"${description}\"`\n : `${name};dur=${dur}`;\n headers.push(metric);\n } else {\n const metric = value ? `${name};desc=\"${value}\"` : name;\n headers.push(metric);\n }\n },\n setTiming: (response: Response) => {\n if (!enabled) return;\n response.headers.append('Server-Timing', headers.join(','));\n response.headers.append('Timing-Allow-Origin', crossOrigin ?? '*');\n },\n };\n}\n"],"mappings":";;AAUA,SAAgB,OAAO,UAAmB,EAAE,SAAS,KAAK,GAAG;CAC3D,MAAM,EAAE,SAAS,gBAAgB;CACjC,IAAI,QAAQ,YAAY,IAAI;CAC5B,MAAM,UAAoB,CAAC;CAC3B,MAAM,yBAAS,IAAI,IAAmB;CAEtC,OAAO;EACL,YAAY,MAAyB,gBAAyB;GAC5D,OAAO,IAAI,MAAM;IAAE;IAAa,OAAO,YAAY,IAAI;GAAE,CAAC;EAC5D;EACA,UAAU,MAAyB,cAAuB;GACxD,MAAM,QAAQ,OAAO,IAAI,IAAI;GAC7B,IAAI,CAAC,OAAO;IACV,QAAQ,KAAK,WAAW,KAAK,WAAW;IACxC;GACF;GACA,MAAM,EAAE,OAAO,gBAAgB;GAC/B,MAAM,OAAO,YAAY,IAAI,IAAI,MAAA,CAAO,QAAQ,aAAa,CAAC;GAC9D,OAAO,OAAO,IAAI;GAClB,MAAM,SAAS,cACX,GAAG,KAAK,OAAO,IAAI,SAAS,YAAY,KACxC,GAAG,KAAK,OAAO;GACnB,QAAQ,KAAK,MAAM;EACrB;EACA,OAAO,MAAyB,aAAsB,cAAuB;GAC3E,MAAM,OAAO,YAAY,IAAI,IAAI,MAAA,CAAO,QAAQ,aAAa,CAAC;GAC9D,MAAM,SAAS,cACX,GAAG,KAAK,OAAO,IAAI,SAAS,YAAY,KACxC,GAAG,KAAK,OAAO;GACnB,QAAQ,KAAK,MAAM;GACnB,QAAQ,YAAY,IAAI;EAC1B;EACA,YACE,MACA,OACA,aACA,cACG;GACH,IAAI,OAAO,UAAU,UAAU;IAC7B,MAAM,MAAM,MAAM,QAAQ,aAAa,CAAC;IACxC,MAAM,SAAS,cACX,GAAG,KAAK,OAAO,IAAI,SAAS,YAAY,KACxC,GAAG,KAAK,OAAO;IACnB,QAAQ,KAAK,MAAM;GACrB,OAAO;IACL,MAAM,SAAS,QAAQ,GAAG,KAAK,SAAS,MAAM,KAAK;IACnD,QAAQ,KAAK,MAAM;GACrB;EACF;EACA,YAAY,aAAuB;GACjC,IAAI,CAAC,SAAS;GACd,SAAS,QAAQ,OAAO,iBAAiB,QAAQ,KAAK,GAAG,CAAC;GAC1D,SAAS,QAAQ,OAAO,uBAAuB,eAAe,GAAG;EACnE;CACF;AACF"}
|
package/dist/utils/timing.mjs
CHANGED
|
@@ -35,7 +35,7 @@ function timing(options = { enabled: true }) {
|
|
|
35
35
|
const metric = description ? `${name};dur=${dur};desc="${description}"` : `${name};dur=${dur}`;
|
|
36
36
|
headers.push(metric);
|
|
37
37
|
} else {
|
|
38
|
-
const metric = value ? `${name};desc="${value}"` :
|
|
38
|
+
const metric = value ? `${name};desc="${value}"` : name;
|
|
39
39
|
headers.push(metric);
|
|
40
40
|
}
|
|
41
41
|
},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"timing.mjs","names":[],"sources":["../../src/utils/timing.ts"],"sourcesContent":["interface Timer {\n description?: string;\n start: number;\n}\n\nexport interface Options {\n enabled: boolean;\n crossOrigin?: string;\n}\n\nexport function timing(options: Options = { enabled: true }) {\n const { enabled, crossOrigin } = options;\n let start = performance.now();\n const headers: string[] = [];\n const timers = new Map<string, Timer>();\n\n return {\n startTime: (name: Lowercase<string>, description?: string) => {\n timers.set(name, { description, start: performance.now() });\n },\n endTime: (name: Lowercase<string>, precision?: number) => {\n const timer = timers.get(name);\n if (!timer) {\n console.warn(`timing: ${name} not found`);\n return;\n }\n const { start, description } = timer;\n const dur = (performance.now() - start).toFixed(precision ?? 1);\n timers.delete(name);\n const metric = description\n ? `${name};dur=${dur};desc=\"${description}\"`\n : `${name};dur=${dur}`;\n headers.push(metric);\n },\n mark: (name: Lowercase<string>, description?: string, precision?: number) => {\n const dur = (performance.now() - start).toFixed(precision ?? 1);\n const metric = description\n ? `${name};dur=${dur};desc=\"${description}\"`\n : `${name};dur=${dur}`;\n headers.push(metric);\n start = performance.now();\n },\n setMetric: (\n name: Lowercase<string>,\n value: number | string | undefined,\n description?: string,\n precision?: number\n ) => {\n if (typeof value === 'number') {\n const dur = value.toFixed(precision ?? 1);\n const metric = description\n ? `${name};dur=${dur};desc=\"${description}\"`\n : `${name};dur=${dur}`;\n headers.push(metric);\n } else {\n const metric = value ? `${name};desc=\"${value}\"` :
|
|
1
|
+
{"version":3,"file":"timing.mjs","names":[],"sources":["../../src/utils/timing.ts"],"sourcesContent":["interface Timer {\n description?: string;\n start: number;\n}\n\nexport interface Options {\n enabled: boolean;\n crossOrigin?: string;\n}\n\nexport function timing(options: Options = { enabled: true }) {\n const { enabled, crossOrigin } = options;\n let start = performance.now();\n const headers: string[] = [];\n const timers = new Map<string, Timer>();\n\n return {\n startTime: (name: Lowercase<string>, description?: string) => {\n timers.set(name, { description, start: performance.now() });\n },\n endTime: (name: Lowercase<string>, precision?: number) => {\n const timer = timers.get(name);\n if (!timer) {\n console.warn(`timing: ${name} not found`);\n return;\n }\n const { start, description } = timer;\n const dur = (performance.now() - start).toFixed(precision ?? 1);\n timers.delete(name);\n const metric = description\n ? `${name};dur=${dur};desc=\"${description}\"`\n : `${name};dur=${dur}`;\n headers.push(metric);\n },\n mark: (name: Lowercase<string>, description?: string, precision?: number) => {\n const dur = (performance.now() - start).toFixed(precision ?? 1);\n const metric = description\n ? `${name};dur=${dur};desc=\"${description}\"`\n : `${name};dur=${dur}`;\n headers.push(metric);\n start = performance.now();\n },\n setMetric: (\n name: Lowercase<string>,\n value: number | string | undefined,\n description?: string,\n precision?: number\n ) => {\n if (typeof value === 'number') {\n const dur = value.toFixed(precision ?? 1);\n const metric = description\n ? `${name};dur=${dur};desc=\"${description}\"`\n : `${name};dur=${dur}`;\n headers.push(metric);\n } else {\n const metric = value ? `${name};desc=\"${value}\"` : name;\n headers.push(metric);\n }\n },\n setTiming: (response: Response) => {\n if (!enabled) return;\n response.headers.append('Server-Timing', headers.join(','));\n response.headers.append('Timing-Allow-Origin', crossOrigin ?? '*');\n },\n };\n}\n"],"mappings":";AAUA,SAAgB,OAAO,UAAmB,EAAE,SAAS,KAAK,GAAG;CAC3D,MAAM,EAAE,SAAS,gBAAgB;CACjC,IAAI,QAAQ,YAAY,IAAI;CAC5B,MAAM,UAAoB,CAAC;CAC3B,MAAM,yBAAS,IAAI,IAAmB;CAEtC,OAAO;EACL,YAAY,MAAyB,gBAAyB;GAC5D,OAAO,IAAI,MAAM;IAAE;IAAa,OAAO,YAAY,IAAI;GAAE,CAAC;EAC5D;EACA,UAAU,MAAyB,cAAuB;GACxD,MAAM,QAAQ,OAAO,IAAI,IAAI;GAC7B,IAAI,CAAC,OAAO;IACV,QAAQ,KAAK,WAAW,KAAK,WAAW;IACxC;GACF;GACA,MAAM,EAAE,OAAO,gBAAgB;GAC/B,MAAM,OAAO,YAAY,IAAI,IAAI,MAAA,CAAO,QAAQ,aAAa,CAAC;GAC9D,OAAO,OAAO,IAAI;GAClB,MAAM,SAAS,cACX,GAAG,KAAK,OAAO,IAAI,SAAS,YAAY,KACxC,GAAG,KAAK,OAAO;GACnB,QAAQ,KAAK,MAAM;EACrB;EACA,OAAO,MAAyB,aAAsB,cAAuB;GAC3E,MAAM,OAAO,YAAY,IAAI,IAAI,MAAA,CAAO,QAAQ,aAAa,CAAC;GAC9D,MAAM,SAAS,cACX,GAAG,KAAK,OAAO,IAAI,SAAS,YAAY,KACxC,GAAG,KAAK,OAAO;GACnB,QAAQ,KAAK,MAAM;GACnB,QAAQ,YAAY,IAAI;EAC1B;EACA,YACE,MACA,OACA,aACA,cACG;GACH,IAAI,OAAO,UAAU,UAAU;IAC7B,MAAM,MAAM,MAAM,QAAQ,aAAa,CAAC;IACxC,MAAM,SAAS,cACX,GAAG,KAAK,OAAO,IAAI,SAAS,YAAY,KACxC,GAAG,KAAK,OAAO;IACnB,QAAQ,KAAK,MAAM;GACrB,OAAO;IACL,MAAM,SAAS,QAAQ,GAAG,KAAK,SAAS,MAAM,KAAK;IACnD,QAAQ,KAAK,MAAM;GACrB;EACF;EACA,YAAY,aAAuB;GACjC,IAAI,CAAC,SAAS;GACd,SAAS,QAAQ,OAAO,iBAAiB,QAAQ,KAAK,GAAG,CAAC;GAC1D,SAAS,QAAQ,OAAO,uBAAuB,eAAe,GAAG;EACnE;CACF;AACF"}
|
package/dist/webhook/index.cjs
CHANGED
|
@@ -5,8 +5,8 @@ let node_crypto = require("node:crypto");
|
|
|
5
5
|
const WEBHOOK_TOLERANCE_IN_SECONDS = 300;
|
|
6
6
|
function verifyTimestamp(webhookTimestamp) {
|
|
7
7
|
const now = Math.floor(Date.now() / 1e3);
|
|
8
|
-
const timestamp = parseInt(webhookTimestamp, 10);
|
|
9
|
-
if (isNaN(timestamp)) throw require_error_status.Status.invalidArgument("invalid webhook timestamp").error();
|
|
8
|
+
const timestamp = Number.parseInt(webhookTimestamp, 10);
|
|
9
|
+
if (Number.isNaN(timestamp)) throw require_error_status.Status.invalidArgument("invalid webhook timestamp").error();
|
|
10
10
|
if (timestamp < now - WEBHOOK_TOLERANCE_IN_SECONDS) throw require_error_status.Status.invalidArgument("webhook timestamp is too old").error();
|
|
11
11
|
if (timestamp > now + WEBHOOK_TOLERANCE_IN_SECONDS) throw require_error_status.Status.invalidArgument("webhook timestamp is too new").error();
|
|
12
12
|
return timestamp;
|
|
@@ -34,7 +34,7 @@ function verifyStandardWebhook(headers, payload, secret) {
|
|
|
34
34
|
if (version !== "v1") continue;
|
|
35
35
|
if ((0, node_crypto.timingSafeEqual)(encoder.encode(signature), encoder.encode(expectedSignature))) try {
|
|
36
36
|
return JSON.parse(payload);
|
|
37
|
-
} catch
|
|
37
|
+
} catch {
|
|
38
38
|
console.error("invalid payload", payload);
|
|
39
39
|
throw require_error_status.Status.invalidArgument("invalid webhook payload").error();
|
|
40
40
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":["Status"],"sources":["../../src/webhook/index.ts"],"sourcesContent":["import { createHmac, timingSafeEqual } from 'node:crypto';\nimport { Status } from '../error/status';\n\nconst WEBHOOK_TOLERANCE_IN_SECONDS = 5 * 60; // 5 minutes\n\nfunction verifyTimestamp(webhookTimestamp: string) {\n const now = Math.floor(Date.now() / 1000);\n const timestamp = parseInt(webhookTimestamp, 10);\n if (isNaN(timestamp)) {\n throw Status.invalidArgument('invalid webhook timestamp').error();\n }\n if (timestamp < now - WEBHOOK_TOLERANCE_IN_SECONDS) {\n throw Status.invalidArgument('webhook timestamp is too old').error();\n }\n if (timestamp > now + WEBHOOK_TOLERANCE_IN_SECONDS) {\n throw Status.invalidArgument('webhook timestamp is too new').error();\n }\n return timestamp;\n}\n\n/**\n * reference: https://github.com/standard-webhooks/standard-webhooks/tree/main/libraries/javascript\n * hono usage:\n * ```ts\n * const webhook = verifyStandardWebhook(c.req.header(), await c.req.text(), 'secret');\n * ```\n */\nexport function verifyStandardWebhook<T = unknown>(\n headers: Record<string, string>,\n payload: string,\n secret: string\n): T {\n const webhookId = headers['webhook-id'];\n const webhookTimestamp = headers['webhook-timestamp'];\n const webhookSignature = headers['webhook-signature'];\n if (!webhookId || !webhookTimestamp || !webhookSignature) {\n throw Status.invalidArgument('invalid webhook').error();\n }\n const timestamp = verifyTimestamp(webhookTimestamp);\n\n const encoder = new TextEncoder();\n const toSign = encoder.encode(`${webhookId}.${timestamp}.${payload}`);\n const hmac = createHmac('sha256', Buffer.from(secret, 'base64'));\n const digest = hmac.update(toSign).digest();\n\n const computedSignature = `v1,${Buffer.from(digest).toString('base64')}`;\n const expectedSignature = computedSignature.split(',')[1];\n const passedSignatures = webhookSignature.split(' ');\n\n for (const versionedSignature of passedSignatures) {\n const [version, signature] = versionedSignature.split(',');\n if (version !== 'v1') continue;\n if (timingSafeEqual(encoder.encode(signature), encoder.encode(expectedSignature))) {\n try {\n return JSON.parse(payload) as T;\n } catch
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["Status"],"sources":["../../src/webhook/index.ts"],"sourcesContent":["import { createHmac, timingSafeEqual } from 'node:crypto';\nimport { Status } from '../error/status';\n\nconst WEBHOOK_TOLERANCE_IN_SECONDS = 5 * 60; // 5 minutes\n\nfunction verifyTimestamp(webhookTimestamp: string) {\n const now = Math.floor(Date.now() / 1000);\n const timestamp = Number.parseInt(webhookTimestamp, 10);\n if (Number.isNaN(timestamp)) {\n throw Status.invalidArgument('invalid webhook timestamp').error();\n }\n if (timestamp < now - WEBHOOK_TOLERANCE_IN_SECONDS) {\n throw Status.invalidArgument('webhook timestamp is too old').error();\n }\n if (timestamp > now + WEBHOOK_TOLERANCE_IN_SECONDS) {\n throw Status.invalidArgument('webhook timestamp is too new').error();\n }\n return timestamp;\n}\n\n/**\n * reference: https://github.com/standard-webhooks/standard-webhooks/tree/main/libraries/javascript\n * hono usage:\n * ```ts\n * const webhook = verifyStandardWebhook(c.req.header(), await c.req.text(), 'secret');\n * ```\n */\nexport function verifyStandardWebhook<T = unknown>(\n headers: Record<string, string>,\n payload: string,\n secret: string\n): T {\n const webhookId = headers['webhook-id'];\n const webhookTimestamp = headers['webhook-timestamp'];\n const webhookSignature = headers['webhook-signature'];\n if (!webhookId || !webhookTimestamp || !webhookSignature) {\n throw Status.invalidArgument('invalid webhook').error();\n }\n const timestamp = verifyTimestamp(webhookTimestamp);\n\n const encoder = new TextEncoder();\n const toSign = encoder.encode(`${webhookId}.${timestamp}.${payload}`);\n const hmac = createHmac('sha256', Buffer.from(secret, 'base64'));\n const digest = hmac.update(toSign).digest();\n\n const computedSignature = `v1,${Buffer.from(digest).toString('base64')}`;\n const expectedSignature = computedSignature.split(',')[1];\n const passedSignatures = webhookSignature.split(' ');\n\n for (const versionedSignature of passedSignatures) {\n const [version, signature] = versionedSignature.split(',');\n if (version !== 'v1') continue;\n if (timingSafeEqual(encoder.encode(signature), encoder.encode(expectedSignature))) {\n try {\n return JSON.parse(payload) as T;\n } catch {\n console.error('invalid payload', payload);\n throw Status.invalidArgument('invalid webhook payload').error();\n }\n }\n }\n console.error('webhook verification failed');\n throw Status.invalidArgument('invalid webhook signature').error();\n}\n"],"mappings":";;;;AAGA,MAAM,+BAA+B;AAErC,SAAS,gBAAgB,kBAA0B;CACjD,MAAM,MAAM,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI;CACxC,MAAM,YAAY,OAAO,SAAS,kBAAkB,EAAE;CACtD,IAAI,OAAO,MAAM,SAAS,GACxB,MAAMA,qBAAAA,OAAO,gBAAgB,2BAA2B,CAAC,CAAC,MAAM;CAElE,IAAI,YAAY,MAAM,8BACpB,MAAMA,qBAAAA,OAAO,gBAAgB,8BAA8B,CAAC,CAAC,MAAM;CAErE,IAAI,YAAY,MAAM,8BACpB,MAAMA,qBAAAA,OAAO,gBAAgB,8BAA8B,CAAC,CAAC,MAAM;CAErE,OAAO;AACT;;;;;;;;AASA,SAAgB,sBACd,SACA,SACA,QACG;CACH,MAAM,YAAY,QAAQ;CAC1B,MAAM,mBAAmB,QAAQ;CACjC,MAAM,mBAAmB,QAAQ;CACjC,IAAI,CAAC,aAAa,CAAC,oBAAoB,CAAC,kBACtC,MAAMA,qBAAAA,OAAO,gBAAgB,iBAAiB,CAAC,CAAC,MAAM;CAExD,MAAM,YAAY,gBAAgB,gBAAgB;CAElD,MAAM,UAAU,IAAI,YAAY;CAChC,MAAM,SAAS,QAAQ,OAAO,GAAG,UAAU,GAAG,UAAU,GAAG,SAAS;CAEpE,MAAM,UAAA,GAAA,YAAA,WAAA,CADkB,UAAU,OAAO,KAAK,QAAQ,QAAQ,CAC5C,CAAC,CAAC,OAAO,MAAM,CAAC,CAAC,OAAO;CAG1C,MAAM,oBAAoB,MADM,OAAO,KAAK,MAAM,CAAC,CAAC,SAAS,QAAQ,IACzB,MAAM,GAAG,CAAC,CAAC;CACvD,MAAM,mBAAmB,iBAAiB,MAAM,GAAG;CAEnD,KAAK,MAAM,sBAAsB,kBAAkB;EACjD,MAAM,CAAC,SAAS,aAAa,mBAAmB,MAAM,GAAG;EACzD,IAAI,YAAY,MAAM;EACtB,KAAA,GAAA,YAAA,gBAAA,CAAoB,QAAQ,OAAO,SAAS,GAAG,QAAQ,OAAO,iBAAiB,CAAC,GAC9E,IAAI;GACF,OAAO,KAAK,MAAM,OAAO;EAC3B,QAAQ;GACN,QAAQ,MAAM,mBAAmB,OAAO;GACxC,MAAMA,qBAAAA,OAAO,gBAAgB,yBAAyB,CAAC,CAAC,MAAM;EAChE;CAEJ;CACA,QAAQ,MAAM,6BAA6B;CAC3C,MAAMA,qBAAAA,OAAO,gBAAgB,2BAA2B,CAAC,CAAC,MAAM;AAClE"}
|
package/dist/webhook/index.mjs
CHANGED
|
@@ -4,8 +4,8 @@ import { createHmac, timingSafeEqual } from "node:crypto";
|
|
|
4
4
|
const WEBHOOK_TOLERANCE_IN_SECONDS = 300;
|
|
5
5
|
function verifyTimestamp(webhookTimestamp) {
|
|
6
6
|
const now = Math.floor(Date.now() / 1e3);
|
|
7
|
-
const timestamp = parseInt(webhookTimestamp, 10);
|
|
8
|
-
if (isNaN(timestamp)) throw Status.invalidArgument("invalid webhook timestamp").error();
|
|
7
|
+
const timestamp = Number.parseInt(webhookTimestamp, 10);
|
|
8
|
+
if (Number.isNaN(timestamp)) throw Status.invalidArgument("invalid webhook timestamp").error();
|
|
9
9
|
if (timestamp < now - WEBHOOK_TOLERANCE_IN_SECONDS) throw Status.invalidArgument("webhook timestamp is too old").error();
|
|
10
10
|
if (timestamp > now + WEBHOOK_TOLERANCE_IN_SECONDS) throw Status.invalidArgument("webhook timestamp is too new").error();
|
|
11
11
|
return timestamp;
|
|
@@ -33,7 +33,7 @@ function verifyStandardWebhook(headers, payload, secret) {
|
|
|
33
33
|
if (version !== "v1") continue;
|
|
34
34
|
if (timingSafeEqual(encoder.encode(signature), encoder.encode(expectedSignature))) try {
|
|
35
35
|
return JSON.parse(payload);
|
|
36
|
-
} catch
|
|
36
|
+
} catch {
|
|
37
37
|
console.error("invalid payload", payload);
|
|
38
38
|
throw Status.invalidArgument("invalid webhook payload").error();
|
|
39
39
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../../src/webhook/index.ts"],"sourcesContent":["import { createHmac, timingSafeEqual } from 'node:crypto';\nimport { Status } from '../error/status';\n\nconst WEBHOOK_TOLERANCE_IN_SECONDS = 5 * 60; // 5 minutes\n\nfunction verifyTimestamp(webhookTimestamp: string) {\n const now = Math.floor(Date.now() / 1000);\n const timestamp = parseInt(webhookTimestamp, 10);\n if (isNaN(timestamp)) {\n throw Status.invalidArgument('invalid webhook timestamp').error();\n }\n if (timestamp < now - WEBHOOK_TOLERANCE_IN_SECONDS) {\n throw Status.invalidArgument('webhook timestamp is too old').error();\n }\n if (timestamp > now + WEBHOOK_TOLERANCE_IN_SECONDS) {\n throw Status.invalidArgument('webhook timestamp is too new').error();\n }\n return timestamp;\n}\n\n/**\n * reference: https://github.com/standard-webhooks/standard-webhooks/tree/main/libraries/javascript\n * hono usage:\n * ```ts\n * const webhook = verifyStandardWebhook(c.req.header(), await c.req.text(), 'secret');\n * ```\n */\nexport function verifyStandardWebhook<T = unknown>(\n headers: Record<string, string>,\n payload: string,\n secret: string\n): T {\n const webhookId = headers['webhook-id'];\n const webhookTimestamp = headers['webhook-timestamp'];\n const webhookSignature = headers['webhook-signature'];\n if (!webhookId || !webhookTimestamp || !webhookSignature) {\n throw Status.invalidArgument('invalid webhook').error();\n }\n const timestamp = verifyTimestamp(webhookTimestamp);\n\n const encoder = new TextEncoder();\n const toSign = encoder.encode(`${webhookId}.${timestamp}.${payload}`);\n const hmac = createHmac('sha256', Buffer.from(secret, 'base64'));\n const digest = hmac.update(toSign).digest();\n\n const computedSignature = `v1,${Buffer.from(digest).toString('base64')}`;\n const expectedSignature = computedSignature.split(',')[1];\n const passedSignatures = webhookSignature.split(' ');\n\n for (const versionedSignature of passedSignatures) {\n const [version, signature] = versionedSignature.split(',');\n if (version !== 'v1') continue;\n if (timingSafeEqual(encoder.encode(signature), encoder.encode(expectedSignature))) {\n try {\n return JSON.parse(payload) as T;\n } catch
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../../src/webhook/index.ts"],"sourcesContent":["import { createHmac, timingSafeEqual } from 'node:crypto';\nimport { Status } from '../error/status';\n\nconst WEBHOOK_TOLERANCE_IN_SECONDS = 5 * 60; // 5 minutes\n\nfunction verifyTimestamp(webhookTimestamp: string) {\n const now = Math.floor(Date.now() / 1000);\n const timestamp = Number.parseInt(webhookTimestamp, 10);\n if (Number.isNaN(timestamp)) {\n throw Status.invalidArgument('invalid webhook timestamp').error();\n }\n if (timestamp < now - WEBHOOK_TOLERANCE_IN_SECONDS) {\n throw Status.invalidArgument('webhook timestamp is too old').error();\n }\n if (timestamp > now + WEBHOOK_TOLERANCE_IN_SECONDS) {\n throw Status.invalidArgument('webhook timestamp is too new').error();\n }\n return timestamp;\n}\n\n/**\n * reference: https://github.com/standard-webhooks/standard-webhooks/tree/main/libraries/javascript\n * hono usage:\n * ```ts\n * const webhook = verifyStandardWebhook(c.req.header(), await c.req.text(), 'secret');\n * ```\n */\nexport function verifyStandardWebhook<T = unknown>(\n headers: Record<string, string>,\n payload: string,\n secret: string\n): T {\n const webhookId = headers['webhook-id'];\n const webhookTimestamp = headers['webhook-timestamp'];\n const webhookSignature = headers['webhook-signature'];\n if (!webhookId || !webhookTimestamp || !webhookSignature) {\n throw Status.invalidArgument('invalid webhook').error();\n }\n const timestamp = verifyTimestamp(webhookTimestamp);\n\n const encoder = new TextEncoder();\n const toSign = encoder.encode(`${webhookId}.${timestamp}.${payload}`);\n const hmac = createHmac('sha256', Buffer.from(secret, 'base64'));\n const digest = hmac.update(toSign).digest();\n\n const computedSignature = `v1,${Buffer.from(digest).toString('base64')}`;\n const expectedSignature = computedSignature.split(',')[1];\n const passedSignatures = webhookSignature.split(' ');\n\n for (const versionedSignature of passedSignatures) {\n const [version, signature] = versionedSignature.split(',');\n if (version !== 'v1') continue;\n if (timingSafeEqual(encoder.encode(signature), encoder.encode(expectedSignature))) {\n try {\n return JSON.parse(payload) as T;\n } catch {\n console.error('invalid payload', payload);\n throw Status.invalidArgument('invalid webhook payload').error();\n }\n }\n }\n console.error('webhook verification failed');\n throw Status.invalidArgument('invalid webhook signature').error();\n}\n"],"mappings":";;;AAGA,MAAM,+BAA+B;AAErC,SAAS,gBAAgB,kBAA0B;CACjD,MAAM,MAAM,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI;CACxC,MAAM,YAAY,OAAO,SAAS,kBAAkB,EAAE;CACtD,IAAI,OAAO,MAAM,SAAS,GACxB,MAAM,OAAO,gBAAgB,2BAA2B,CAAC,CAAC,MAAM;CAElE,IAAI,YAAY,MAAM,8BACpB,MAAM,OAAO,gBAAgB,8BAA8B,CAAC,CAAC,MAAM;CAErE,IAAI,YAAY,MAAM,8BACpB,MAAM,OAAO,gBAAgB,8BAA8B,CAAC,CAAC,MAAM;CAErE,OAAO;AACT;;;;;;;;AASA,SAAgB,sBACd,SACA,SACA,QACG;CACH,MAAM,YAAY,QAAQ;CAC1B,MAAM,mBAAmB,QAAQ;CACjC,MAAM,mBAAmB,QAAQ;CACjC,IAAI,CAAC,aAAa,CAAC,oBAAoB,CAAC,kBACtC,MAAM,OAAO,gBAAgB,iBAAiB,CAAC,CAAC,MAAM;CAExD,MAAM,YAAY,gBAAgB,gBAAgB;CAElD,MAAM,UAAU,IAAI,YAAY;CAChC,MAAM,SAAS,QAAQ,OAAO,GAAG,UAAU,GAAG,UAAU,GAAG,SAAS;CAEpE,MAAM,SADO,WAAW,UAAU,OAAO,KAAK,QAAQ,QAAQ,CAC5C,CAAC,CAAC,OAAO,MAAM,CAAC,CAAC,OAAO;CAG1C,MAAM,oBAAoB,MADM,OAAO,KAAK,MAAM,CAAC,CAAC,SAAS,QAAQ,IACzB,MAAM,GAAG,CAAC,CAAC;CACvD,MAAM,mBAAmB,iBAAiB,MAAM,GAAG;CAEnD,KAAK,MAAM,sBAAsB,kBAAkB;EACjD,MAAM,CAAC,SAAS,aAAa,mBAAmB,MAAM,GAAG;EACzD,IAAI,YAAY,MAAM;EACtB,IAAI,gBAAgB,QAAQ,OAAO,SAAS,GAAG,QAAQ,OAAO,iBAAiB,CAAC,GAC9E,IAAI;GACF,OAAO,KAAK,MAAM,OAAO;EAC3B,QAAQ;GACN,QAAQ,MAAM,mBAAmB,OAAO;GACxC,MAAM,OAAO,gBAAgB,yBAAyB,CAAC,CAAC,MAAM;EAChE;CAEJ;CACA,QAAQ,MAAM,6BAA6B;CAC3C,MAAM,OAAO,gBAAgB,2BAA2B,CAAC,CAAC,MAAM;AAClE"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shware/http",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.2.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -66,19 +66,19 @@
|
|
|
66
66
|
"access": "public"
|
|
67
67
|
},
|
|
68
68
|
"dependencies": {
|
|
69
|
-
"
|
|
70
|
-
"
|
|
71
|
-
"
|
|
69
|
+
"@shware/utils": "^1.6.1",
|
|
70
|
+
"vitest": "^4.1.11",
|
|
71
|
+
"zod": "^4.4.3"
|
|
72
72
|
},
|
|
73
73
|
"devDependencies": {
|
|
74
|
-
"@
|
|
74
|
+
"@repo/typescript-config": "0.0.0",
|
|
75
|
+
"@types/node": "^26.3.0",
|
|
75
76
|
"@types/react": "^19.2.18",
|
|
76
|
-
"typescript": "^7.0.2"
|
|
77
|
-
"@repo/typescript-config": "0.0.0"
|
|
77
|
+
"typescript": "^7.0.2"
|
|
78
78
|
},
|
|
79
79
|
"peerDependencies": {
|
|
80
|
-
"hono": "^4.
|
|
81
|
-
"i18next": "^26.
|
|
80
|
+
"hono": "^4.13.5",
|
|
81
|
+
"i18next": "^26.4.0",
|
|
82
82
|
"react": "^19.2.8"
|
|
83
83
|
},
|
|
84
84
|
"peerDependenciesMeta": {
|