@ops-ai/react-router-toggly 1.0.1 → 1.2.0

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.
@@ -15,7 +15,7 @@ var path__default = /*#__PURE__*/_interopDefault(path);
15
15
 
16
16
  // src/core/sdk-identity.ts
17
17
  var SDK_ID = "react-router";
18
- var SDK_VERSION = "1.0.1";
18
+ var SDK_VERSION = "1.2.0";
19
19
  function sdkUserAgent() {
20
20
  return `toggly-${SDK_ID}/${SDK_VERSION}`;
21
21
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/core/sdk-identity.ts","../../src/core/telemetry/https-client.ts","../../src/core/telemetry/grpc-clients.ts"],"names":["createRequire","path","fileURLToPath","fs"],"mappings":"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","file":"grpc.cjs","sourcesContent":["export const SDK_ID = 'react-router'\nexport const SDK_VERSION = '1.0.1'\n\nexport const SDK_HEADER_ID = 'X-Toggly-Sdk'\nexport const SDK_HEADER_VERSION = 'X-Toggly-Sdk-Version'\n\nexport function sdkUserAgent(): string {\n return `toggly-${SDK_ID}/${SDK_VERSION}`\n}\n\nexport function sdkCustomHeaders(): Record<string, string> {\n return {\n [SDK_HEADER_ID]: SDK_ID,\n [SDK_HEADER_VERSION]: SDK_VERSION,\n }\n}\n\nexport function appendSdkQueryParams(params: URLSearchParams): void {\n params.set('sdk', SDK_ID)\n params.set('sdkVersion', SDK_VERSION)\n}\n\ntype BrowserLikeGlobal = typeof globalThis & {\n window?: unknown\n document?: unknown\n navigator?: { product?: string }\n}\n\n/** Browser and React Native use custom headers on HTTP (User-Agent is forbidden in browser fetch). */\nexport function usesSdkCustomHeaders(): boolean {\n const g = globalThis as BrowserLikeGlobal\n if (g.window !== undefined && g.document !== undefined) {\n return true\n }\n if (g.navigator?.product === 'ReactNative') {\n return true\n }\n return false\n}\n\nexport function buildDefinitionFetchHeaders(\n existing: Record<string, string> = {},\n): Record<string, string> {\n const headers = { ...existing }\n if (usesSdkCustomHeaders()) {\n Object.assign(headers, sdkCustomHeaders())\n } else {\n headers['User-Agent'] = sdkUserAgent()\n }\n return headers\n}\n","import { sdkUserAgent } from '../sdk-identity.js'\nimport { protobufTimestampToIso } from './hash.js'\nimport type { FeatureStatPayload } from './usage-batcher.js'\nimport type { MetricStatPayload } from './metrics-batcher.js'\n\nexport const DEFAULT_METRICS_BASE_URL = 'https://metrics.toggly.io/'\nexport const DEFAULT_TELEMETRY_FLUSH_MS = 60_000\n\nexport interface HttpsTelemetryClientOptions {\n metricsBaseUrl?: string\n userAgent?: string\n fetchImpl?: typeof fetch\n}\n\nfunction ensureTrailingSlash(baseUrl: string): string {\n return baseUrl.endsWith('/') ? baseUrl : `${baseUrl}/`\n}\n\n/** Convert protobuf-timestamp usage payload to gateway HTTPS JSON (PHP parity). */\nexport function usagePayloadToHttpJson(payload: FeatureStatPayload): Record<string, unknown> {\n return {\n ...payload,\n time: protobufTimestampToIso(payload.time),\n ...(payload.processStartTime\n ? { processStartTime: protobufTimestampToIso(payload.processStartTime) }\n : {}),\n }\n}\n\n/** Convert protobuf-timestamp metrics payload to gateway HTTPS JSON (PHP parity). */\nexport function metricsPayloadToHttpJson(payload: MetricStatPayload): Record<string, unknown> {\n return {\n ...payload,\n time: protobufTimestampToIso(payload.time),\n observations: (payload.observations ?? []).map((obs) => ({\n ...obs,\n time: protobufTimestampToIso(obs.time),\n })),\n }\n}\n\n/**\n * Soft-fail HTTPS JSON client for gateway-accepted usage/metrics paths.\n * Network and non-2xx errors never throw to callers.\n */\nexport class HttpsTelemetryClient {\n private readonly baseUrl: string\n private readonly userAgent: string\n private readonly fetchImpl: typeof fetch\n\n constructor(options: HttpsTelemetryClientOptions = {}) {\n this.baseUrl = ensureTrailingSlash(options.metricsBaseUrl ?? DEFAULT_METRICS_BASE_URL)\n this.userAgent = options.userAgent ?? sdkUserAgent()\n this.fetchImpl = options.fetchImpl ?? fetch.bind(globalThis)\n }\n\n getUserAgent(): string {\n return this.userAgent\n }\n\n getBaseUrl(): string {\n return this.baseUrl\n }\n\n /**\n * POST JSON. Returns true on 2xx, false on soft-fail (network / non-2xx).\n */\n async post(path: string, body: unknown): Promise<boolean> {\n try {\n const url = new URL(path.replace(/^\\//, ''), this.baseUrl).toString()\n const response = await this.fetchImpl(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Accept: 'application/json',\n 'User-Agent': this.userAgent,\n },\n body: JSON.stringify(body),\n })\n return response.ok\n } catch {\n return false\n }\n }\n\n sendUsageStats(payload: FeatureStatPayload): Promise<boolean> {\n return this.post('api/usage/stats', usagePayloadToHttpJson(payload))\n }\n\n sendMetrics(payload: MetricStatPayload): Promise<boolean> {\n return this.post('api/metrics', metricsPayloadToHttpJson(payload))\n }\n}\n\nexport function resolveMetricsBaseUrl(raw?: string): string {\n const trimmed = (raw ?? '').trim()\n if (!trimmed) {\n return DEFAULT_METRICS_BASE_URL\n }\n return ensureTrailingSlash(trimmed)\n}\n\nexport function isTelemetryEnvDisabled(): boolean {\n try {\n return typeof process !== 'undefined' && process.env?.TOGGLY_DISABLE_TELEMETRY === '1'\n } catch {\n return false\n }\n}\n\n/**\n * Resolve an enable flag. `TOGGLY_DISABLE_TELEMETRY=1` is authoritative and\n * wins over any explicit `true` in config.\n */\nexport function resolveTelemetryEnableFlag(\n explicit: boolean | undefined,\n defaultWhenUnset: boolean,\n): boolean {\n if (isTelemetryEnvDisabled()) {\n return false\n }\n return explicit ?? defaultWhenUnset\n}\n","import { createRequire } from 'node:module'\nimport fs from 'node:fs'\nimport path from 'node:path'\nimport { fileURLToPath } from 'node:url'\nimport { sdkUserAgent } from '../sdk-identity.js'\nimport { DEFAULT_METRICS_BASE_URL } from './https-client.js'\n\ndeclare global {\n var __TOGGLY_MODULE_URL__: string\n}\n\n// Rollup replaces this test seam with the native module URL for each output:\n// `import.meta.url` in ESM and `pathToFileURL(__filename).href` in CommonJS.\n// This lets a packed SDK find its proto files without asking the consuming app\n// to supply a process-global filename.\nconst thisModuleUrl = globalThis.__TOGGLY_MODULE_URL__\nconst nodeRequire = createRequire(thisModuleUrl)\n\n/**\n * Resolve vendored proto/ for source (src/telemetry), bundled dist/ ESM+CJS,\n * and the published package layout (files includes \"proto\").\n */\nexport function resolveProtoRoot(moduleUrl: string = thisModuleUrl): string {\n const moduleDir = path.dirname(fileURLToPath(moduleUrl))\n const candidates = [\n // Built dist/cjs|esm/telemetry/grpc.js → packageRoot/proto\n path.resolve(moduleDir, '..', '..', '..', 'proto'),\n // Built dist/telemetry/grpc.js → packageRoot/proto\n path.resolve(moduleDir, '..', '..', 'proto'),\n // Built dist/index.js sibling layouts\n path.resolve(moduleDir, '..', 'proto'),\n // Source src/telemetry/*.ts → packageRoot/proto\n path.resolve(moduleDir, '..', '..', 'proto'),\n path.resolve(moduleDir, 'proto'),\n ]\n for (const candidate of candidates) {\n if (fs.existsSync(path.join(candidate, 'usage.proto'))) {\n return candidate\n }\n }\n return candidates[0] ?? path.resolve(moduleDir, '..', '..', 'proto')\n}\n\nconst protoRoot = resolveProtoRoot()\n\nexport function grpcTarget(baseUrl: string): string {\n try {\n const u = new URL(baseUrl.includes('://') ? baseUrl : `https://${baseUrl}`)\n let host = u.host\n if (!host.includes(':')) {\n host = `${host}:443`\n }\n return host\n } catch {\n const trimmed = baseUrl.replace(/^https?:\\/\\//, '').replace(/\\/$/, '')\n return trimmed.includes(':') ? trimmed : `${trimmed}:443`\n }\n}\n\nexport function resolveUserAgent(override?: string): string {\n return override ?? sdkUserAgent()\n}\n\nexport type GrpcMetadataMap = Record<string, string>\n\nexport interface UsageGrpcClient {\n sendStats(\n request: Record<string, unknown>,\n metadata?: GrpcMetadataMap,\n ): Promise<{ featureCount?: number }>\n close(): void\n}\n\nexport interface MetricsGrpcClient {\n sendMetrics(\n request: Record<string, unknown>,\n metadata?: GrpcMetadataMap,\n ): Promise<{ count?: number }>\n close(): void\n}\n\nexport interface GrpcClients {\n usage: UsageGrpcClient\n metrics: MetricsGrpcClient\n}\n\ntype ProtoLoader = typeof import('@grpc/proto-loader')\ntype GrpcJs = typeof import('@grpc/grpc-js')\n\nfunction tryLoadGrpcModules(): { grpc: GrpcJs; protoLoader: ProtoLoader } | null {\n try {\n // Optional peer/optionalDependencies — evaluate flags without gRPC installed.\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const grpc = nodeRequire('@grpc/grpc-js') as GrpcJs\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const protoLoader = nodeRequire('@grpc/proto-loader') as ProtoLoader\n return { grpc, protoLoader }\n } catch {\n return null\n }\n}\n\nfunction loadPackageDefinition(\n protoLoader: ProtoLoader,\n grpc: GrpcJs,\n protoFile: string,\n): Record<string, unknown> {\n const packageDefinition = protoLoader.loadSync(path.join(protoRoot, protoFile), {\n keepCase: true,\n longs: Number,\n enums: String,\n defaults: true,\n oneofs: true,\n includeDirs: [protoRoot],\n })\n return grpc.loadPackageDefinition(packageDefinition) as Record<string, unknown>\n}\n\nfunction createMetadata(grpc: GrpcJs, map?: GrpcMetadataMap): InstanceType<GrpcJs['Metadata']> {\n const metadata = new grpc.Metadata()\n if (map) {\n for (const [key, value] of Object.entries(map)) {\n metadata.set(key, value)\n }\n }\n return metadata\n}\n\n/**\n * Dial usage + metrics gRPC clients against metricsBaseUrl.\n * Returns null when @grpc/grpc-js (+ proto-loader) are not installed.\n */\nexport function createGrpcClients(\n metricsBaseUrl: string = DEFAULT_METRICS_BASE_URL,\n userAgent?: string,\n): GrpcClients | null {\n const modules = tryLoadGrpcModules()\n if (!modules) {\n return null\n }\n\n const { grpc, protoLoader } = modules\n const target = grpcTarget(metricsBaseUrl)\n const credentials = grpc.credentials.createSsl()\n const ua = resolveUserAgent(userAgent)\n const defaultMeta: GrpcMetadataMap = { UA: ua }\n\n const usageDef = loadPackageDefinition(protoLoader, grpc, 'usage.proto')\n const metricsDef = loadPackageDefinition(protoLoader, grpc, 'metrics.proto')\n\n const UsageService = (usageDef as { Usage: { Usage: new (...args: unknown[]) => unknown } }).Usage\n .Usage as new (\n address: string,\n creds: unknown,\n ) => {\n SendStats: (\n req: unknown,\n meta: unknown,\n cb: (err: Error | null, res: { featureCount?: number }) => void,\n ) => void\n close: () => void\n }\n\n const MetricsService = (\n metricsDef as { Metrics: { Metrics: new (...args: unknown[]) => unknown } }\n ).Metrics.Metrics as new (\n address: string,\n creds: unknown,\n ) => {\n SendMetrics: (\n req: unknown,\n meta: unknown,\n cb: (err: Error | null, res: { count?: number }) => void,\n ) => void\n close: () => void\n }\n\n const usageStub = new UsageService(target, credentials)\n const metricsStub = new MetricsService(target, credentials)\n\n return {\n usage: {\n sendStats(request, metadata) {\n return new Promise((resolve, reject) => {\n usageStub.SendStats(\n request,\n createMetadata(grpc, { ...defaultMeta, ...metadata }),\n (err, res) => {\n if (err) reject(err)\n else resolve(res ?? {})\n },\n )\n })\n },\n close() {\n usageStub.close()\n },\n },\n metrics: {\n sendMetrics(request, metadata) {\n return new Promise((resolve, reject) => {\n metricsStub.SendMetrics(\n request,\n createMetadata(grpc, { ...defaultMeta, ...metadata }),\n (err, res) => {\n if (err) reject(err)\n else resolve(res ?? {})\n },\n )\n })\n },\n close() {\n metricsStub.close()\n },\n },\n }\n}\n\nexport function isGrpcAvailable(): boolean {\n return tryLoadGrpcModules() !== null\n}\n\nexport function getProtoRoot(): string {\n return protoRoot\n}\n\nexport { DEFAULT_METRICS_BASE_URL }\n"]}
1
+ {"version":3,"sources":["../../src/core/sdk-identity.ts","../../src/core/telemetry/https-client.ts","../../src/core/telemetry/grpc-clients.ts"],"names":["createRequire","path","fileURLToPath","fs"],"mappings":";;;;;;;;;;;;;;;;AAAO,IAAM,MAAA,GAAS,cAAA;AACf,IAAM,WAAA,GAAc,OAAA;AAKpB,SAAS,YAAA,GAAuB;AACrC,EAAA,OAAO,CAAA,OAAA,EAAU,MAAM,CAAA,CAAA,EAAI,WAAW,CAAA,CAAA;AACxC;;;ACHO,IAAM,wBAAA,GAA2B,4BAAA;;;ACUxC,IAAM,aAAA,GAAgB,0PAAA;AACtB,IAAM,WAAA,GAAcA,uBAAc,aAAa,CAAA;AAMxC,SAAS,gBAAA,CAAiB,YAAoB,aAAA,EAAuB;AAC1E,EAAA,MAAM,SAAA,GAAYC,qBAAA,CAAK,OAAA,CAAQC,iBAAA,CAAc,SAAS,CAAC,CAAA;AACvD,EAAA,MAAM,UAAA,GAAa;AAAA;AAAA,IAEjBD,sBAAK,OAAA,CAAQ,SAAA,EAAW,IAAA,EAAM,IAAA,EAAM,MAAM,OAAO,CAAA;AAAA;AAAA,IAEjDA,qBAAA,CAAK,OAAA,CAAQ,SAAA,EAAW,IAAA,EAAM,MAAM,OAAO,CAAA;AAAA;AAAA,IAE3CA,qBAAA,CAAK,OAAA,CAAQ,SAAA,EAAW,IAAA,EAAM,OAAO,CAAA;AAAA;AAAA,IAErCA,qBAAA,CAAK,OAAA,CAAQ,SAAA,EAAW,IAAA,EAAM,MAAM,OAAO,CAAA;AAAA,IAC3CA,qBAAA,CAAK,OAAA,CAAQ,SAAA,EAAW,OAAO;AAAA,GACjC;AACA,EAAA,KAAA,MAAW,aAAa,UAAA,EAAY;AAClC,IAAA,IAAIE,oBAAG,UAAA,CAAWF,qBAAA,CAAK,KAAK,SAAA,EAAW,aAAa,CAAC,CAAA,EAAG;AACtD,MAAA,OAAO,SAAA;AAAA,IACT;AAAA,EACF;AACA,EAAA,OAAO,UAAA,CAAW,CAAC,CAAA,IAAKA,qBAAA,CAAK,QAAQ,SAAA,EAAW,IAAA,EAAM,MAAM,OAAO,CAAA;AACrE;AAEA,IAAM,YAAY,gBAAA,EAAiB;AAE5B,SAAS,WAAW,OAAA,EAAyB;AAClD,EAAA,IAAI;AACF,IAAA,MAAM,CAAA,GAAI,IAAI,GAAA,CAAI,OAAA,CAAQ,QAAA,CAAS,KAAK,CAAA,GAAI,OAAA,GAAU,CAAA,QAAA,EAAW,OAAO,CAAA,CAAE,CAAA;AAC1E,IAAA,IAAI,OAAO,CAAA,CAAE,IAAA;AACb,IAAA,IAAI,CAAC,IAAA,CAAK,QAAA,CAAS,GAAG,CAAA,EAAG;AACvB,MAAA,IAAA,GAAO,GAAG,IAAI,CAAA,IAAA,CAAA;AAAA,IAChB;AACA,IAAA,OAAO,IAAA;AAAA,EACT,CAAA,CAAA,MAAQ;AACN,IAAA,MAAM,OAAA,GAAU,QAAQ,OAAA,CAAQ,cAAA,EAAgB,EAAE,CAAA,CAAE,OAAA,CAAQ,OAAO,EAAE,CAAA;AACrE,IAAA,OAAO,QAAQ,QAAA,CAAS,GAAG,CAAA,GAAI,OAAA,GAAU,GAAG,OAAO,CAAA,IAAA,CAAA;AAAA,EACrD;AACF;AAEO,SAAS,iBAAiB,QAAA,EAA2B;AAC1D,EAAA,OAAO,YAAY,YAAA,EAAa;AAClC;AA4BA,SAAS,kBAAA,GAAwE;AAC/E,EAAA,IAAI;AAGF,IAAA,MAAM,IAAA,GAAO,YAAY,eAAe,CAAA;AAExC,IAAA,MAAM,WAAA,GAAc,YAAY,oBAAoB,CAAA;AACpD,IAAA,OAAO,EAAE,MAAM,WAAA,EAAY;AAAA,EAC7B,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,IAAA;AAAA,EACT;AACF;AAEA,SAAS,qBAAA,CACP,WAAA,EACA,IAAA,EACA,SAAA,EACyB;AACzB,EAAA,MAAM,oBAAoB,WAAA,CAAY,QAAA,CAASA,sBAAK,IAAA,CAAK,SAAA,EAAW,SAAS,CAAA,EAAG;AAAA,IAC9E,QAAA,EAAU,IAAA;AAAA,IACV,KAAA,EAAO,MAAA;AAAA,IACP,KAAA,EAAO,MAAA;AAAA,IACP,QAAA,EAAU,IAAA;AAAA,IACV,MAAA,EAAQ,IAAA;AAAA,IACR,WAAA,EAAa,CAAC,SAAS;AAAA,GACxB,CAAA;AACD,EAAA,OAAO,IAAA,CAAK,sBAAsB,iBAAiB,CAAA;AACrD;AAEA,SAAS,cAAA,CAAe,MAAc,GAAA,EAAyD;AAC7F,EAAA,MAAM,QAAA,GAAW,IAAI,IAAA,CAAK,QAAA,EAAS;AACnC,EAAA,IAAI,GAAA,EAAK;AACP,IAAA,KAAA,MAAW,CAAC,GAAA,EAAK,KAAK,KAAK,MAAA,CAAO,OAAA,CAAQ,GAAG,CAAA,EAAG;AAC9C,MAAA,QAAA,CAAS,GAAA,CAAI,KAAK,KAAK,CAAA;AAAA,IACzB;AAAA,EACF;AACA,EAAA,OAAO,QAAA;AACT;AAMO,SAAS,iBAAA,CACd,cAAA,GAAyB,wBAAA,EACzB,SAAA,EACoB;AACpB,EAAA,MAAM,UAAU,kBAAA,EAAmB;AACnC,EAAA,IAAI,CAAC,OAAA,EAAS;AACZ,IAAA,OAAO,IAAA;AAAA,EACT;AAEA,EAAA,MAAM,EAAE,IAAA,EAAM,WAAA,EAAY,GAAI,OAAA;AAC9B,EAAA,MAAM,MAAA,GAAS,WAAW,cAAc,CAAA;AACxC,EAAA,MAAM,WAAA,GAAc,IAAA,CAAK,WAAA,CAAY,SAAA,EAAU;AAC/C,EAAA,MAAM,EAAA,GAAK,iBAAiB,SAAS,CAAA;AACrC,EAAA,MAAM,WAAA,GAA+B,EAAE,EAAA,EAAI,EAAA,EAAG;AAE9C,EAAA,MAAM,QAAA,GAAW,qBAAA,CAAsB,WAAA,EAAa,IAAA,EAAM,aAAa,CAAA;AACvE,EAAA,MAAM,UAAA,GAAa,qBAAA,CAAsB,WAAA,EAAa,IAAA,EAAM,eAAe,CAAA;AAE3E,EAAA,MAAM,YAAA,GAAgB,SAAuE,KAAA,CAC1F,KAAA;AAYH,EAAA,MAAM,cAAA,GACJ,WACA,OAAA,CAAQ,OAAA;AAYV,EAAA,MAAM,SAAA,GAAY,IAAI,YAAA,CAAa,MAAA,EAAQ,WAAW,CAAA;AACtD,EAAA,MAAM,WAAA,GAAc,IAAI,cAAA,CAAe,MAAA,EAAQ,WAAW,CAAA;AAE1D,EAAA,OAAO;AAAA,IACL,KAAA,EAAO;AAAA,MACL,SAAA,CAAU,SAAS,QAAA,EAAU;AAC3B,QAAA,OAAO,IAAI,OAAA,CAAQ,CAAC,OAAA,EAAS,MAAA,KAAW;AACtC,UAAA,SAAA,CAAU,SAAA;AAAA,YACR,OAAA;AAAA,YACA,eAAe,IAAA,EAAM,EAAE,GAAG,WAAA,EAAa,GAAG,UAAU,CAAA;AAAA,YACpD,CAAC,KAAK,GAAA,KAAQ;AACZ,cAAA,IAAI,GAAA,SAAY,GAAG,CAAA;AAAA,mBACd,OAAA,CAAQ,GAAA,IAAO,EAAE,CAAA;AAAA,YACxB;AAAA,WACF;AAAA,QACF,CAAC,CAAA;AAAA,MACH,CAAA;AAAA,MACA,KAAA,GAAQ;AACN,QAAA,SAAA,CAAU,KAAA,EAAM;AAAA,MAClB;AAAA,KACF;AAAA,IACA,OAAA,EAAS;AAAA,MACP,WAAA,CAAY,SAAS,QAAA,EAAU;AAC7B,QAAA,OAAO,IAAI,OAAA,CAAQ,CAAC,OAAA,EAAS,MAAA,KAAW;AACtC,UAAA,WAAA,CAAY,WAAA;AAAA,YACV,OAAA;AAAA,YACA,eAAe,IAAA,EAAM,EAAE,GAAG,WAAA,EAAa,GAAG,UAAU,CAAA;AAAA,YACpD,CAAC,KAAK,GAAA,KAAQ;AACZ,cAAA,IAAI,GAAA,SAAY,GAAG,CAAA;AAAA,mBACd,OAAA,CAAQ,GAAA,IAAO,EAAE,CAAA;AAAA,YACxB;AAAA,WACF;AAAA,QACF,CAAC,CAAA;AAAA,MACH,CAAA;AAAA,MACA,KAAA,GAAQ;AACN,QAAA,WAAA,CAAY,KAAA,EAAM;AAAA,MACpB;AAAA;AACF,GACF;AACF;AAEO,SAAS,eAAA,GAA2B;AACzC,EAAA,OAAO,oBAAmB,KAAM,IAAA;AAClC;AAEO,SAAS,YAAA,GAAuB;AACrC,EAAA,OAAO,SAAA;AACT","file":"grpc.cjs","sourcesContent":["export const SDK_ID = 'react-router'\nexport const SDK_VERSION = '1.2.0'\n\nexport const SDK_HEADER_ID = 'X-Toggly-Sdk'\nexport const SDK_HEADER_VERSION = 'X-Toggly-Sdk-Version'\n\nexport function sdkUserAgent(): string {\n return `toggly-${SDK_ID}/${SDK_VERSION}`\n}\n\nexport function sdkCustomHeaders(): Record<string, string> {\n return {\n [SDK_HEADER_ID]: SDK_ID,\n [SDK_HEADER_VERSION]: SDK_VERSION,\n }\n}\n\nexport function appendSdkQueryParams(params: URLSearchParams): void {\n params.set('sdk', SDK_ID)\n params.set('sdkVersion', SDK_VERSION)\n}\n\ntype BrowserLikeGlobal = typeof globalThis & {\n window?: unknown\n document?: unknown\n navigator?: { product?: string }\n}\n\n/** Browser and React Native use custom headers on HTTP (User-Agent is forbidden in browser fetch). */\nexport function usesSdkCustomHeaders(): boolean {\n const g = globalThis as BrowserLikeGlobal\n if (g.window !== undefined && g.document !== undefined) {\n return true\n }\n if (g.navigator?.product === 'ReactNative') {\n return true\n }\n return false\n}\n\nexport function buildDefinitionFetchHeaders(\n existing: Record<string, string> = {},\n): Record<string, string> {\n const headers = { ...existing }\n if (usesSdkCustomHeaders()) {\n Object.assign(headers, sdkCustomHeaders())\n } else {\n headers['User-Agent'] = sdkUserAgent()\n }\n return headers\n}\n","import { sdkUserAgent } from '../sdk-identity.js'\nimport { protobufTimestampToIso } from './hash.js'\nimport type { FeatureStatPayload } from './usage-batcher.js'\nimport type { MetricStatPayload } from './metrics-batcher.js'\n\nexport const DEFAULT_METRICS_BASE_URL = 'https://metrics.toggly.io/'\nexport const DEFAULT_TELEMETRY_FLUSH_MS = 60_000\n\nexport interface HttpsTelemetryClientOptions {\n metricsBaseUrl?: string\n userAgent?: string\n fetchImpl?: typeof fetch\n}\n\nfunction ensureTrailingSlash(baseUrl: string): string {\n return baseUrl.endsWith('/') ? baseUrl : `${baseUrl}/`\n}\n\n/** Convert protobuf-timestamp usage payload to gateway HTTPS JSON (PHP parity). */\nexport function usagePayloadToHttpJson(payload: FeatureStatPayload): Record<string, unknown> {\n return {\n ...payload,\n time: protobufTimestampToIso(payload.time),\n ...(payload.processStartTime\n ? { processStartTime: protobufTimestampToIso(payload.processStartTime) }\n : {}),\n }\n}\n\n/** Convert protobuf-timestamp metrics payload to gateway HTTPS JSON (PHP parity). */\nexport function metricsPayloadToHttpJson(payload: MetricStatPayload): Record<string, unknown> {\n return {\n ...payload,\n time: protobufTimestampToIso(payload.time),\n observations: (payload.observations ?? []).map((obs) => ({\n ...obs,\n time: protobufTimestampToIso(obs.time),\n })),\n }\n}\n\n/**\n * Soft-fail HTTPS JSON client for gateway-accepted usage/metrics paths.\n * Network and non-2xx errors never throw to callers.\n */\nexport class HttpsTelemetryClient {\n private readonly baseUrl: string\n private readonly userAgent: string\n private readonly fetchImpl: typeof fetch\n\n constructor(options: HttpsTelemetryClientOptions = {}) {\n this.baseUrl = ensureTrailingSlash(options.metricsBaseUrl ?? DEFAULT_METRICS_BASE_URL)\n this.userAgent = options.userAgent ?? sdkUserAgent()\n this.fetchImpl = options.fetchImpl ?? fetch.bind(globalThis)\n }\n\n getUserAgent(): string {\n return this.userAgent\n }\n\n getBaseUrl(): string {\n return this.baseUrl\n }\n\n /**\n * POST JSON. Returns true on 2xx, false on soft-fail (network / non-2xx).\n */\n async post(path: string, body: unknown): Promise<boolean> {\n try {\n const url = new URL(path.replace(/^\\//, ''), this.baseUrl).toString()\n const response = await this.fetchImpl(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Accept: 'application/json',\n 'User-Agent': this.userAgent,\n },\n body: JSON.stringify(body),\n })\n return response.ok\n } catch {\n return false\n }\n }\n\n sendUsageStats(payload: FeatureStatPayload): Promise<boolean> {\n return this.post('api/usage/stats', usagePayloadToHttpJson(payload))\n }\n\n sendMetrics(payload: MetricStatPayload): Promise<boolean> {\n return this.post('api/metrics', metricsPayloadToHttpJson(payload))\n }\n}\n\nexport function resolveMetricsBaseUrl(raw?: string): string {\n const trimmed = (raw ?? '').trim()\n if (!trimmed) {\n return DEFAULT_METRICS_BASE_URL\n }\n return ensureTrailingSlash(trimmed)\n}\n\nexport function isTelemetryEnvDisabled(): boolean {\n try {\n return typeof process !== 'undefined' && process.env?.TOGGLY_DISABLE_TELEMETRY === '1'\n } catch {\n return false\n }\n}\n\n/**\n * Resolve an enable flag. `TOGGLY_DISABLE_TELEMETRY=1` is authoritative and\n * wins over any explicit `true` in config.\n */\nexport function resolveTelemetryEnableFlag(\n explicit: boolean | undefined,\n defaultWhenUnset: boolean,\n): boolean {\n if (isTelemetryEnvDisabled()) {\n return false\n }\n return explicit ?? defaultWhenUnset\n}\n","import { createRequire } from 'node:module'\nimport fs from 'node:fs'\nimport path from 'node:path'\nimport { fileURLToPath } from 'node:url'\nimport { sdkUserAgent } from '../sdk-identity.js'\nimport { DEFAULT_METRICS_BASE_URL } from './https-client.js'\n\ndeclare global {\n var __TOGGLY_MODULE_URL__: string\n}\n\n// Rollup replaces this test seam with the native module URL for each output:\n// `import.meta.url` in ESM and `pathToFileURL(__filename).href` in CommonJS.\n// This lets a packed SDK find its proto files without asking the consuming app\n// to supply a process-global filename.\nconst thisModuleUrl = globalThis.__TOGGLY_MODULE_URL__\nconst nodeRequire = createRequire(thisModuleUrl)\n\n/**\n * Resolve vendored proto/ for source (src/telemetry), bundled dist/ ESM+CJS,\n * and the published package layout (files includes \"proto\").\n */\nexport function resolveProtoRoot(moduleUrl: string = thisModuleUrl): string {\n const moduleDir = path.dirname(fileURLToPath(moduleUrl))\n const candidates = [\n // Built dist/cjs|esm/telemetry/grpc.js → packageRoot/proto\n path.resolve(moduleDir, '..', '..', '..', 'proto'),\n // Built dist/telemetry/grpc.js → packageRoot/proto\n path.resolve(moduleDir, '..', '..', 'proto'),\n // Built dist/index.js sibling layouts\n path.resolve(moduleDir, '..', 'proto'),\n // Source src/telemetry/*.ts → packageRoot/proto\n path.resolve(moduleDir, '..', '..', 'proto'),\n path.resolve(moduleDir, 'proto'),\n ]\n for (const candidate of candidates) {\n if (fs.existsSync(path.join(candidate, 'usage.proto'))) {\n return candidate\n }\n }\n return candidates[0] ?? path.resolve(moduleDir, '..', '..', 'proto')\n}\n\nconst protoRoot = resolveProtoRoot()\n\nexport function grpcTarget(baseUrl: string): string {\n try {\n const u = new URL(baseUrl.includes('://') ? baseUrl : `https://${baseUrl}`)\n let host = u.host\n if (!host.includes(':')) {\n host = `${host}:443`\n }\n return host\n } catch {\n const trimmed = baseUrl.replace(/^https?:\\/\\//, '').replace(/\\/$/, '')\n return trimmed.includes(':') ? trimmed : `${trimmed}:443`\n }\n}\n\nexport function resolveUserAgent(override?: string): string {\n return override ?? sdkUserAgent()\n}\n\nexport type GrpcMetadataMap = Record<string, string>\n\nexport interface UsageGrpcClient {\n sendStats(\n request: Record<string, unknown>,\n metadata?: GrpcMetadataMap,\n ): Promise<{ featureCount?: number }>\n close(): void\n}\n\nexport interface MetricsGrpcClient {\n sendMetrics(\n request: Record<string, unknown>,\n metadata?: GrpcMetadataMap,\n ): Promise<{ count?: number }>\n close(): void\n}\n\nexport interface GrpcClients {\n usage: UsageGrpcClient\n metrics: MetricsGrpcClient\n}\n\ntype ProtoLoader = typeof import('@grpc/proto-loader')\ntype GrpcJs = typeof import('@grpc/grpc-js')\n\nfunction tryLoadGrpcModules(): { grpc: GrpcJs; protoLoader: ProtoLoader } | null {\n try {\n // Optional peer/optionalDependencies — evaluate flags without gRPC installed.\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const grpc = nodeRequire('@grpc/grpc-js') as GrpcJs\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const protoLoader = nodeRequire('@grpc/proto-loader') as ProtoLoader\n return { grpc, protoLoader }\n } catch {\n return null\n }\n}\n\nfunction loadPackageDefinition(\n protoLoader: ProtoLoader,\n grpc: GrpcJs,\n protoFile: string,\n): Record<string, unknown> {\n const packageDefinition = protoLoader.loadSync(path.join(protoRoot, protoFile), {\n keepCase: true,\n longs: Number,\n enums: String,\n defaults: true,\n oneofs: true,\n includeDirs: [protoRoot],\n })\n return grpc.loadPackageDefinition(packageDefinition) as Record<string, unknown>\n}\n\nfunction createMetadata(grpc: GrpcJs, map?: GrpcMetadataMap): InstanceType<GrpcJs['Metadata']> {\n const metadata = new grpc.Metadata()\n if (map) {\n for (const [key, value] of Object.entries(map)) {\n metadata.set(key, value)\n }\n }\n return metadata\n}\n\n/**\n * Dial usage + metrics gRPC clients against metricsBaseUrl.\n * Returns null when @grpc/grpc-js (+ proto-loader) are not installed.\n */\nexport function createGrpcClients(\n metricsBaseUrl: string = DEFAULT_METRICS_BASE_URL,\n userAgent?: string,\n): GrpcClients | null {\n const modules = tryLoadGrpcModules()\n if (!modules) {\n return null\n }\n\n const { grpc, protoLoader } = modules\n const target = grpcTarget(metricsBaseUrl)\n const credentials = grpc.credentials.createSsl()\n const ua = resolveUserAgent(userAgent)\n const defaultMeta: GrpcMetadataMap = { UA: ua }\n\n const usageDef = loadPackageDefinition(protoLoader, grpc, 'usage.proto')\n const metricsDef = loadPackageDefinition(protoLoader, grpc, 'metrics.proto')\n\n const UsageService = (usageDef as { Usage: { Usage: new (...args: unknown[]) => unknown } }).Usage\n .Usage as new (\n address: string,\n creds: unknown,\n ) => {\n SendStats: (\n req: unknown,\n meta: unknown,\n cb: (err: Error | null, res: { featureCount?: number }) => void,\n ) => void\n close: () => void\n }\n\n const MetricsService = (\n metricsDef as { Metrics: { Metrics: new (...args: unknown[]) => unknown } }\n ).Metrics.Metrics as new (\n address: string,\n creds: unknown,\n ) => {\n SendMetrics: (\n req: unknown,\n meta: unknown,\n cb: (err: Error | null, res: { count?: number }) => void,\n ) => void\n close: () => void\n }\n\n const usageStub = new UsageService(target, credentials)\n const metricsStub = new MetricsService(target, credentials)\n\n return {\n usage: {\n sendStats(request, metadata) {\n return new Promise((resolve, reject) => {\n usageStub.SendStats(\n request,\n createMetadata(grpc, { ...defaultMeta, ...metadata }),\n (err, res) => {\n if (err) reject(err)\n else resolve(res ?? {})\n },\n )\n })\n },\n close() {\n usageStub.close()\n },\n },\n metrics: {\n sendMetrics(request, metadata) {\n return new Promise((resolve, reject) => {\n metricsStub.SendMetrics(\n request,\n createMetadata(grpc, { ...defaultMeta, ...metadata }),\n (err, res) => {\n if (err) reject(err)\n else resolve(res ?? {})\n },\n )\n })\n },\n close() {\n metricsStub.close()\n },\n },\n }\n}\n\nexport function isGrpcAvailable(): boolean {\n return tryLoadGrpcModules() !== null\n}\n\nexport function getProtoRoot(): string {\n return protoRoot\n}\n\nexport { DEFAULT_METRICS_BASE_URL }\n"]}
@@ -7,7 +7,7 @@ import { fileURLToPath } from 'url';
7
7
 
8
8
  // src/core/sdk-identity.ts
9
9
  var SDK_ID = "react-router";
10
- var SDK_VERSION = "1.0.1";
10
+ var SDK_VERSION = "1.2.0";
11
11
  function sdkUserAgent() {
12
12
  return `toggly-${SDK_ID}/${SDK_VERSION}`;
13
13
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/core/sdk-identity.ts","../../src/core/telemetry/https-client.ts","../../src/core/telemetry/grpc-clients.ts"],"names":[],"mappings":"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","file":"grpc.js","sourcesContent":["export const SDK_ID = 'react-router'\nexport const SDK_VERSION = '1.0.1'\n\nexport const SDK_HEADER_ID = 'X-Toggly-Sdk'\nexport const SDK_HEADER_VERSION = 'X-Toggly-Sdk-Version'\n\nexport function sdkUserAgent(): string {\n return `toggly-${SDK_ID}/${SDK_VERSION}`\n}\n\nexport function sdkCustomHeaders(): Record<string, string> {\n return {\n [SDK_HEADER_ID]: SDK_ID,\n [SDK_HEADER_VERSION]: SDK_VERSION,\n }\n}\n\nexport function appendSdkQueryParams(params: URLSearchParams): void {\n params.set('sdk', SDK_ID)\n params.set('sdkVersion', SDK_VERSION)\n}\n\ntype BrowserLikeGlobal = typeof globalThis & {\n window?: unknown\n document?: unknown\n navigator?: { product?: string }\n}\n\n/** Browser and React Native use custom headers on HTTP (User-Agent is forbidden in browser fetch). */\nexport function usesSdkCustomHeaders(): boolean {\n const g = globalThis as BrowserLikeGlobal\n if (g.window !== undefined && g.document !== undefined) {\n return true\n }\n if (g.navigator?.product === 'ReactNative') {\n return true\n }\n return false\n}\n\nexport function buildDefinitionFetchHeaders(\n existing: Record<string, string> = {},\n): Record<string, string> {\n const headers = { ...existing }\n if (usesSdkCustomHeaders()) {\n Object.assign(headers, sdkCustomHeaders())\n } else {\n headers['User-Agent'] = sdkUserAgent()\n }\n return headers\n}\n","import { sdkUserAgent } from '../sdk-identity.js'\nimport { protobufTimestampToIso } from './hash.js'\nimport type { FeatureStatPayload } from './usage-batcher.js'\nimport type { MetricStatPayload } from './metrics-batcher.js'\n\nexport const DEFAULT_METRICS_BASE_URL = 'https://metrics.toggly.io/'\nexport const DEFAULT_TELEMETRY_FLUSH_MS = 60_000\n\nexport interface HttpsTelemetryClientOptions {\n metricsBaseUrl?: string\n userAgent?: string\n fetchImpl?: typeof fetch\n}\n\nfunction ensureTrailingSlash(baseUrl: string): string {\n return baseUrl.endsWith('/') ? baseUrl : `${baseUrl}/`\n}\n\n/** Convert protobuf-timestamp usage payload to gateway HTTPS JSON (PHP parity). */\nexport function usagePayloadToHttpJson(payload: FeatureStatPayload): Record<string, unknown> {\n return {\n ...payload,\n time: protobufTimestampToIso(payload.time),\n ...(payload.processStartTime\n ? { processStartTime: protobufTimestampToIso(payload.processStartTime) }\n : {}),\n }\n}\n\n/** Convert protobuf-timestamp metrics payload to gateway HTTPS JSON (PHP parity). */\nexport function metricsPayloadToHttpJson(payload: MetricStatPayload): Record<string, unknown> {\n return {\n ...payload,\n time: protobufTimestampToIso(payload.time),\n observations: (payload.observations ?? []).map((obs) => ({\n ...obs,\n time: protobufTimestampToIso(obs.time),\n })),\n }\n}\n\n/**\n * Soft-fail HTTPS JSON client for gateway-accepted usage/metrics paths.\n * Network and non-2xx errors never throw to callers.\n */\nexport class HttpsTelemetryClient {\n private readonly baseUrl: string\n private readonly userAgent: string\n private readonly fetchImpl: typeof fetch\n\n constructor(options: HttpsTelemetryClientOptions = {}) {\n this.baseUrl = ensureTrailingSlash(options.metricsBaseUrl ?? DEFAULT_METRICS_BASE_URL)\n this.userAgent = options.userAgent ?? sdkUserAgent()\n this.fetchImpl = options.fetchImpl ?? fetch.bind(globalThis)\n }\n\n getUserAgent(): string {\n return this.userAgent\n }\n\n getBaseUrl(): string {\n return this.baseUrl\n }\n\n /**\n * POST JSON. Returns true on 2xx, false on soft-fail (network / non-2xx).\n */\n async post(path: string, body: unknown): Promise<boolean> {\n try {\n const url = new URL(path.replace(/^\\//, ''), this.baseUrl).toString()\n const response = await this.fetchImpl(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Accept: 'application/json',\n 'User-Agent': this.userAgent,\n },\n body: JSON.stringify(body),\n })\n return response.ok\n } catch {\n return false\n }\n }\n\n sendUsageStats(payload: FeatureStatPayload): Promise<boolean> {\n return this.post('api/usage/stats', usagePayloadToHttpJson(payload))\n }\n\n sendMetrics(payload: MetricStatPayload): Promise<boolean> {\n return this.post('api/metrics', metricsPayloadToHttpJson(payload))\n }\n}\n\nexport function resolveMetricsBaseUrl(raw?: string): string {\n const trimmed = (raw ?? '').trim()\n if (!trimmed) {\n return DEFAULT_METRICS_BASE_URL\n }\n return ensureTrailingSlash(trimmed)\n}\n\nexport function isTelemetryEnvDisabled(): boolean {\n try {\n return typeof process !== 'undefined' && process.env?.TOGGLY_DISABLE_TELEMETRY === '1'\n } catch {\n return false\n }\n}\n\n/**\n * Resolve an enable flag. `TOGGLY_DISABLE_TELEMETRY=1` is authoritative and\n * wins over any explicit `true` in config.\n */\nexport function resolveTelemetryEnableFlag(\n explicit: boolean | undefined,\n defaultWhenUnset: boolean,\n): boolean {\n if (isTelemetryEnvDisabled()) {\n return false\n }\n return explicit ?? defaultWhenUnset\n}\n","import { createRequire } from 'node:module'\nimport fs from 'node:fs'\nimport path from 'node:path'\nimport { fileURLToPath } from 'node:url'\nimport { sdkUserAgent } from '../sdk-identity.js'\nimport { DEFAULT_METRICS_BASE_URL } from './https-client.js'\n\ndeclare global {\n var __TOGGLY_MODULE_URL__: string\n}\n\n// Rollup replaces this test seam with the native module URL for each output:\n// `import.meta.url` in ESM and `pathToFileURL(__filename).href` in CommonJS.\n// This lets a packed SDK find its proto files without asking the consuming app\n// to supply a process-global filename.\nconst thisModuleUrl = globalThis.__TOGGLY_MODULE_URL__\nconst nodeRequire = createRequire(thisModuleUrl)\n\n/**\n * Resolve vendored proto/ for source (src/telemetry), bundled dist/ ESM+CJS,\n * and the published package layout (files includes \"proto\").\n */\nexport function resolveProtoRoot(moduleUrl: string = thisModuleUrl): string {\n const moduleDir = path.dirname(fileURLToPath(moduleUrl))\n const candidates = [\n // Built dist/cjs|esm/telemetry/grpc.js → packageRoot/proto\n path.resolve(moduleDir, '..', '..', '..', 'proto'),\n // Built dist/telemetry/grpc.js → packageRoot/proto\n path.resolve(moduleDir, '..', '..', 'proto'),\n // Built dist/index.js sibling layouts\n path.resolve(moduleDir, '..', 'proto'),\n // Source src/telemetry/*.ts → packageRoot/proto\n path.resolve(moduleDir, '..', '..', 'proto'),\n path.resolve(moduleDir, 'proto'),\n ]\n for (const candidate of candidates) {\n if (fs.existsSync(path.join(candidate, 'usage.proto'))) {\n return candidate\n }\n }\n return candidates[0] ?? path.resolve(moduleDir, '..', '..', 'proto')\n}\n\nconst protoRoot = resolveProtoRoot()\n\nexport function grpcTarget(baseUrl: string): string {\n try {\n const u = new URL(baseUrl.includes('://') ? baseUrl : `https://${baseUrl}`)\n let host = u.host\n if (!host.includes(':')) {\n host = `${host}:443`\n }\n return host\n } catch {\n const trimmed = baseUrl.replace(/^https?:\\/\\//, '').replace(/\\/$/, '')\n return trimmed.includes(':') ? trimmed : `${trimmed}:443`\n }\n}\n\nexport function resolveUserAgent(override?: string): string {\n return override ?? sdkUserAgent()\n}\n\nexport type GrpcMetadataMap = Record<string, string>\n\nexport interface UsageGrpcClient {\n sendStats(\n request: Record<string, unknown>,\n metadata?: GrpcMetadataMap,\n ): Promise<{ featureCount?: number }>\n close(): void\n}\n\nexport interface MetricsGrpcClient {\n sendMetrics(\n request: Record<string, unknown>,\n metadata?: GrpcMetadataMap,\n ): Promise<{ count?: number }>\n close(): void\n}\n\nexport interface GrpcClients {\n usage: UsageGrpcClient\n metrics: MetricsGrpcClient\n}\n\ntype ProtoLoader = typeof import('@grpc/proto-loader')\ntype GrpcJs = typeof import('@grpc/grpc-js')\n\nfunction tryLoadGrpcModules(): { grpc: GrpcJs; protoLoader: ProtoLoader } | null {\n try {\n // Optional peer/optionalDependencies — evaluate flags without gRPC installed.\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const grpc = nodeRequire('@grpc/grpc-js') as GrpcJs\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const protoLoader = nodeRequire('@grpc/proto-loader') as ProtoLoader\n return { grpc, protoLoader }\n } catch {\n return null\n }\n}\n\nfunction loadPackageDefinition(\n protoLoader: ProtoLoader,\n grpc: GrpcJs,\n protoFile: string,\n): Record<string, unknown> {\n const packageDefinition = protoLoader.loadSync(path.join(protoRoot, protoFile), {\n keepCase: true,\n longs: Number,\n enums: String,\n defaults: true,\n oneofs: true,\n includeDirs: [protoRoot],\n })\n return grpc.loadPackageDefinition(packageDefinition) as Record<string, unknown>\n}\n\nfunction createMetadata(grpc: GrpcJs, map?: GrpcMetadataMap): InstanceType<GrpcJs['Metadata']> {\n const metadata = new grpc.Metadata()\n if (map) {\n for (const [key, value] of Object.entries(map)) {\n metadata.set(key, value)\n }\n }\n return metadata\n}\n\n/**\n * Dial usage + metrics gRPC clients against metricsBaseUrl.\n * Returns null when @grpc/grpc-js (+ proto-loader) are not installed.\n */\nexport function createGrpcClients(\n metricsBaseUrl: string = DEFAULT_METRICS_BASE_URL,\n userAgent?: string,\n): GrpcClients | null {\n const modules = tryLoadGrpcModules()\n if (!modules) {\n return null\n }\n\n const { grpc, protoLoader } = modules\n const target = grpcTarget(metricsBaseUrl)\n const credentials = grpc.credentials.createSsl()\n const ua = resolveUserAgent(userAgent)\n const defaultMeta: GrpcMetadataMap = { UA: ua }\n\n const usageDef = loadPackageDefinition(protoLoader, grpc, 'usage.proto')\n const metricsDef = loadPackageDefinition(protoLoader, grpc, 'metrics.proto')\n\n const UsageService = (usageDef as { Usage: { Usage: new (...args: unknown[]) => unknown } }).Usage\n .Usage as new (\n address: string,\n creds: unknown,\n ) => {\n SendStats: (\n req: unknown,\n meta: unknown,\n cb: (err: Error | null, res: { featureCount?: number }) => void,\n ) => void\n close: () => void\n }\n\n const MetricsService = (\n metricsDef as { Metrics: { Metrics: new (...args: unknown[]) => unknown } }\n ).Metrics.Metrics as new (\n address: string,\n creds: unknown,\n ) => {\n SendMetrics: (\n req: unknown,\n meta: unknown,\n cb: (err: Error | null, res: { count?: number }) => void,\n ) => void\n close: () => void\n }\n\n const usageStub = new UsageService(target, credentials)\n const metricsStub = new MetricsService(target, credentials)\n\n return {\n usage: {\n sendStats(request, metadata) {\n return new Promise((resolve, reject) => {\n usageStub.SendStats(\n request,\n createMetadata(grpc, { ...defaultMeta, ...metadata }),\n (err, res) => {\n if (err) reject(err)\n else resolve(res ?? {})\n },\n )\n })\n },\n close() {\n usageStub.close()\n },\n },\n metrics: {\n sendMetrics(request, metadata) {\n return new Promise((resolve, reject) => {\n metricsStub.SendMetrics(\n request,\n createMetadata(grpc, { ...defaultMeta, ...metadata }),\n (err, res) => {\n if (err) reject(err)\n else resolve(res ?? {})\n },\n )\n })\n },\n close() {\n metricsStub.close()\n },\n },\n }\n}\n\nexport function isGrpcAvailable(): boolean {\n return tryLoadGrpcModules() !== null\n}\n\nexport function getProtoRoot(): string {\n return protoRoot\n}\n\nexport { DEFAULT_METRICS_BASE_URL }\n"]}
1
+ {"version":3,"sources":["../../src/core/sdk-identity.ts","../../src/core/telemetry/https-client.ts","../../src/core/telemetry/grpc-clients.ts"],"names":[],"mappings":"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","file":"grpc.js","sourcesContent":["export const SDK_ID = 'react-router'\nexport const SDK_VERSION = '1.2.0'\n\nexport const SDK_HEADER_ID = 'X-Toggly-Sdk'\nexport const SDK_HEADER_VERSION = 'X-Toggly-Sdk-Version'\n\nexport function sdkUserAgent(): string {\n return `toggly-${SDK_ID}/${SDK_VERSION}`\n}\n\nexport function sdkCustomHeaders(): Record<string, string> {\n return {\n [SDK_HEADER_ID]: SDK_ID,\n [SDK_HEADER_VERSION]: SDK_VERSION,\n }\n}\n\nexport function appendSdkQueryParams(params: URLSearchParams): void {\n params.set('sdk', SDK_ID)\n params.set('sdkVersion', SDK_VERSION)\n}\n\ntype BrowserLikeGlobal = typeof globalThis & {\n window?: unknown\n document?: unknown\n navigator?: { product?: string }\n}\n\n/** Browser and React Native use custom headers on HTTP (User-Agent is forbidden in browser fetch). */\nexport function usesSdkCustomHeaders(): boolean {\n const g = globalThis as BrowserLikeGlobal\n if (g.window !== undefined && g.document !== undefined) {\n return true\n }\n if (g.navigator?.product === 'ReactNative') {\n return true\n }\n return false\n}\n\nexport function buildDefinitionFetchHeaders(\n existing: Record<string, string> = {},\n): Record<string, string> {\n const headers = { ...existing }\n if (usesSdkCustomHeaders()) {\n Object.assign(headers, sdkCustomHeaders())\n } else {\n headers['User-Agent'] = sdkUserAgent()\n }\n return headers\n}\n","import { sdkUserAgent } from '../sdk-identity.js'\nimport { protobufTimestampToIso } from './hash.js'\nimport type { FeatureStatPayload } from './usage-batcher.js'\nimport type { MetricStatPayload } from './metrics-batcher.js'\n\nexport const DEFAULT_METRICS_BASE_URL = 'https://metrics.toggly.io/'\nexport const DEFAULT_TELEMETRY_FLUSH_MS = 60_000\n\nexport interface HttpsTelemetryClientOptions {\n metricsBaseUrl?: string\n userAgent?: string\n fetchImpl?: typeof fetch\n}\n\nfunction ensureTrailingSlash(baseUrl: string): string {\n return baseUrl.endsWith('/') ? baseUrl : `${baseUrl}/`\n}\n\n/** Convert protobuf-timestamp usage payload to gateway HTTPS JSON (PHP parity). */\nexport function usagePayloadToHttpJson(payload: FeatureStatPayload): Record<string, unknown> {\n return {\n ...payload,\n time: protobufTimestampToIso(payload.time),\n ...(payload.processStartTime\n ? { processStartTime: protobufTimestampToIso(payload.processStartTime) }\n : {}),\n }\n}\n\n/** Convert protobuf-timestamp metrics payload to gateway HTTPS JSON (PHP parity). */\nexport function metricsPayloadToHttpJson(payload: MetricStatPayload): Record<string, unknown> {\n return {\n ...payload,\n time: protobufTimestampToIso(payload.time),\n observations: (payload.observations ?? []).map((obs) => ({\n ...obs,\n time: protobufTimestampToIso(obs.time),\n })),\n }\n}\n\n/**\n * Soft-fail HTTPS JSON client for gateway-accepted usage/metrics paths.\n * Network and non-2xx errors never throw to callers.\n */\nexport class HttpsTelemetryClient {\n private readonly baseUrl: string\n private readonly userAgent: string\n private readonly fetchImpl: typeof fetch\n\n constructor(options: HttpsTelemetryClientOptions = {}) {\n this.baseUrl = ensureTrailingSlash(options.metricsBaseUrl ?? DEFAULT_METRICS_BASE_URL)\n this.userAgent = options.userAgent ?? sdkUserAgent()\n this.fetchImpl = options.fetchImpl ?? fetch.bind(globalThis)\n }\n\n getUserAgent(): string {\n return this.userAgent\n }\n\n getBaseUrl(): string {\n return this.baseUrl\n }\n\n /**\n * POST JSON. Returns true on 2xx, false on soft-fail (network / non-2xx).\n */\n async post(path: string, body: unknown): Promise<boolean> {\n try {\n const url = new URL(path.replace(/^\\//, ''), this.baseUrl).toString()\n const response = await this.fetchImpl(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Accept: 'application/json',\n 'User-Agent': this.userAgent,\n },\n body: JSON.stringify(body),\n })\n return response.ok\n } catch {\n return false\n }\n }\n\n sendUsageStats(payload: FeatureStatPayload): Promise<boolean> {\n return this.post('api/usage/stats', usagePayloadToHttpJson(payload))\n }\n\n sendMetrics(payload: MetricStatPayload): Promise<boolean> {\n return this.post('api/metrics', metricsPayloadToHttpJson(payload))\n }\n}\n\nexport function resolveMetricsBaseUrl(raw?: string): string {\n const trimmed = (raw ?? '').trim()\n if (!trimmed) {\n return DEFAULT_METRICS_BASE_URL\n }\n return ensureTrailingSlash(trimmed)\n}\n\nexport function isTelemetryEnvDisabled(): boolean {\n try {\n return typeof process !== 'undefined' && process.env?.TOGGLY_DISABLE_TELEMETRY === '1'\n } catch {\n return false\n }\n}\n\n/**\n * Resolve an enable flag. `TOGGLY_DISABLE_TELEMETRY=1` is authoritative and\n * wins over any explicit `true` in config.\n */\nexport function resolveTelemetryEnableFlag(\n explicit: boolean | undefined,\n defaultWhenUnset: boolean,\n): boolean {\n if (isTelemetryEnvDisabled()) {\n return false\n }\n return explicit ?? defaultWhenUnset\n}\n","import { createRequire } from 'node:module'\nimport fs from 'node:fs'\nimport path from 'node:path'\nimport { fileURLToPath } from 'node:url'\nimport { sdkUserAgent } from '../sdk-identity.js'\nimport { DEFAULT_METRICS_BASE_URL } from './https-client.js'\n\ndeclare global {\n var __TOGGLY_MODULE_URL__: string\n}\n\n// Rollup replaces this test seam with the native module URL for each output:\n// `import.meta.url` in ESM and `pathToFileURL(__filename).href` in CommonJS.\n// This lets a packed SDK find its proto files without asking the consuming app\n// to supply a process-global filename.\nconst thisModuleUrl = globalThis.__TOGGLY_MODULE_URL__\nconst nodeRequire = createRequire(thisModuleUrl)\n\n/**\n * Resolve vendored proto/ for source (src/telemetry), bundled dist/ ESM+CJS,\n * and the published package layout (files includes \"proto\").\n */\nexport function resolveProtoRoot(moduleUrl: string = thisModuleUrl): string {\n const moduleDir = path.dirname(fileURLToPath(moduleUrl))\n const candidates = [\n // Built dist/cjs|esm/telemetry/grpc.js → packageRoot/proto\n path.resolve(moduleDir, '..', '..', '..', 'proto'),\n // Built dist/telemetry/grpc.js → packageRoot/proto\n path.resolve(moduleDir, '..', '..', 'proto'),\n // Built dist/index.js sibling layouts\n path.resolve(moduleDir, '..', 'proto'),\n // Source src/telemetry/*.ts → packageRoot/proto\n path.resolve(moduleDir, '..', '..', 'proto'),\n path.resolve(moduleDir, 'proto'),\n ]\n for (const candidate of candidates) {\n if (fs.existsSync(path.join(candidate, 'usage.proto'))) {\n return candidate\n }\n }\n return candidates[0] ?? path.resolve(moduleDir, '..', '..', 'proto')\n}\n\nconst protoRoot = resolveProtoRoot()\n\nexport function grpcTarget(baseUrl: string): string {\n try {\n const u = new URL(baseUrl.includes('://') ? baseUrl : `https://${baseUrl}`)\n let host = u.host\n if (!host.includes(':')) {\n host = `${host}:443`\n }\n return host\n } catch {\n const trimmed = baseUrl.replace(/^https?:\\/\\//, '').replace(/\\/$/, '')\n return trimmed.includes(':') ? trimmed : `${trimmed}:443`\n }\n}\n\nexport function resolveUserAgent(override?: string): string {\n return override ?? sdkUserAgent()\n}\n\nexport type GrpcMetadataMap = Record<string, string>\n\nexport interface UsageGrpcClient {\n sendStats(\n request: Record<string, unknown>,\n metadata?: GrpcMetadataMap,\n ): Promise<{ featureCount?: number }>\n close(): void\n}\n\nexport interface MetricsGrpcClient {\n sendMetrics(\n request: Record<string, unknown>,\n metadata?: GrpcMetadataMap,\n ): Promise<{ count?: number }>\n close(): void\n}\n\nexport interface GrpcClients {\n usage: UsageGrpcClient\n metrics: MetricsGrpcClient\n}\n\ntype ProtoLoader = typeof import('@grpc/proto-loader')\ntype GrpcJs = typeof import('@grpc/grpc-js')\n\nfunction tryLoadGrpcModules(): { grpc: GrpcJs; protoLoader: ProtoLoader } | null {\n try {\n // Optional peer/optionalDependencies — evaluate flags without gRPC installed.\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const grpc = nodeRequire('@grpc/grpc-js') as GrpcJs\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const protoLoader = nodeRequire('@grpc/proto-loader') as ProtoLoader\n return { grpc, protoLoader }\n } catch {\n return null\n }\n}\n\nfunction loadPackageDefinition(\n protoLoader: ProtoLoader,\n grpc: GrpcJs,\n protoFile: string,\n): Record<string, unknown> {\n const packageDefinition = protoLoader.loadSync(path.join(protoRoot, protoFile), {\n keepCase: true,\n longs: Number,\n enums: String,\n defaults: true,\n oneofs: true,\n includeDirs: [protoRoot],\n })\n return grpc.loadPackageDefinition(packageDefinition) as Record<string, unknown>\n}\n\nfunction createMetadata(grpc: GrpcJs, map?: GrpcMetadataMap): InstanceType<GrpcJs['Metadata']> {\n const metadata = new grpc.Metadata()\n if (map) {\n for (const [key, value] of Object.entries(map)) {\n metadata.set(key, value)\n }\n }\n return metadata\n}\n\n/**\n * Dial usage + metrics gRPC clients against metricsBaseUrl.\n * Returns null when @grpc/grpc-js (+ proto-loader) are not installed.\n */\nexport function createGrpcClients(\n metricsBaseUrl: string = DEFAULT_METRICS_BASE_URL,\n userAgent?: string,\n): GrpcClients | null {\n const modules = tryLoadGrpcModules()\n if (!modules) {\n return null\n }\n\n const { grpc, protoLoader } = modules\n const target = grpcTarget(metricsBaseUrl)\n const credentials = grpc.credentials.createSsl()\n const ua = resolveUserAgent(userAgent)\n const defaultMeta: GrpcMetadataMap = { UA: ua }\n\n const usageDef = loadPackageDefinition(protoLoader, grpc, 'usage.proto')\n const metricsDef = loadPackageDefinition(protoLoader, grpc, 'metrics.proto')\n\n const UsageService = (usageDef as { Usage: { Usage: new (...args: unknown[]) => unknown } }).Usage\n .Usage as new (\n address: string,\n creds: unknown,\n ) => {\n SendStats: (\n req: unknown,\n meta: unknown,\n cb: (err: Error | null, res: { featureCount?: number }) => void,\n ) => void\n close: () => void\n }\n\n const MetricsService = (\n metricsDef as { Metrics: { Metrics: new (...args: unknown[]) => unknown } }\n ).Metrics.Metrics as new (\n address: string,\n creds: unknown,\n ) => {\n SendMetrics: (\n req: unknown,\n meta: unknown,\n cb: (err: Error | null, res: { count?: number }) => void,\n ) => void\n close: () => void\n }\n\n const usageStub = new UsageService(target, credentials)\n const metricsStub = new MetricsService(target, credentials)\n\n return {\n usage: {\n sendStats(request, metadata) {\n return new Promise((resolve, reject) => {\n usageStub.SendStats(\n request,\n createMetadata(grpc, { ...defaultMeta, ...metadata }),\n (err, res) => {\n if (err) reject(err)\n else resolve(res ?? {})\n },\n )\n })\n },\n close() {\n usageStub.close()\n },\n },\n metrics: {\n sendMetrics(request, metadata) {\n return new Promise((resolve, reject) => {\n metricsStub.SendMetrics(\n request,\n createMetadata(grpc, { ...defaultMeta, ...metadata }),\n (err, res) => {\n if (err) reject(err)\n else resolve(res ?? {})\n },\n )\n })\n },\n close() {\n metricsStub.close()\n },\n },\n }\n}\n\nexport function isGrpcAvailable(): boolean {\n return tryLoadGrpcModules() !== null\n}\n\nexport function getProtoRoot(): string {\n return protoRoot\n}\n\nexport { DEFAULT_METRICS_BASE_URL }\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ops-ai/react-router-toggly",
3
- "version": "1.0.1",
3
+ "version": "1.2.0",
4
4
  "description": "Toggly feature flags for React Router 7/8 framework mode (client + server)",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -39,7 +39,8 @@
39
39
  "@ops-ai/toggly-hooks-types": "^1.4.3",
40
40
  "@ops-ai/toggly-local-gates": "^1.0.1",
41
41
  "@ops-ai/toggly-signed-defs": "^1.2.3",
42
- "ws": "^8.0.0"
42
+ "ws": "^8.0.0",
43
+ "@ops-ai/toggly-client-telemetry": "^1.1.0"
43
44
  },
44
45
  "optionalDependencies": {
45
46
  "@grpc/grpc-js": "^1.12.6",