UNPKG

@open-condo/miniapp-utils

Version:

A set of helper functions / components / hooks used to build new condo apps fast

163 lines (157 loc) 5.58 kB
// src/helpers/tracing.ts import { parse as parseCookieString } from "cookie"; // src/helpers/sender.ts import { getCookie as getCookie2, setCookie as setCookie2 } from "cookies-next"; // src/helpers/embeddingContext.tsx import { deleteCookie, getCookie, setCookie } from "cookies-next"; import React, { useEffect, useMemo, useState, createContext, useContext } from "react"; import { z } from "zod"; // src/helpers/uuid.ts import { randomBytes } from "crypto"; function generateUUIDv4() { let randomValues; if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") { return crypto.randomUUID(); } else if (typeof window !== "undefined" && window.crypto && window.crypto.getRandomValues) { randomValues = new Uint8Array(16); window.crypto.getRandomValues(randomValues); } else { randomValues = randomBytes(16); } randomValues[6] = randomValues[6] & 15 | 64; randomValues[8] = randomValues[8] & 63 | 128; return [...randomValues].map((value, index) => { const hex = value.toString(16).padStart(2, "0"); if (index === 4 || index === 6 || index === 8 || index === 10) { return `-${hex}`; } return hex; }).join(""); } // src/helpers/embeddingContext.tsx var EMBEDDING_CONTEXT_COOKIE_NAME = "embeddingContext"; var EMBEDDING_CONTEXT_QUERY_PARAM = "embeddingContext"; var EMBEDDING_CONTEXT_SCHEMA = z.strictObject({ dv: z.literal(1), app: z.strictObject({ id: z.string(), version: z.string().optional(), build: z.string().optional() }), platform: z.enum(["iOS", "Android", "web"]), os: z.strictObject({ name: z.string(), version: z.string().optional() }).optional(), device: z.strictObject({ id: z.string() }) }); var EMBEDDING_CONTEXT_WITH_SOURCE_SCHEMA = z.strictObject({ ctx: EMBEDDING_CONTEXT_SCHEMA, source: z.enum(["query", "cookie"]) }); var IS_PRIMARY_ALIVE_MESSAGE_SCHEMA = z.object({ type: z.literal("EmbeddingContextPrimaryPolling"), data: z.strictObject({ requestId: z.string() }) }); var IS_PRIMARY_ALIVE_RESPONSE_SCHEMA = z.object({ type: z.literal("EmbeddingContextPrimaryPollingResult"), data: z.strictObject({ requestId: z.string(), isPrimary: z.boolean() }) }); var ReactEmbeddingContext = createContext(null); function b64toContext(b64) { try { const bytes = Uint8Array.from(atob(b64), (c) => c.charCodeAt(0)); const decodedUTFString = new TextDecoder().decode(bytes); const parsedCtx = JSON.parse(decodedUTFString); return EMBEDDING_CONTEXT_SCHEMA.parse(parsedCtx); } catch { return null; } } function getEmbeddingContext(req, res) { try { const queryParamValue = req ? new URL(req.url ?? "/", "https://_").searchParams.get(EMBEDDING_CONTEXT_QUERY_PARAM) : new URLSearchParams(window.location.search).get(EMBEDDING_CONTEXT_QUERY_PARAM); if (queryParamValue) { const ctx = b64toContext(decodeURIComponent(queryParamValue)); if (ctx) return { ctx, source: "query" }; } } catch { } const cookieValue = getCookie(EMBEDDING_CONTEXT_COOKIE_NAME, { req, res }); if (cookieValue) { const ctx = b64toContext(cookieValue); if (ctx) return { ctx, source: "cookie" }; } return null; } // src/helpers/sender.ts var FINGERPRINT_ID_COOKIE_NAME = "fingerprint"; var FINGERPRINT_ID_LENGTH = 32; var VERY_LONG_MAX_AGE_IN_SECONDS = Math.pow(2, 31) - 1; function makeId(length) { const croppedLength = Math.min(length, 32); return generateUUIDv4().replaceAll("-", "").substring(0, croppedLength); } function generateFingerprint() { return makeId(FINGERPRINT_ID_LENGTH); } function getClientSideFingerprint() { const embeddingContext = getEmbeddingContext(); if (embeddingContext) { return embeddingContext.ctx.device.id; } let fingerprint = getCookie2(FINGERPRINT_ID_COOKIE_NAME); if (!fingerprint) { fingerprint = generateFingerprint(); } setCookie2(FINGERPRINT_ID_COOKIE_NAME, fingerprint, { maxAge: VERY_LONG_MAX_AGE_IN_SECONDS // no "maxAge" or "expires" means that cookie clears when session ends (f.e. when browser closes) }); return fingerprint; } // src/helpers/tracing.ts var SSR_DEFAULT_FINGERPRINT = "webAppSSR"; var COOKIE_HEADER_NAME = "cookie"; var REMOTE_APP_HEADER_NAME = "x-remote-app"; var REMOTE_VERSION_HEADER_NAME = "x-remote-version"; var REMOTE_CLIENT_HEADER_NAME = "x-remote-client"; var REMOTE_ENV_HEADER_NAME = "x-remote-env"; var TARGET_HEADER_NAME = "x-target"; var START_REQUEST_ID_HEADER_NAME = "x-start-request-id"; var PARENT_REQUEST_ID_HEADER_NAME = "x-parent-request-id"; function generateRequestId() { return `BR${generateUUIDv4().replaceAll("-", "")}`; } function getAppTracingHeaders(options) { const reqId = generateRequestId(); const headers = { ...options.previousHeaders, [REMOTE_APP_HEADER_NAME]: options.serviceUrl, [REMOTE_VERSION_HEADER_NAME]: options.codeVersion, [PARENT_REQUEST_ID_HEADER_NAME]: reqId, [START_REQUEST_ID_HEADER_NAME]: reqId }; if (options.target) { headers[TARGET_HEADER_NAME] = options.target; } headers[REMOTE_ENV_HEADER_NAME] = typeof document === "undefined" ? "SSR" : "CSR"; if (typeof document !== "undefined" && document.cookie) { headers[REMOTE_CLIENT_HEADER_NAME] = getClientSideFingerprint(); } else if (headers[COOKIE_HEADER_NAME]) { const ssrCookies = parseCookieString(headers[COOKIE_HEADER_NAME]); headers[REMOTE_CLIENT_HEADER_NAME] = ssrCookies[FINGERPRINT_ID_COOKIE_NAME] || SSR_DEFAULT_FINGERPRINT; } return headers; } export { getAppTracingHeaders }; //# sourceMappingURL=tracing.mjs.map