@kanca-app/ikas 0.2.0 → 0.3.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.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/graphql.ts","../src/shared.ts","../src/transport.ts","../src/index.ts"],"sourcesContent":["// GraphQL helpers: operation-name extraction and ikas Admin API URL detection.\n\ntype Tok = { t: \"name\" | \"punct\"; v: string };\n\n/** Minimal GraphQL lexer: names and punctuation only; strings/comments/numbers skipped. */\nfunction lex(src: string): Tok[] {\n const out: Tok[] = [];\n let i = 0;\n const n = src.length;\n while (i < n) {\n const c = src[i]!;\n if (c === \"#\") {\n while (i < n && src[i] !== \"\\n\" && src[i] !== \"\\r\") i++;\n continue;\n }\n if (c === '\"') {\n if (src.startsWith('\"\"\"', i)) {\n const end = src.indexOf('\"\"\"', i + 3);\n i = end === -1 ? n : end + 3;\n } else {\n i++;\n while (i < n && src[i] !== '\"' && src[i] !== \"\\n\") i += src[i] === \"\\\\\" ? 2 : 1;\n i++;\n }\n continue;\n }\n if (/[A-Za-z_]/.test(c)) {\n let j = i + 1;\n while (j < n && /[A-Za-z0-9_]/.test(src[j]!)) j++;\n out.push({ t: \"name\", v: src.slice(i, j) });\n i = j;\n continue;\n }\n if (src.startsWith(\"...\", i)) {\n out.push({ t: \"punct\", v: \"...\" });\n i += 3;\n continue;\n }\n if (\"{}()[]:@$!=|&\".includes(c)) out.push({ t: \"punct\", v: c });\n i++;\n }\n return out;\n}\n\n/** Index just past the balanced block that opens at `start` (a \"{\" or \"(\" token). */\nfunction skipBlock(toks: Tok[], start: number): number {\n const open = toks[start]!.v;\n const close = open === \"{\" ? \"}\" : \")\";\n let depth = 0;\n for (let i = start; i < toks.length; i++) {\n const v = toks[i]!;\n if (v.t !== \"punct\") continue;\n if (v.v === open) depth++;\n else if (v.v === close && --depth === 0) return i + 1;\n }\n return toks.length;\n}\n\n/** First root field name inside the selection set that opens at `start`. */\nfunction firstRootField(toks: Tok[], start: number): string | undefined {\n for (let i = start + 1; i < toks.length; i++) {\n const tk = toks[i]!;\n if (tk.t === \"punct\") {\n if (tk.v === \"}\") return undefined;\n if (tk.v === \"...\") return undefined; // fragment spread at root: no field name to report\n continue;\n }\n const next = toks[i + 1];\n if (next && next.t === \"punct\" && next.v === \":\") {\n const real = toks[i + 2];\n return real && real.t === \"name\" ? real.v : undefined; // alias: report the real field\n }\n return tk.v;\n }\n return undefined;\n}\n\n/**\n * Operation name per PROTOCOL.md: the operation name; if anonymous, the first\n * root field; else \"anonymous\". Uses `operationName` from the request body to\n * pick among multiple operations when given.\n */\nexport function operationName(query: unknown, requestedName?: unknown): string {\n if (typeof query !== \"string\" || !query.trim()) return \"anonymous\";\n let toks: Tok[];\n try {\n toks = lex(query);\n } catch {\n return \"anonymous\";\n }\n const ops: { name?: string; field?: string }[] = [];\n let i = 0;\n while (i < toks.length) {\n const tk = toks[i]!;\n if (tk.t === \"punct\" && tk.v === \"{\") {\n ops.push({ field: firstRootField(toks, i) });\n i = skipBlock(toks, i);\n continue;\n }\n if (tk.t === \"name\" && (tk.v === \"query\" || tk.v === \"mutation\" || tk.v === \"subscription\")) {\n let j = i + 1;\n let name: string | undefined;\n if (toks[j]?.t === \"name\") name = toks[j++]!.v;\n // skip variable definitions and directives until the selection set\n while (j < toks.length && !(toks[j]!.t === \"punct\" && toks[j]!.v === \"{\")) {\n if (toks[j]!.t === \"punct\" && toks[j]!.v === \"(\") j = skipBlock(toks, j);\n else j++;\n }\n if (j >= toks.length) {\n ops.push({ name });\n break;\n }\n ops.push({ name, field: firstRootField(toks, j) });\n i = skipBlock(toks, j);\n continue;\n }\n if (tk.t === \"name\" && tk.v === \"fragment\") {\n let j = i + 1;\n while (j < toks.length && !(toks[j]!.t === \"punct\" && toks[j]!.v === \"{\")) j++;\n i = j < toks.length ? skipBlock(toks, j) : j;\n continue;\n }\n i++;\n }\n if (!ops.length) return \"anonymous\";\n let op = ops[0]!;\n if (typeof requestedName === \"string\" && requestedName) {\n op = ops.find((o) => o.name === requestedName) ?? op;\n }\n return op.name ?? op.field ?? \"anonymous\";\n}\n\n/** ikas Admin GraphQL: api.myikas.com/api/v1/admin/graphql, or any *.myikas.com URL whose path contains \"graphql\". */\nexport function isIkasGraphqlUrl(url: URL): boolean {\n const host = url.hostname.toLowerCase();\n if (host !== \"myikas.com\" && !host.endsWith(\".myikas.com\")) return false;\n return url.pathname.toLowerCase().includes(\"graphql\");\n}\n\n/** Extracts the operation name from a GraphQL-over-HTTP JSON body (single or batched). */\nexport function operationNameFromBody(body: unknown): string {\n if (typeof body !== \"string\") return \"anonymous\";\n try {\n let parsed: unknown = JSON.parse(body);\n if (Array.isArray(parsed)) parsed = parsed[0];\n if (!parsed || typeof parsed !== \"object\") return \"anonymous\";\n const p = parsed as { query?: unknown; operationName?: unknown };\n return operationName(p.query, p.operationName);\n } catch {\n return \"anonymous\";\n }\n}\n\nexport interface GraphqlOutcome {\n ok: boolean;\n code?: string;\n message?: string;\n}\n\n/** Classifies a GraphQL response: ok = 2xx AND no `errors`. */\nexport function classifyGraphql(status: number, json: unknown): GraphqlOutcome {\n const payload = Array.isArray(json) ? json.find((j) => j && typeof j === \"object\" && Array.isArray(j.errors) && j.errors.length) ?? json[0] : json;\n const errors =\n payload && typeof payload === \"object\" && Array.isArray((payload as { errors?: unknown }).errors)\n ? ((payload as { errors: unknown[] }).errors as unknown[])\n : [];\n const is2xx = status >= 200 && status < 300;\n if (is2xx && errors.length === 0) return { ok: true };\n const first = errors[0] as { message?: unknown; extensions?: { code?: unknown } } | undefined;\n const extCode = first?.extensions?.code;\n const code = typeof extCode === \"string\" && extCode ? extCode : `HTTP_${status}`;\n const message = typeof first?.message === \"string\" ? first.message : undefined;\n return { ok: false, code, message };\n}\n","// Shared, runtime-agnostic helpers (used by both the Node and browser entries).\n// Wire format: see docs/PROTOCOL.md (v1).\n\nexport const SDK_NAME = \"@kanca-app/ikas\";\nexport const SDK_VERSION = \"0.2.0\";\nexport const DEFAULT_ENDPOINT = \"https://kanca.onurhan.dev\";\n\nexport const MAX_NAME = 200;\nexport const MAX_ERROR_MESSAGE = 500;\nexport const MAX_ERROR_CODE = 100;\nexport const MAX_ATTR_KEYS = 20;\nexport const MAX_ATTR_STRING = 500;\nexport const MAX_ATTR_KEY = 64;\nexport const MAX_EVENTS_PER_BATCH = 500;\n/** Protocol limit is 512 KB; keep headroom for the envelope. */\nexport const MAX_BATCH_BYTES = 480 * 1024;\n\nexport type EventType = \"webhook\" | \"graphql\" | \"rest\" | \"install\" | \"uninstall\" | \"vital\" | \"storefront\";\n\nexport type AttrValue = string | number | boolean;\n\nexport interface KancaError {\n code: string;\n message?: string;\n}\n\n/** A single wire event (PROTOCOL.md → Event). */\nexport interface KancaEvent {\n id: string;\n type: EventType;\n ts: number;\n name: string;\n status?: number;\n ok?: boolean;\n durationMs?: number;\n value?: number;\n error?: KancaError;\n merchantId?: string;\n deliveryId?: string;\n attrs?: Record<string, AttrValue>;\n}\n\nexport interface Batch {\n sdk: { name: string; version: string };\n events: KancaEvent[];\n}\n\nexport function truncate(s: string, max: number): string {\n return s.length > max ? s.slice(0, max) : s;\n}\n\nfunction hex(bytes: Uint8Array): string {\n let out = \"\";\n for (const b of bytes) out += b.toString(16).padStart(2, \"0\");\n return out;\n}\n\n/** UUID v4 via crypto.randomUUID, with a getRandomValues fallback (non-secure browser contexts). */\nexport function uuid(): string {\n const c = (globalThis as { crypto?: Crypto }).crypto;\n if (c && typeof c.randomUUID === \"function\") return c.randomUUID();\n const b = new Uint8Array(16);\n if (c && typeof c.getRandomValues === \"function\") c.getRandomValues(b);\n else for (let i = 0; i < 16; i++) b[i] = Math.floor(Math.random() * 256);\n b[6] = (b[6]! & 0x0f) | 0x40;\n b[8] = (b[8]! & 0x3f) | 0x80;\n const h = hex(b);\n return `${h.slice(0, 8)}-${h.slice(8, 12)}-${h.slice(12, 16)}-${h.slice(16, 20)}-${h.slice(20)}`;\n}\n\nfunction isFiniteNumber(v: unknown): v is number {\n return typeof v === \"number\" && Number.isFinite(v);\n}\n\nfunction cleanAttrs(attrs: unknown): Record<string, AttrValue> | undefined {\n if (!attrs || typeof attrs !== \"object\") return undefined;\n const out: Record<string, AttrValue> = {};\n let n = 0;\n for (const [k, v] of Object.entries(attrs as Record<string, unknown>)) {\n if (n >= MAX_ATTR_KEYS) break;\n if (v === undefined || v === null || !k) continue;\n let val: AttrValue;\n if (typeof v === \"boolean\") val = v;\n else if (typeof v === \"number\") {\n if (!Number.isFinite(v)) continue;\n val = v;\n } else if (typeof v === \"string\") val = truncate(v, MAX_ATTR_STRING);\n else continue; // attrs are flat: nested values are dropped, never stringified\n out[truncate(k, MAX_ATTR_KEY)] = val;\n n++;\n }\n return n > 0 ? out : undefined;\n}\n\n/**\n * Normalises an event so it always satisfies the protocol limits. Unknown\n * fields are dropped — only the whitelisted metadata below ever leaves the process.\n */\nexport function sanitizeEvent(e: KancaEvent): KancaEvent {\n const out: KancaEvent = {\n id: typeof e.id === \"string\" && e.id ? e.id : uuid(),\n type: e.type,\n ts: isFiniteNumber(e.ts) ? Math.round(e.ts) : Date.now(),\n name: truncate(typeof e.name === \"string\" && e.name ? e.name : \"unknown\", MAX_NAME),\n };\n if (isFiniteNumber(e.status) && e.status >= 100 && e.status <= 599) out.status = Math.trunc(e.status);\n if (typeof e.ok === \"boolean\") out.ok = e.ok;\n if (isFiniteNumber(e.durationMs)) out.durationMs = Math.min(86_400_000, Math.max(0, Math.round(e.durationMs * 10) / 10));\n if (isFiniteNumber(e.value)) out.value = e.value;\n if (e.error && typeof e.error.code === \"string\") {\n out.error = { code: truncate(e.error.code, MAX_ERROR_CODE) };\n if (typeof e.error.message === \"string\" && e.error.message) {\n out.error.message = truncate(e.error.message, MAX_ERROR_MESSAGE);\n }\n }\n if (typeof e.merchantId === \"string\" && e.merchantId) out.merchantId = truncate(e.merchantId, 200);\n if (typeof e.deliveryId === \"string\" && e.deliveryId) out.deliveryId = truncate(e.deliveryId, 200);\n const attrs = cleanAttrs(e.attrs);\n if (attrs) out.attrs = attrs;\n return out;\n}\n\nexport function errorMessage(err: unknown): string | undefined {\n try {\n if (err instanceof Error) return truncate(err.message || err.name, MAX_ERROR_MESSAGE);\n if (typeof err === \"string\") return truncate(err, MAX_ERROR_MESSAGE);\n } catch {\n /* ignore */\n }\n return undefined;\n}\n\nexport function joinUrl(endpoint: string, path: string): string {\n return endpoint.replace(/\\/+$/, \"\") + path;\n}\n\nconst encoder = typeof TextEncoder !== \"undefined\" ? /* @__PURE__ */ new TextEncoder() : undefined;\nexport function byteLength(s: string): number {\n return encoder ? encoder.encode(s).length : s.length * 3;\n}\n\n/**\n * Splits events into protocol-sized batches (<= maxEvents events and\n * <= MAX_BATCH_BYTES serialized). Returns serialized bodies.\n */\nexport function buildBodies(events: KancaEvent[], maxEvents: number): { body: string; events: KancaEvent[] }[] {\n const sdk = { name: SDK_NAME, version: SDK_VERSION };\n const envelope = byteLength(JSON.stringify({ sdk, events: [] }));\n const out: { body: string; events: KancaEvent[] }[] = [];\n let cur: KancaEvent[] = [];\n let size = envelope;\n const push = () => {\n if (cur.length) out.push({ body: JSON.stringify({ sdk, events: cur } satisfies Batch), events: cur });\n cur = [];\n size = envelope;\n };\n for (const ev of events) {\n const len = byteLength(JSON.stringify(ev)) + 1;\n if (cur.length && (cur.length >= maxEvents || size + len > MAX_BATCH_BYTES)) push();\n cur.push(ev);\n size += len;\n }\n push();\n return out;\n}\n","// Server-side transport: bounded in-memory queue, batched POST /api/ingest,\n// retry 429/5xx/network with exponential backoff (max 3 attempts). Never throws.\n\nimport { buildBodies, joinUrl, type KancaEvent } from \"./shared\";\n\nexport interface TransportOptions {\n key: string;\n endpoint: string;\n flushIntervalMs: number;\n maxBatchSize: number;\n maxQueueSize: number;\n maxAttempts: number;\n retryBaseMs: number;\n log: (msg: string, ...args: unknown[]) => void;\n warn: (msg: string) => void;\n /** Resolved at send time so tests / polyfills can replace globalThis.fetch. */\n getFetch: () => typeof fetch | undefined;\n}\n\ntype Timer = ReturnType<typeof setTimeout>;\n\nfunction unref(t: Timer): Timer {\n (t as { unref?: () => void }).unref?.();\n return t;\n}\n\nconst sleep = (ms: number) => new Promise<void>((r) => unref(setTimeout(r, ms)));\n\nexport class Transport {\n private queue: KancaEvent[] = [];\n private timer: Timer | undefined;\n private immediate: Timer | undefined;\n private chain: Promise<void> = Promise.resolve();\n private warned401 = false;\n dropped = 0;\n sent = 0;\n\n constructor(private readonly o: TransportOptions) {}\n\n get size(): number {\n return this.queue.length;\n }\n\n enqueue(ev: KancaEvent): void {\n this.queue.push(ev);\n if (this.queue.length > this.o.maxQueueSize) {\n const over = this.queue.length - this.o.maxQueueSize;\n this.queue.splice(0, over); // drop oldest\n this.dropped += over;\n this.o.log(`queue full, dropped ${over} oldest event(s) (total dropped: ${this.dropped})`);\n }\n if (this.queue.length >= this.o.maxBatchSize) this.scheduleSoon();\n else this.arm();\n }\n\n /** Arms the interval flush if not armed. */\n private arm(): void {\n if (this.timer) return;\n this.timer = unref(\n setTimeout(() => {\n this.timer = undefined;\n void this.flush();\n }, this.o.flushIntervalMs),\n );\n }\n\n /** Coalesced flush on the next macrotask (does not block the caller). */\n scheduleSoon(): void {\n if (this.immediate) return;\n this.immediate = unref(\n setTimeout(() => {\n this.immediate = undefined;\n void this.flush();\n }, 0),\n );\n }\n\n /**\n * Sends everything queued at call time. Calls are serialized, so awaiting\n * flush() always covers events enqueued before it. Always resolves.\n */\n flush(): Promise<void> {\n this.chain = this.chain.then(() => this.drain()).catch((e) => this.o.log(\"flush failed\", e));\n return this.chain;\n }\n\n private async drain(): Promise<void> {\n if (this.timer) {\n clearTimeout(this.timer);\n this.timer = undefined;\n }\n if (!this.queue.length) return;\n const events = this.queue.splice(0, this.queue.length);\n await this.sendAll(events);\n }\n\n private async sendAll(events: KancaEvent[]): Promise<void> {\n for (const { body, events: evs } of buildBodies(events, this.o.maxBatchSize)) {\n const ok = await this.send(body);\n if (ok) this.sent += evs.length;\n else {\n this.dropped += evs.length;\n this.o.log(`dropped batch of ${evs.length} event(s) (total dropped: ${this.dropped})`);\n }\n }\n }\n\n private async send(body: string): Promise<boolean> {\n const f = this.o.getFetch();\n if (!f) {\n this.o.log(\"no fetch available; cannot send\");\n return false;\n }\n const url = joinUrl(this.o.endpoint, \"/api/ingest\");\n for (let attempt = 1; attempt <= this.o.maxAttempts; attempt++) {\n let retryAfterMs: number | undefined;\n try {\n const res = await f(url, {\n method: \"POST\",\n headers: { authorization: `Bearer ${this.o.key}`, \"content-type\": \"application/json\" },\n body,\n });\n // drain so the connection can be reused\n res.text().catch(() => undefined);\n if (res.status >= 200 && res.status < 300) {\n this.o.log(`sent batch (${res.status})`);\n return true;\n }\n if (res.status === 401 && !this.warned401) {\n this.warned401 = true;\n this.o.warn(\"[kanca] ingest rejected the key (401). Check KANCA_KEY — events are being dropped.\");\n }\n if (res.status !== 429 && res.status < 500) {\n this.o.log(`ingest responded ${res.status}; not retrying`);\n return false;\n }\n const ra = Number(res.headers.get(\"retry-after\"));\n if (res.status === 429 && Number.isFinite(ra) && ra >= 0) retryAfterMs = Math.min(ra * 1000, 30_000);\n this.o.log(`ingest responded ${res.status} (attempt ${attempt}/${this.o.maxAttempts})`);\n } catch (err) {\n this.o.log(`network error (attempt ${attempt}/${this.o.maxAttempts})`, err);\n }\n if (attempt < this.o.maxAttempts) {\n const backoff = this.o.retryBaseMs * 2 ** (attempt - 1);\n const jitter = backoff * 0.2 * Math.random();\n await sleep(retryAfterMs ?? backoff + jitter);\n }\n }\n return false;\n }\n}\n","// @kanca-app/ikas — server entry (Node 18+, Next.js route handlers, any runtime with fetch/Request).\n// Contract: docs/PROTOCOL.md (v1). Metadata only — never payloads, variables, headers or tokens.\n\nimport { classifyGraphql, isIkasGraphqlUrl, operationName, operationNameFromBody } from \"./graphql\";\nimport {\n DEFAULT_ENDPOINT,\n MAX_EVENTS_PER_BATCH,\n errorMessage,\n sanitizeEvent,\n uuid,\n type AttrValue,\n type EventType,\n type KancaError,\n type KancaEvent,\n} from \"./shared\";\nimport { Transport } from \"./transport\";\n\nexport { SDK_NAME, SDK_VERSION } from \"./shared\";\nexport type { AttrValue, EventType, KancaError, KancaEvent } from \"./shared\";\nexport { operationName } from \"./graphql\";\n\nexport interface KancaOptions {\n /** Server key (`kk_...`). If missing, the SDK is a silent no-op. */\n key?: string | undefined;\n /** Kanca base URL. Default `https://kanca.onurhan.dev`. */\n endpoint?: string;\n /** Log SDK activity to the console. */\n debug?: boolean;\n /** Turn the SDK into a no-op (e.g. in tests). */\n disabled?: boolean;\n /** Flush interval in ms. Default 2000. */\n flushIntervalMs?: number;\n /** Events per request. Default 100, max 500 (protocol limit). */\n maxBatchSize?: number;\n /** Max events buffered in memory; oldest are dropped beyond this. Default 5000. */\n maxQueueSize?: number;\n /**\n * Optional hook to keep serverless functions alive until telemetry is sent,\n * e.g. `waitUntil` from `@vercel/functions`. Called with a flush promise\n * whenever events are recorded. Never delays the response.\n */\n waitUntil?: (promise: Promise<unknown>) => void;\n /** @internal Base backoff delay in ms (default 500). */\n retryBaseMs?: number;\n}\n\nexport interface TrackFields {\n name?: string;\n ts?: number;\n status?: number;\n ok?: boolean;\n durationMs?: number;\n value?: number;\n error?: KancaError;\n merchantId?: string;\n deliveryId?: string;\n /** install: shop name, stored as `attrs.storeName`. */\n storeName?: string;\n attrs?: Record<string, AttrValue>;\n}\n\nexport interface InstallFields {\n merchantId: string;\n storeName?: string;\n attrs?: Record<string, AttrValue>;\n}\n\nexport interface InstrumentOptions {\n /** merchantId to attach to every call. Defaults to `client.tokenData.merchantId` when present. */\n merchantId?: string;\n}\n\nexport type WebhookHandler<R extends Request, A extends unknown[]> = (req: R, ...rest: A) => Response | Promise<Response>;\n\nexport interface Kanca {\n /** Wraps a webhook route handler (Next.js `POST` compatible). Records one `webhook` event per delivery. */\n webhook<R extends Request, A extends unknown[]>(handler: WebhookHandler<R, A>): (req: R, ...rest: A) => Promise<Response>;\n /** Drop-in `fetch` that records ikas Admin GraphQL calls (`graphql`) and any other call (`rest`). */\n fetch: typeof globalThis.fetch;\n /** Instruments an `@ikas/admin-api-client` client (BaseGraphQLAPIClient subclass) in place. Returns it. */\n instrument<T>(client: T, options?: InstrumentOptions): T;\n /** Record an install (call in the OAuth callback after a successful token exchange). */\n track(type: \"install\", fields: InstallFields): void;\n /** Record any event manually. */\n track(type: Exclude<EventType, \"install\">, fields?: TrackFields): void;\n /** Send everything buffered. Never rejects. `await` it at the end of serverless invocations. */\n flush(): Promise<void>;\n /** True when a valid key is configured and the SDK is not disabled. */\n readonly enabled: boolean;\n}\n\n// ---- flush on natural process exit (Node). One listener for all instances; an\n// instance is registered only while it has unsent events, so nothing leaks.\nconst exitFlushers = new Set<() => Promise<void>>();\nlet exitHookInstalled = false;\nfunction registerExitFlush(fn: () => Promise<void>): void {\n exitFlushers.add(fn);\n if (exitHookInstalled) return;\n try {\n const proc = (globalThis as { process?: { on?: (ev: string, fn: () => void) => void } }).process;\n if (proc && typeof proc.on === \"function\") {\n proc.on(\"beforeExit\", () => {\n // Scheduling work here keeps the process alive until the flush settles;\n // beforeExit fires again afterwards and finds the set empty.\n for (const f of [...exitFlushers]) void f();\n });\n exitHookInstalled = true;\n }\n } catch {\n /* not Node (edge runtime, browser) */\n }\n}\n\n/**\n * Webhook scopes that would mean \"this store removed the app\". ikas does not\n * document an app-uninstall scope (its documented scopes are store/order/*,\n * store/product/*, store/customer/* and store/stock/*), so these may never\n * arrive; if one does, it is recorded as an uninstall (deduped server-side by\n * deliveryId). Kanca separately infers \"access revoked\" from Admin API\n * authorization failures, which needs no scope (see docs/PROTOCOL.md).\n */\nexport const UNINSTALL_SCOPES: readonly string[] = [\n \"store/app/deleted\",\n \"store/app/uninstalled\",\n \"store/authorizedApp/deleted\",\n];\nconst MARK = Symbol.for(\"kanca.instrumented\");\n\nconst now = (): number =>\n typeof performance !== \"undefined\" && typeof performance.now === \"function\" ? performance.now() : Date.now();\n\nfunction clampInt(v: unknown, def: number, min: number, max: number): number {\n const n = typeof v === \"number\" && Number.isFinite(v) ? Math.floor(v) : def;\n return Math.min(max, Math.max(min, n));\n}\n\nfunction isRequest(v: unknown): v is Request {\n return typeof Request !== \"undefined\" && v instanceof Request;\n}\n\nfunction toUrl(input: unknown): URL | undefined {\n try {\n if (typeof input === \"string\") return new URL(input);\n if (input instanceof URL) return input;\n if (input && typeof (input as { url?: unknown }).url === \"string\") return new URL((input as { url: string }).url);\n } catch {\n /* relative or invalid URL */\n }\n return undefined;\n}\n\nfunction bodyToString(body: unknown): string | undefined {\n if (typeof body === \"string\") return body;\n try {\n if (body instanceof Uint8Array) return new TextDecoder().decode(body);\n if (body instanceof ArrayBuffer) return new TextDecoder().decode(new Uint8Array(body));\n } catch {\n /* ignore */\n }\n return undefined;\n}\n\ninterface WebhookMeta {\n scope?: string;\n id?: string;\n merchantId?: string;\n}\n\nfunction parseWebhookMeta(text: string): WebhookMeta | null {\n try {\n const j: unknown = JSON.parse(text);\n if (!j || typeof j !== \"object\" || Array.isArray(j)) return null;\n const o = j as Record<string, unknown>;\n // Only these three fields are read. Nothing else from the body is kept.\n return {\n scope: typeof o.scope === \"string\" && o.scope ? o.scope : undefined,\n id: typeof o.id === \"string\" && o.id ? o.id : undefined,\n merchantId: typeof o.merchantId === \"string\" && o.merchantId ? o.merchantId : undefined,\n };\n } catch {\n return null;\n }\n}\n\nfunction noopKanca(): Kanca {\n return {\n webhook: (handler) => async (req, ...rest) => handler(req, ...rest),\n fetch: ((input: RequestInfo | URL, init?: RequestInit) => globalThis.fetch(input, init)) as typeof fetch,\n instrument: (client) => client,\n track: () => undefined,\n flush: () => Promise.resolve(),\n enabled: false,\n };\n}\n\nexport function createKanca(options: KancaOptions = {}): Kanca {\n const debug = !!options.debug;\n const log = (msg: string, ...args: unknown[]) => {\n if (debug) console.debug(`[kanca] ${msg}`, ...args);\n };\n const warn = (msg: string) => console.warn(msg);\n\n if (options.disabled) {\n log(\"disabled; SDK is a no-op\");\n return noopKanca();\n }\n const key = typeof options.key === \"string\" ? options.key.trim() : \"\";\n if (!key) {\n log(\"no key (KANCA_KEY) configured; SDK is a no-op\");\n return noopKanca();\n }\n if (!key.startsWith(\"kk_\")) {\n warn(\n key.startsWith(\"kp_\")\n ? \"[kanca] createKanca() needs a server key (kk_...), got a public key (kp_...). Public keys are for reportVitals()/<KancaVitals/> only. SDK disabled.\"\n : \"[kanca] KANCA_KEY must start with kk_. SDK disabled.\",\n );\n return noopKanca();\n }\n\n const maxBatchSize = clampInt(options.maxBatchSize, 100, 1, MAX_EVENTS_PER_BATCH);\n const transport = new Transport({\n key,\n endpoint: options.endpoint || DEFAULT_ENDPOINT,\n flushIntervalMs: clampInt(options.flushIntervalMs, 2000, 0, 60 * 60 * 1000),\n maxBatchSize,\n maxQueueSize: clampInt(options.maxQueueSize, 5000, maxBatchSize, 1_000_000),\n maxAttempts: 3,\n retryBaseMs: clampInt(options.retryBaseMs, 500, 0, 60_000),\n log,\n warn,\n getFetch: () => (typeof globalThis.fetch === \"function\" ? globalThis.fetch.bind(globalThis) : undefined),\n });\n\n // Observations still being finalised (e.g. waiting for a cloned body). flush() awaits them.\n const pending = new Set<Promise<unknown>>();\n const observe = (p: Promise<unknown>) => {\n const tracked = p.catch((e) => log(\"observation failed\", e)).finally(() => pending.delete(tracked));\n pending.add(tracked);\n };\n\n // With waitUntil, every event (not only webhooks) keeps the invocation alive until sent;\n // otherwise a serverless function can be frozen before the interval flush fires.\n let keepAlive = false;\n const holdUntilSent = () => {\n if (!options.waitUntil || keepAlive) return;\n keepAlive = true;\n try {\n options.waitUntil(\n new Promise<void>((r) => setTimeout(r, 0))\n .then(flush)\n .finally(() => {\n keepAlive = false;\n }),\n );\n } catch (e) {\n keepAlive = false;\n log(\"waitUntil threw\", e);\n }\n };\n\n const emit = (ev: Omit<KancaEvent, \"id\"> & { id?: string }) => {\n try {\n const clean = sanitizeEvent({ ...ev, id: ev.id ?? uuid() } as KancaEvent);\n transport.enqueue(clean);\n registerExitFlush(flush);\n holdUntilSent();\n log(`${clean.type} ${clean.name}`, { status: clean.status, ok: clean.ok, durationMs: clean.durationMs });\n } catch (e) {\n log(\"failed to record event\", e);\n }\n };\n\n const flush = async (): Promise<void> => {\n try {\n while (pending.size) await Promise.allSettled([...pending]);\n await transport.flush();\n } catch (e) {\n log(\"flush error\", e);\n }\n if (!transport.size && !pending.size) exitFlushers.delete(flush);\n };\n\n const afterDelivery = () => {\n transport.scheduleSoon();\n if (options.waitUntil) {\n try {\n options.waitUntil(flush());\n } catch (e) {\n log(\"waitUntil threw\", e);\n }\n }\n };\n\n // ---- webhook ---------------------------------------------------------------------------------\n\n const webhook: Kanca[\"webhook\"] = (handler) =>\n async function kancaWebhook(this: unknown, req, ...rest) {\n const ts = Date.now();\n const t0 = now();\n let meta: Promise<WebhookMeta | null>;\n try {\n // Clone before the handler touches the body; the original stays fully readable.\n meta = req.clone().text().then(parseWebhookMeta, () => null);\n } catch {\n meta = Promise.resolve(null);\n }\n\n const record = (status: number | undefined, error: KancaError | undefined, durationMs: number) => {\n observe(\n meta.then((m) => {\n const base = { ts, merchantId: m?.merchantId, deliveryId: m?.id };\n emit({\n ...base,\n type: \"webhook\",\n name: m?.scope ?? \"unknown\",\n status,\n ok: status === 200,\n durationMs,\n error,\n });\n if (m?.scope && UNINSTALL_SCOPES.includes(m.scope)) emit({ ...base, type: \"uninstall\", name: \"uninstall\" });\n }),\n );\n afterDelivery();\n };\n\n let res: Response;\n try {\n res = await handler.call(this, req, ...rest);\n } catch (err) {\n try {\n record(500, { code: \"HANDLER_THREW\", message: errorMessage(err) }, now() - t0);\n } catch {\n /* never mask the handler's error */\n }\n throw err;\n }\n try {\n const status = res && typeof (res as { status?: unknown }).status === \"number\" ? res.status : undefined;\n record(status, status === undefined ? { code: \"NO_RESPONSE\", message: \"handler did not return a Response\" } : status === 200 ? undefined : { code: `HTTP_${status}` }, now() - t0);\n } catch {\n /* ignore */\n }\n return res;\n };\n\n // ---- fetch -----------------------------------------------------------------------------------\n\n const kfetch = async function kancaFetch(input: RequestInfo | URL, init?: RequestInit): Promise<Response> {\n const base = globalThis.fetch;\n let url: URL | undefined;\n let method = \"GET\";\n let gql = false;\n let opName: Promise<string> | undefined;\n try {\n url = toUrl(input);\n method = (init?.method ?? (isRequest(input) ? input.method : \"GET\")).toUpperCase();\n gql = !!url && isIkasGraphqlUrl(url);\n if (gql) {\n if (init && init.body != null) opName = Promise.resolve(operationNameFromBody(bodyToString(init.body)));\n else if (isRequest(input) && input.body) opName = input.clone().text().then(operationNameFromBody, () => \"anonymous\");\n else opName = Promise.resolve(\"anonymous\");\n }\n } catch {\n url = undefined;\n }\n if (!url) return base(input, init);\n\n const ts = Date.now();\n const t0 = now();\n let res: Response;\n try {\n res = await base(input, init);\n } catch (err) {\n const durationMs = now() - t0;\n try {\n const error = { code: \"NETWORK\", message: errorMessage(err) };\n if (gql) observe((opName ?? Promise.resolve(\"anonymous\")).then((name) => emit({ type: \"graphql\", name, ts, ok: false, durationMs, error })));\n else emit({ type: \"rest\", name: `${method} ${url.host}${url.pathname}`, ts, ok: false, durationMs, error });\n } catch {\n /* ignore */\n }\n throw err;\n }\n const durationMs = now() - t0;\n try {\n const status = res.status;\n if (gql) {\n let clone: Response | undefined;\n try {\n clone = res.clone(); // synchronously, before the caller can consume the body\n } catch {\n clone = undefined;\n }\n observe(\n (async () => {\n const name = await (opName ?? Promise.resolve(\"anonymous\"));\n let json: unknown;\n try {\n json = clone ? JSON.parse(await clone.text()) : undefined;\n } catch {\n json = undefined;\n }\n const out = classifyGraphql(status, json);\n emit({\n type: \"graphql\",\n name,\n ts,\n status,\n ok: out.ok,\n durationMs,\n error: out.ok ? undefined : { code: out.code ?? `HTTP_${status}`, message: out.message },\n });\n })(),\n );\n } else {\n const ok = status < 400;\n emit({ type: \"rest\", name: `${method} ${url.host}${url.pathname}`, ts, status, ok, durationMs, error: ok ? undefined : { code: `HTTP_${status}` } });\n }\n } catch {\n /* ignore */\n }\n return res;\n } as typeof fetch;\n\n // ---- instrument (@ikas/admin-api-client) -----------------------------------------------------\n\n let warnedInstrument = false;\n const instrument: Kanca[\"instrument\"] = (client, opts) => {\n const c = client as unknown as {\n [MARK]?: boolean;\n _client?: { request?: unknown };\n createGraphQLClient?: (...a: unknown[]) => unknown;\n tokenData?: { merchantId?: unknown };\n };\n try {\n if (!c || typeof c !== \"object\" || typeof c.createGraphQLClient !== \"function\" || !c._client || typeof c._client.request !== \"function\") {\n if (!warnedInstrument) {\n warnedInstrument = true;\n warn(\n \"[kanca] instrument(): not an @ikas/admin-api-client client (expected createGraphQLClient() and _client.request()). Nothing will be recorded for it — use kanca.fetch instead.\",\n );\n }\n return client;\n }\n if (c[MARK]) return client;\n\n const merchantIdFor = (): string | undefined => {\n if (opts?.merchantId) return opts.merchantId;\n const m = c.tokenData?.merchantId;\n return typeof m === \"string\" && m ? m : undefined;\n };\n\n const wrap = (gc: unknown): unknown => {\n const g = gc as { [MARK]?: boolean; request?: (...a: unknown[]) => Promise<unknown> };\n if (!g || typeof g.request !== \"function\" || g[MARK]) return gc;\n const orig = g.request;\n g.request = async function (this: unknown, ...args: unknown[]) {\n let name = \"anonymous\";\n try {\n const a0 = args[0] as { document?: unknown; operationName?: unknown; loc?: { source?: { body?: unknown } } } | string | undefined;\n const doc = typeof a0 === \"string\" ? a0 : a0 && typeof a0 === \"object\" && \"document\" in a0 ? a0.document : a0;\n const text =\n typeof doc === \"string\" ? doc : (doc as { loc?: { source?: { body?: unknown } } } | undefined)?.loc?.source?.body;\n name = operationName(text, typeof a0 === \"object\" ? a0?.operationName : undefined);\n } catch {\n /* keep anonymous */\n }\n const ts = Date.now();\n const t0 = now();\n try {\n const data = await orig.apply(this, args);\n // Success means the library saw a 2xx without `errors`; the exact status is not exposed, so it is omitted.\n emit({ type: \"graphql\", name, ts, ok: true, durationMs: now() - t0, merchantId: merchantIdFor() });\n return data;\n } catch (err) {\n try {\n const durationMs = now() - t0;\n const resp = (err as { response?: { status?: unknown; errors?: unknown } } | undefined)?.response;\n if (resp && typeof resp.status === \"number\") {\n const out = classifyGraphql(resp.status, { errors: Array.isArray(resp.errors) ? resp.errors : [] });\n // A thrown ClientError is always a failure, even for a 2xx without parsable errors.\n emit({\n type: \"graphql\",\n name,\n ts,\n status: resp.status,\n ok: false,\n durationMs,\n merchantId: merchantIdFor(),\n error: { code: out.code ?? `HTTP_${resp.status}`, message: out.message },\n });\n } else {\n emit({ type: \"graphql\", name, ts, ok: false, durationMs, merchantId: merchantIdFor(), error: { code: \"NETWORK\", message: errorMessage(err) } });\n }\n } catch {\n /* ignore */\n }\n throw err;\n }\n };\n g[MARK] = true;\n return gc;\n };\n\n const origCreate = c.createGraphQLClient;\n // The client rebuilds `_client` after a token refresh, so wrap the factory too.\n c.createGraphQLClient = function (this: unknown, ...a: unknown[]) {\n return wrap(origCreate.apply(this, a));\n };\n c._client = wrap(c._client) as typeof c._client;\n c[MARK] = true;\n log(\"instrumented @ikas/admin-api-client\");\n } catch (e) {\n log(\"instrument failed\", e);\n }\n return client;\n };\n\n // ---- track -----------------------------------------------------------------------------------\n\n const track = (type: EventType, fields: TrackFields = {}): void => {\n try {\n const f = fields ?? {};\n let name = f.name;\n if (type === \"install\" || type === \"uninstall\") name = type;\n let ok = f.ok;\n if (ok === undefined && typeof f.status === \"number\") {\n if (type === \"webhook\") ok = f.status === 200;\n else if (type === \"graphql\") ok = f.status >= 200 && f.status < 300;\n else if (type === \"rest\") ok = f.status < 400;\n }\n const attrs = { ...(f.attrs ?? {}) };\n if (typeof f.storeName === \"string\" && f.storeName) attrs.storeName = f.storeName;\n emit({\n type,\n name: name ?? \"unknown\",\n ts: f.ts ?? Date.now(),\n status: f.status,\n ok,\n durationMs: f.durationMs,\n value: f.value,\n error: f.error,\n merchantId: f.merchantId,\n deliveryId: f.deliveryId,\n attrs,\n });\n if (type === \"install\") transport.scheduleSoon();\n } catch (e) {\n log(\"track failed\", e);\n }\n };\n\n log(`enabled → ${options.endpoint || DEFAULT_ENDPOINT}`);\n\n return {\n webhook,\n fetch: kfetch,\n instrument,\n track: track as Kanca[\"track\"],\n flush,\n enabled: true,\n };\n}\n"],"mappings":"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","names":["durationMs"]}
1
+ {"version":3,"sources":["../src/graphql.ts","../src/shared.ts","../src/transport.ts","../src/install.ts","../src/oauth.ts","../src/index.ts"],"sourcesContent":["// GraphQL helpers: operation-name extraction and ikas Admin API URL detection.\n\ntype Tok = { t: \"name\" | \"punct\"; v: string };\n\n/** Minimal GraphQL lexer: names and punctuation only; strings/comments/numbers skipped. */\nfunction lex(src: string): Tok[] {\n const out: Tok[] = [];\n let i = 0;\n const n = src.length;\n while (i < n) {\n const c = src[i]!;\n if (c === \"#\") {\n while (i < n && src[i] !== \"\\n\" && src[i] !== \"\\r\") i++;\n continue;\n }\n if (c === '\"') {\n if (src.startsWith('\"\"\"', i)) {\n const end = src.indexOf('\"\"\"', i + 3);\n i = end === -1 ? n : end + 3;\n } else {\n i++;\n while (i < n && src[i] !== '\"' && src[i] !== \"\\n\") i += src[i] === \"\\\\\" ? 2 : 1;\n i++;\n }\n continue;\n }\n if (/[A-Za-z_]/.test(c)) {\n let j = i + 1;\n while (j < n && /[A-Za-z0-9_]/.test(src[j]!)) j++;\n out.push({ t: \"name\", v: src.slice(i, j) });\n i = j;\n continue;\n }\n if (src.startsWith(\"...\", i)) {\n out.push({ t: \"punct\", v: \"...\" });\n i += 3;\n continue;\n }\n if (\"{}()[]:@$!=|&\".includes(c)) out.push({ t: \"punct\", v: c });\n i++;\n }\n return out;\n}\n\n/** Index just past the balanced block that opens at `start` (a \"{\" or \"(\" token). */\nfunction skipBlock(toks: Tok[], start: number): number {\n const open = toks[start]!.v;\n const close = open === \"{\" ? \"}\" : \")\";\n let depth = 0;\n for (let i = start; i < toks.length; i++) {\n const v = toks[i]!;\n if (v.t !== \"punct\") continue;\n if (v.v === open) depth++;\n else if (v.v === close && --depth === 0) return i + 1;\n }\n return toks.length;\n}\n\n/** First root field name inside the selection set that opens at `start`. */\nfunction firstRootField(toks: Tok[], start: number): string | undefined {\n for (let i = start + 1; i < toks.length; i++) {\n const tk = toks[i]!;\n if (tk.t === \"punct\") {\n if (tk.v === \"}\") return undefined;\n if (tk.v === \"...\") return undefined; // fragment spread at root: no field name to report\n continue;\n }\n const next = toks[i + 1];\n if (next && next.t === \"punct\" && next.v === \":\") {\n const real = toks[i + 2];\n return real && real.t === \"name\" ? real.v : undefined; // alias: report the real field\n }\n return tk.v;\n }\n return undefined;\n}\n\n/**\n * Operation name per PROTOCOL.md: the operation name; if anonymous, the first\n * root field; else \"anonymous\". Uses `operationName` from the request body to\n * pick among multiple operations when given.\n */\nexport function operationName(query: unknown, requestedName?: unknown): string {\n if (typeof query !== \"string\" || !query.trim()) return \"anonymous\";\n let toks: Tok[];\n try {\n toks = lex(query);\n } catch {\n return \"anonymous\";\n }\n const ops: { name?: string; field?: string }[] = [];\n let i = 0;\n while (i < toks.length) {\n const tk = toks[i]!;\n if (tk.t === \"punct\" && tk.v === \"{\") {\n ops.push({ field: firstRootField(toks, i) });\n i = skipBlock(toks, i);\n continue;\n }\n if (tk.t === \"name\" && (tk.v === \"query\" || tk.v === \"mutation\" || tk.v === \"subscription\")) {\n let j = i + 1;\n let name: string | undefined;\n if (toks[j]?.t === \"name\") name = toks[j++]!.v;\n // skip variable definitions and directives until the selection set\n while (j < toks.length && !(toks[j]!.t === \"punct\" && toks[j]!.v === \"{\")) {\n if (toks[j]!.t === \"punct\" && toks[j]!.v === \"(\") j = skipBlock(toks, j);\n else j++;\n }\n if (j >= toks.length) {\n ops.push({ name });\n break;\n }\n ops.push({ name, field: firstRootField(toks, j) });\n i = skipBlock(toks, j);\n continue;\n }\n if (tk.t === \"name\" && tk.v === \"fragment\") {\n let j = i + 1;\n while (j < toks.length && !(toks[j]!.t === \"punct\" && toks[j]!.v === \"{\")) j++;\n i = j < toks.length ? skipBlock(toks, j) : j;\n continue;\n }\n i++;\n }\n if (!ops.length) return \"anonymous\";\n let op = ops[0]!;\n if (typeof requestedName === \"string\" && requestedName) {\n op = ops.find((o) => o.name === requestedName) ?? op;\n }\n return op.name ?? op.field ?? \"anonymous\";\n}\n\n/** ikas Admin GraphQL: api.myikas.com/api/v1/admin/graphql, or any *.myikas.com URL whose path contains \"graphql\". */\nexport function isIkasGraphqlUrl(url: URL): boolean {\n const host = url.hostname.toLowerCase();\n if (host !== \"myikas.com\" && !host.endsWith(\".myikas.com\")) return false;\n return url.pathname.toLowerCase().includes(\"graphql\");\n}\n\n/** Extracts the operation name from a GraphQL-over-HTTP JSON body (single or batched). */\nexport function operationNameFromBody(body: unknown): string {\n if (typeof body !== \"string\") return \"anonymous\";\n try {\n let parsed: unknown = JSON.parse(body);\n if (Array.isArray(parsed)) parsed = parsed[0];\n if (!parsed || typeof parsed !== \"object\") return \"anonymous\";\n const p = parsed as { query?: unknown; operationName?: unknown };\n return operationName(p.query, p.operationName);\n } catch {\n return \"anonymous\";\n }\n}\n\nexport interface GraphqlOutcome {\n ok: boolean;\n code?: string;\n message?: string;\n}\n\n/** Classifies a GraphQL response: ok = 2xx AND no `errors`. */\nexport function classifyGraphql(status: number, json: unknown): GraphqlOutcome {\n const payload = Array.isArray(json) ? json.find((j) => j && typeof j === \"object\" && Array.isArray(j.errors) && j.errors.length) ?? json[0] : json;\n const errors =\n payload && typeof payload === \"object\" && Array.isArray((payload as { errors?: unknown }).errors)\n ? ((payload as { errors: unknown[] }).errors as unknown[])\n : [];\n const is2xx = status >= 200 && status < 300;\n if (is2xx && errors.length === 0) return { ok: true };\n const first = errors[0] as { message?: unknown; extensions?: { code?: unknown } } | undefined;\n const extCode = first?.extensions?.code;\n const code = typeof extCode === \"string\" && extCode ? extCode : `HTTP_${status}`;\n const message = typeof first?.message === \"string\" ? first.message : undefined;\n return { ok: false, code, message };\n}\n","// Shared, runtime-agnostic helpers (used by both the Node and browser entries).\n// Wire format: see docs/PROTOCOL.md (v1).\n\nexport const SDK_NAME = \"@kanca-app/ikas\";\nexport const SDK_VERSION = \"0.3.0\";\nexport const DEFAULT_ENDPOINT = \"https://kanca.onurhan.dev\";\n\nexport const MAX_NAME = 200;\nexport const MAX_ERROR_MESSAGE = 500;\nexport const MAX_ERROR_CODE = 100;\nexport const MAX_ATTR_KEYS = 20;\nexport const MAX_ATTR_STRING = 500;\nexport const MAX_ATTR_KEY = 64;\nexport const MAX_EVENTS_PER_BATCH = 500;\n/** Protocol limit is 512 KB; keep headroom for the envelope. */\nexport const MAX_BATCH_BYTES = 480 * 1024;\n\nexport type EventType = \"webhook\" | \"graphql\" | \"rest\" | \"install\" | \"uninstall\" | \"vital\" | \"storefront\";\n\nexport type AttrValue = string | number | boolean;\n\nexport interface KancaError {\n code: string;\n message?: string;\n}\n\n/** A single wire event (PROTOCOL.md → Event). */\nexport interface KancaEvent {\n id: string;\n type: EventType;\n ts: number;\n name: string;\n status?: number;\n ok?: boolean;\n durationMs?: number;\n value?: number;\n error?: KancaError;\n merchantId?: string;\n deliveryId?: string;\n attrs?: Record<string, AttrValue>;\n}\n\nexport interface Batch {\n sdk: { name: string; version: string };\n events: KancaEvent[];\n}\n\nexport function truncate(s: string, max: number): string {\n return s.length > max ? s.slice(0, max) : s;\n}\n\nfunction hex(bytes: Uint8Array): string {\n let out = \"\";\n for (const b of bytes) out += b.toString(16).padStart(2, \"0\");\n return out;\n}\n\n/** UUID v4 via crypto.randomUUID, with a getRandomValues fallback (non-secure browser contexts). */\nexport function uuid(): string {\n const c = (globalThis as { crypto?: Crypto }).crypto;\n if (c && typeof c.randomUUID === \"function\") return c.randomUUID();\n const b = new Uint8Array(16);\n if (c && typeof c.getRandomValues === \"function\") c.getRandomValues(b);\n else for (let i = 0; i < 16; i++) b[i] = Math.floor(Math.random() * 256);\n b[6] = (b[6]! & 0x0f) | 0x40;\n b[8] = (b[8]! & 0x3f) | 0x80;\n const h = hex(b);\n return `${h.slice(0, 8)}-${h.slice(8, 12)}-${h.slice(12, 16)}-${h.slice(16, 20)}-${h.slice(20)}`;\n}\n\nfunction isFiniteNumber(v: unknown): v is number {\n return typeof v === \"number\" && Number.isFinite(v);\n}\n\nfunction cleanAttrs(attrs: unknown): Record<string, AttrValue> | undefined {\n if (!attrs || typeof attrs !== \"object\") return undefined;\n const out: Record<string, AttrValue> = {};\n let n = 0;\n for (const [k, v] of Object.entries(attrs as Record<string, unknown>)) {\n if (n >= MAX_ATTR_KEYS) break;\n if (v === undefined || v === null || !k) continue;\n let val: AttrValue;\n if (typeof v === \"boolean\") val = v;\n else if (typeof v === \"number\") {\n if (!Number.isFinite(v)) continue;\n val = v;\n } else if (typeof v === \"string\") val = truncate(v, MAX_ATTR_STRING);\n else continue; // attrs are flat: nested values are dropped, never stringified\n out[truncate(k, MAX_ATTR_KEY)] = val;\n n++;\n }\n return n > 0 ? out : undefined;\n}\n\n/**\n * Normalises an event so it always satisfies the protocol limits. Unknown\n * fields are dropped — only the whitelisted metadata below ever leaves the process.\n */\nexport function sanitizeEvent(e: KancaEvent): KancaEvent {\n const out: KancaEvent = {\n id: typeof e.id === \"string\" && e.id ? e.id : uuid(),\n type: e.type,\n ts: isFiniteNumber(e.ts) ? Math.round(e.ts) : Date.now(),\n name: truncate(typeof e.name === \"string\" && e.name ? e.name : \"unknown\", MAX_NAME),\n };\n if (isFiniteNumber(e.status) && e.status >= 100 && e.status <= 599) out.status = Math.trunc(e.status);\n if (typeof e.ok === \"boolean\") out.ok = e.ok;\n if (isFiniteNumber(e.durationMs)) out.durationMs = Math.min(86_400_000, Math.max(0, Math.round(e.durationMs * 10) / 10));\n if (isFiniteNumber(e.value)) out.value = e.value;\n if (e.error && typeof e.error.code === \"string\") {\n out.error = { code: truncate(e.error.code, MAX_ERROR_CODE) };\n if (typeof e.error.message === \"string\" && e.error.message) {\n out.error.message = truncate(e.error.message, MAX_ERROR_MESSAGE);\n }\n }\n if (typeof e.merchantId === \"string\" && e.merchantId) out.merchantId = truncate(e.merchantId, 200);\n if (typeof e.deliveryId === \"string\" && e.deliveryId) out.deliveryId = truncate(e.deliveryId, 200);\n const attrs = cleanAttrs(e.attrs);\n if (attrs) out.attrs = attrs;\n return out;\n}\n\nexport function errorMessage(err: unknown): string | undefined {\n try {\n if (err instanceof Error) return truncate(err.message || err.name, MAX_ERROR_MESSAGE);\n if (typeof err === \"string\") return truncate(err, MAX_ERROR_MESSAGE);\n } catch {\n /* ignore */\n }\n return undefined;\n}\n\nexport function joinUrl(endpoint: string, path: string): string {\n return endpoint.replace(/\\/+$/, \"\") + path;\n}\n\nconst encoder = typeof TextEncoder !== \"undefined\" ? /* @__PURE__ */ new TextEncoder() : undefined;\nexport function byteLength(s: string): number {\n return encoder ? encoder.encode(s).length : s.length * 3;\n}\n\n/**\n * Splits events into protocol-sized batches (<= maxEvents events and\n * <= MAX_BATCH_BYTES serialized). Returns serialized bodies.\n */\nexport function buildBodies(events: KancaEvent[], maxEvents: number): { body: string; events: KancaEvent[] }[] {\n const sdk = { name: SDK_NAME, version: SDK_VERSION };\n const envelope = byteLength(JSON.stringify({ sdk, events: [] }));\n const out: { body: string; events: KancaEvent[] }[] = [];\n let cur: KancaEvent[] = [];\n let size = envelope;\n const push = () => {\n if (cur.length) out.push({ body: JSON.stringify({ sdk, events: cur } satisfies Batch), events: cur });\n cur = [];\n size = envelope;\n };\n for (const ev of events) {\n const len = byteLength(JSON.stringify(ev)) + 1;\n if (cur.length && (cur.length >= maxEvents || size + len > MAX_BATCH_BYTES)) push();\n cur.push(ev);\n size += len;\n }\n push();\n return out;\n}\n","// Server-side transport: bounded in-memory queue, batched POST /api/ingest,\n// retry 429/5xx/network with exponential backoff (max 3 attempts). Never throws.\n\nimport { buildBodies, joinUrl, type KancaEvent } from \"./shared\";\n\nexport interface TransportOptions {\n key: string;\n endpoint: string;\n flushIntervalMs: number;\n maxBatchSize: number;\n maxQueueSize: number;\n maxAttempts: number;\n retryBaseMs: number;\n log: (msg: string, ...args: unknown[]) => void;\n warn: (msg: string) => void;\n /** Resolved at send time so tests / polyfills can replace globalThis.fetch. */\n getFetch: () => typeof fetch | undefined;\n}\n\ntype Timer = ReturnType<typeof setTimeout>;\n\nfunction unref(t: Timer): Timer {\n (t as { unref?: () => void }).unref?.();\n return t;\n}\n\nconst sleep = (ms: number) => new Promise<void>((r) => unref(setTimeout(r, ms)));\n\nexport class Transport {\n private queue: KancaEvent[] = [];\n private timer: Timer | undefined;\n private immediate: Timer | undefined;\n private chain: Promise<void> = Promise.resolve();\n private warned401 = false;\n dropped = 0;\n sent = 0;\n\n constructor(private readonly o: TransportOptions) {}\n\n get size(): number {\n return this.queue.length;\n }\n\n enqueue(ev: KancaEvent): void {\n this.queue.push(ev);\n if (this.queue.length > this.o.maxQueueSize) {\n const over = this.queue.length - this.o.maxQueueSize;\n this.queue.splice(0, over); // drop oldest\n this.dropped += over;\n this.o.log(`queue full, dropped ${over} oldest event(s) (total dropped: ${this.dropped})`);\n }\n if (this.queue.length >= this.o.maxBatchSize) this.scheduleSoon();\n else this.arm();\n }\n\n /** Arms the interval flush if not armed. */\n private arm(): void {\n if (this.timer) return;\n this.timer = unref(\n setTimeout(() => {\n this.timer = undefined;\n void this.flush();\n }, this.o.flushIntervalMs),\n );\n }\n\n /** Coalesced flush on the next macrotask (does not block the caller). */\n scheduleSoon(): void {\n if (this.immediate) return;\n this.immediate = unref(\n setTimeout(() => {\n this.immediate = undefined;\n void this.flush();\n }, 0),\n );\n }\n\n /**\n * Sends everything queued at call time. Calls are serialized, so awaiting\n * flush() always covers events enqueued before it. Always resolves.\n */\n flush(): Promise<void> {\n this.chain = this.chain.then(() => this.drain()).catch((e) => this.o.log(\"flush failed\", e));\n return this.chain;\n }\n\n private async drain(): Promise<void> {\n if (this.timer) {\n clearTimeout(this.timer);\n this.timer = undefined;\n }\n if (!this.queue.length) return;\n const events = this.queue.splice(0, this.queue.length);\n await this.sendAll(events);\n }\n\n private async sendAll(events: KancaEvent[]): Promise<void> {\n for (const { body, events: evs } of buildBodies(events, this.o.maxBatchSize)) {\n const ok = await this.send(body);\n if (ok) this.sent += evs.length;\n else {\n this.dropped += evs.length;\n this.o.log(`dropped batch of ${evs.length} event(s) (total dropped: ${this.dropped})`);\n }\n }\n }\n\n private async send(body: string): Promise<boolean> {\n const f = this.o.getFetch();\n if (!f) {\n this.o.log(\"no fetch available; cannot send\");\n return false;\n }\n const url = joinUrl(this.o.endpoint, \"/api/ingest\");\n for (let attempt = 1; attempt <= this.o.maxAttempts; attempt++) {\n let retryAfterMs: number | undefined;\n try {\n const res = await f(url, {\n method: \"POST\",\n headers: { authorization: `Bearer ${this.o.key}`, \"content-type\": \"application/json\" },\n body,\n });\n // drain so the connection can be reused\n res.text().catch(() => undefined);\n if (res.status >= 200 && res.status < 300) {\n this.o.log(`sent batch (${res.status})`);\n return true;\n }\n if (res.status === 401 && !this.warned401) {\n this.warned401 = true;\n this.o.warn(\"[kanca] ingest rejected the key (401). Check KANCA_KEY — events are being dropped.\");\n }\n if (res.status !== 429 && res.status < 500) {\n this.o.log(`ingest responded ${res.status}; not retrying`);\n return false;\n }\n const ra = Number(res.headers.get(\"retry-after\"));\n if (res.status === 429 && Number.isFinite(ra) && ra >= 0) retryAfterMs = Math.min(ra * 1000, 30_000);\n this.o.log(`ingest responded ${res.status} (attempt ${attempt}/${this.o.maxAttempts})`);\n } catch (err) {\n this.o.log(`network error (attempt ${attempt}/${this.o.maxAttempts})`, err);\n }\n if (attempt < this.o.maxAttempts) {\n const backoff = this.o.retryBaseMs * 2 ** (attempt - 1);\n const jitter = backoff * 0.2 * Math.random();\n await sleep(retryAfterMs ?? backoff + jitter);\n }\n }\n return false;\n }\n}\n","export type InstallState = \"installed\" | \"uninstalled\" | \"unknown\";\n\nexport interface InstallCheck {\n merchantId: string;\n state: InstallState;\n reason?: string;\n}\n\nexport interface InstallCheckStore<C = unknown> {\n merchantId: string;\n client: C;\n}\n\nexport interface InstallChecks {\n results: InstallCheck[];\n massRevocation: boolean;\n}\n\nexport const INSTALL_RECHECK_DELAY_MS = 3000;\nexport const MASS_REVOCATION_MIN = 5;\n\nconst PROBE = \"query getAuthorizedApp { getAuthorizedApp { id } }\";\n\ntype Probe = { state: \"installed\" | \"removed\" | \"unknown\"; reason?: string };\n\ntype ProbeClient = {\n _checkToken?: () => unknown;\n _client?: { request?: (args: { document: string }) => Promise<unknown> };\n};\n\nasync function probe(client: unknown): Promise<Probe> {\n const c = client as ProbeClient | null | undefined;\n if (!c || typeof c !== \"object\" || !c._client || typeof c._client.request !== \"function\") {\n return { state: \"unknown\", reason: \"UNSUPPORTED_CLIENT\" };\n }\n if (typeof c._checkToken === \"function\") {\n try {\n await c._checkToken();\n } catch {\n return { state: \"unknown\", reason: \"TOKEN_CHECK_FAILED\" };\n }\n }\n let data: unknown;\n try {\n data = await c._client!.request!({ document: PROBE });\n } catch (err) {\n const resp = (err as { response?: { status?: unknown; errors?: unknown } } | null | undefined)?.response;\n const status = resp?.status;\n if (typeof status !== \"number\") return { state: \"unknown\", reason: \"NETWORK\" };\n const first = Array.isArray(resp!.errors) ? (resp!.errors[0] as { extensions?: { code?: unknown } } | undefined) : undefined;\n const code = first?.extensions?.code;\n return { state: \"unknown\", reason: typeof code === \"string\" && code ? code.slice(0, 100) : `HTTP_${status}` };\n }\n if (data && typeof data === \"object\" && \"getAuthorizedApp\" in data) {\n const app = (data as { getAuthorizedApp: unknown }).getAuthorizedApp;\n if (app === null) return { state: \"removed\" };\n const id = app && typeof app === \"object\" ? (app as { id?: unknown }).id : undefined;\n if (typeof id === \"string\" && id) return { state: \"installed\" };\n }\n return { state: \"unknown\", reason: \"UNEXPECTED_RESPONSE\" };\n}\n\nconst sleep = (ms: number) => new Promise<void>((r) => setTimeout(r, ms));\n\nexport async function checkInstallState(merchantId: unknown, client: unknown, recheckDelayMs: number): Promise<InstallCheck> {\n if (typeof merchantId !== \"string\" || !merchantId) return { merchantId: \"\", state: \"unknown\", reason: \"MISSING_MERCHANT_ID\" };\n try {\n const first = await probe(client);\n if (first.state === \"installed\") return { merchantId, state: \"installed\" };\n if (first.state === \"unknown\") return { merchantId, state: \"unknown\", reason: first.reason };\n await sleep(recheckDelayMs);\n const second = await probe(client);\n if (second.state === \"removed\") return { merchantId, state: \"uninstalled\" };\n if (second.state === \"installed\") return { merchantId, state: \"installed\" };\n return { merchantId, state: \"unknown\", reason: second.reason };\n } catch {\n return { merchantId, state: \"unknown\", reason: \"UNEXPECTED_RESPONSE\" };\n }\n}\n\nexport function isMassRevocation(uninstalled: number, checked: number): boolean {\n return uninstalled >= MASS_REVOCATION_MIN && uninstalled * 2 > checked;\n}\n\nexport async function checkInstallStates(\n stores: readonly InstallCheckStore[],\n concurrency: number,\n recheckDelayMs: number,\n): Promise<InstallChecks> {\n const list = Array.isArray(stores) ? stores : [];\n const results: InstallCheck[] = new Array(list.length);\n let next = 0;\n const worker = async () => {\n while (next < list.length) {\n const i = next++;\n const store = list[i];\n results[i] = await checkInstallState(store?.merchantId, store?.client, recheckDelayMs);\n }\n };\n await Promise.all(Array.from({ length: Math.min(concurrency, list.length) }, worker));\n const uninstalled = results.filter((r) => r.state === \"uninstalled\").length;\n if (!isMassRevocation(uninstalled, results.length)) return { results, massRevocation: false };\n return {\n massRevocation: true,\n results: results.map((r) => (r.state === \"uninstalled\" ? { merchantId: r.merchantId, state: \"unknown\", reason: \"MASS_REVOCATION\" } : r)),\n };\n}\n","import type { KancaError } from \"./shared\";\n\nexport const OAUTH_REFRESH = \"oauth:refresh\";\nexport const OAUTH_TOKEN = \"oauth:token\";\n\nconst STORE_SUFFIXES = [\".myikas.com\", \".myikas.dev\"];\nconst OAUTH_ERROR = /^[a-z0-9_]{1,64}$/;\n\nexport function ikasTokenUrl(url: URL): { storeName?: string } | undefined {\n const host = url.hostname.toLowerCase();\n const suffix = STORE_SUFFIXES.find((s) => host.endsWith(s));\n if (!suffix) return undefined;\n if (url.pathname.replace(/\\/+$/, \"\") !== \"/api/admin/oauth/token\") return undefined;\n const store = host.slice(0, -suffix.length);\n if (!store || store.includes(\".\")) return undefined;\n return store === \"api\" ? {} : { storeName: store };\n}\n\nexport function oauthNameFromBody(body: unknown): string {\n try {\n let params: URLSearchParams | undefined;\n if (body instanceof URLSearchParams) params = body;\n else if (typeof body === \"string\") params = new URLSearchParams(body);\n return params?.get(\"grant_type\") === \"refresh_token\" ? OAUTH_REFRESH : OAUTH_TOKEN;\n } catch {\n return OAUTH_TOKEN;\n }\n}\n\nexport function oauthErrorCode(data: unknown, status: number): string {\n const code = data && typeof data === \"object\" ? (data as { error?: unknown }).error : undefined;\n return typeof code === \"string\" && OAUTH_ERROR.test(code) ? code : `HTTP_${status}`;\n}\n\nexport interface OAuthOutcome {\n status?: number;\n ok?: boolean;\n error?: KancaError;\n}\n\nfunction fromStatus(status: number, data: unknown): OAuthOutcome {\n const ok = status < 400;\n return ok ? { status, ok } : { status, ok, error: { code: oauthErrorCode(data, status) } };\n}\n\nfunction validStatus(v: unknown): v is number {\n return typeof v === \"number\" && Number.isInteger(v) && v >= 100 && v <= 599;\n}\n\nexport function refreshResultOutcome(result: unknown): OAuthOutcome {\n const r = result as { status?: unknown; data?: unknown } | null | undefined;\n if (r && typeof r === \"object\" && validStatus(r.status)) return fromStatus(r.status, r.data);\n return {};\n}\n\nexport function refreshErrorOutcome(err: unknown): OAuthOutcome {\n const resp = (err as { response?: { status?: unknown; data?: unknown } } | null | undefined)?.response;\n if (resp && typeof resp === \"object\" && validStatus(resp.status)) return fromStatus(resp.status, resp.data);\n return { ok: false, error: { code: \"NETWORK\" } };\n}\n","// @kanca-app/ikas — server entry (Node 18+, Next.js route handlers, any runtime with fetch/Request).\n// Contract: docs/PROTOCOL.md (v1). Metadata only — never payloads, variables, headers or tokens.\n\nimport { classifyGraphql, isIkasGraphqlUrl, operationName, operationNameFromBody } from \"./graphql\";\nimport {\n DEFAULT_ENDPOINT,\n MAX_EVENTS_PER_BATCH,\n errorMessage,\n sanitizeEvent,\n uuid,\n type AttrValue,\n type EventType,\n type KancaError,\n type KancaEvent,\n} from \"./shared\";\nimport { Transport } from \"./transport\";\nimport { INSTALL_RECHECK_DELAY_MS, checkInstallState, checkInstallStates, type InstallCheck, type InstallCheckStore, type InstallChecks } from \"./install\";\nimport { OAUTH_REFRESH, ikasTokenUrl, oauthErrorCode, oauthNameFromBody, refreshErrorOutcome, refreshResultOutcome, type OAuthOutcome } from \"./oauth\";\n\nexport { SDK_NAME, SDK_VERSION } from \"./shared\";\nexport type { AttrValue, EventType, KancaError, KancaEvent } from \"./shared\";\nexport { operationName } from \"./graphql\";\nexport type { InstallCheck, InstallCheckStore, InstallChecks, InstallState } from \"./install\";\n\nexport interface KancaOptions {\n /** Server key (`kk_...`). If missing, the SDK is a silent no-op. */\n key?: string | undefined;\n /** Kanca base URL. Default `https://kanca.onurhan.dev`. */\n endpoint?: string;\n /** Log SDK activity to the console. */\n debug?: boolean;\n /** Turn the SDK into a no-op (e.g. in tests). */\n disabled?: boolean;\n /** Flush interval in ms. Default 2000. */\n flushIntervalMs?: number;\n /** Events per request. Default 100, max 500 (protocol limit). */\n maxBatchSize?: number;\n /** Max events buffered in memory; oldest are dropped beyond this. Default 5000. */\n maxQueueSize?: number;\n /**\n * Optional hook to keep serverless functions alive until telemetry is sent,\n * e.g. `waitUntil` from `@vercel/functions`. Called with a flush promise\n * whenever events are recorded. Never delays the response.\n */\n waitUntil?: (promise: Promise<unknown>) => void;\n /** @internal Base backoff delay in ms (default 500). */\n retryBaseMs?: number;\n /** @internal Delay before the second getAuthorizedApp probe in ms (default 3000). */\n installRecheckDelayMs?: number;\n}\n\nexport interface TrackFields {\n name?: string;\n ts?: number;\n status?: number;\n ok?: boolean;\n durationMs?: number;\n value?: number;\n error?: KancaError;\n merchantId?: string;\n deliveryId?: string;\n /** install: shop name, stored as `attrs.storeName`. */\n storeName?: string;\n attrs?: Record<string, AttrValue>;\n}\n\nexport interface InstallFields {\n merchantId: string;\n storeName?: string;\n attrs?: Record<string, AttrValue>;\n}\n\nexport interface InstrumentOptions {\n /** merchantId to attach to every call. Defaults to `client.tokenData.merchantId` when present. */\n merchantId?: string;\n}\n\nexport interface CheckInstallFields {\n merchantId: string;\n}\n\nexport interface CheckInstallsOptions {\n /** Parallel checks. Default 3, 1..10. */\n concurrency?: number;\n}\n\nexport interface RefreshFields {\n merchantId?: string;\n storeName?: string;\n}\n\nexport type WebhookHandler<R extends Request, A extends unknown[]> = (req: R, ...rest: A) => Response | Promise<Response>;\n\nexport interface Kanca {\n /** Wraps a webhook route handler (Next.js `POST` compatible). Records one `webhook` event per delivery. */\n webhook<R extends Request, A extends unknown[]>(handler: WebhookHandler<R, A>): (req: R, ...rest: A) => Promise<Response>;\n /** Drop-in `fetch` that records ikas Admin GraphQL calls (`graphql`) and any other call (`rest`). */\n fetch: typeof globalThis.fetch;\n /** Instruments an `@ikas/admin-api-client` client (BaseGraphQLAPIClient subclass) in place. Returns it. */\n instrument<T>(client: T, options?: InstrumentOptions): T;\n /** Record an install (call in the OAuth callback after a successful token exchange). */\n track(type: \"install\", fields: InstallFields): void;\n /** Record any event manually. */\n track(type: Exclude<EventType, \"install\">, fields?: TrackFields): void;\n /**\n * Checks whether the store still has the app installed via `getAuthorizedApp` (twice, 3 s apart,\n * only an explicit `null` both times counts) and records an `uninstall` when it does not. Never rejects.\n */\n checkInstall(client: unknown, fields: CheckInstallFields): Promise<InstallCheck>;\n /**\n * `checkInstall` for many stores (e.g. a daily cron). Records nothing when at least 5 stores and more\n * than half of them look uninstalled at once; those results become `unknown` / `MASS_REVOCATION`.\n */\n checkInstalls(stores: readonly InstallCheckStore[], options?: CheckInstallsOptions): Promise<InstallChecks>;\n /** Runs an ikas OAuth token refresh (e.g. `OAuthAPI.refreshToken`) and records it as `oauth:refresh`. Result and errors pass through unchanged. */\n trackRefresh<T>(call: () => Promise<T>, fields?: RefreshFields): Promise<T>;\n /** Send everything buffered. Never rejects. `await` it at the end of serverless invocations. */\n flush(): Promise<void>;\n /** True when a valid key is configured and the SDK is not disabled. */\n readonly enabled: boolean;\n}\n\n// ---- flush on natural process exit (Node). One listener for all instances; an\n// instance is registered only while it has unsent events, so nothing leaks.\nconst exitFlushers = new Set<() => Promise<void>>();\nlet exitHookInstalled = false;\nfunction registerExitFlush(fn: () => Promise<void>): void {\n exitFlushers.add(fn);\n if (exitHookInstalled) return;\n try {\n const proc = (globalThis as { process?: { on?: (ev: string, fn: () => void) => void } }).process;\n if (proc && typeof proc.on === \"function\") {\n proc.on(\"beforeExit\", () => {\n // Scheduling work here keeps the process alive until the flush settles;\n // beforeExit fires again afterwards and finds the set empty.\n for (const f of [...exitFlushers]) void f();\n });\n exitHookInstalled = true;\n }\n } catch {\n /* not Node (edge runtime, browser) */\n }\n}\n\nconst MARK = Symbol.for(\"kanca.instrumented\");\n\nconst now = (): number =>\n typeof performance !== \"undefined\" && typeof performance.now === \"function\" ? performance.now() : Date.now();\n\nfunction clampInt(v: unknown, def: number, min: number, max: number): number {\n const n = typeof v === \"number\" && Number.isFinite(v) ? Math.floor(v) : def;\n return Math.min(max, Math.max(min, n));\n}\n\nfunction isRequest(v: unknown): v is Request {\n return typeof Request !== \"undefined\" && v instanceof Request;\n}\n\nfunction toUrl(input: unknown): URL | undefined {\n try {\n if (typeof input === \"string\") return new URL(input);\n if (input instanceof URL) return input;\n if (input && typeof (input as { url?: unknown }).url === \"string\") return new URL((input as { url: string }).url);\n } catch {\n /* relative or invalid URL */\n }\n return undefined;\n}\n\nfunction bodyToString(body: unknown): string | undefined {\n if (typeof body === \"string\") return body;\n try {\n if (body instanceof Uint8Array) return new TextDecoder().decode(body);\n if (body instanceof ArrayBuffer) return new TextDecoder().decode(new Uint8Array(body));\n } catch {\n /* ignore */\n }\n return undefined;\n}\n\ninterface WebhookMeta {\n scope?: string;\n id?: string;\n merchantId?: string;\n}\n\nfunction parseWebhookMeta(text: string): WebhookMeta | null {\n try {\n const j: unknown = JSON.parse(text);\n if (!j || typeof j !== \"object\" || Array.isArray(j)) return null;\n const o = j as Record<string, unknown>;\n // Only these three fields are read. Nothing else from the body is kept.\n return {\n scope: typeof o.scope === \"string\" && o.scope ? o.scope : undefined,\n id: typeof o.id === \"string\" && o.id ? o.id : undefined,\n merchantId: typeof o.merchantId === \"string\" && o.merchantId ? o.merchantId : undefined,\n };\n } catch {\n return null;\n }\n}\n\nfunction checkInstallsOptions(options: CheckInstallsOptions | undefined): number {\n return clampInt(options?.concurrency, 3, 1, 10);\n}\n\nfunction noopKanca(recheckDelayMs: number): Kanca {\n return {\n webhook: (handler) => async (req, ...rest) => handler(req, ...rest),\n fetch: ((input: RequestInfo | URL, init?: RequestInit) => globalThis.fetch(input, init)) as typeof fetch,\n instrument: (client) => client,\n track: () => undefined,\n checkInstall: (client, fields) => checkInstallState(fields?.merchantId, client, recheckDelayMs),\n checkInstalls: (stores, options) => checkInstallStates(stores, checkInstallsOptions(options), recheckDelayMs),\n trackRefresh: async (call) => await call(),\n flush: () => Promise.resolve(),\n enabled: false,\n };\n}\n\nexport function createKanca(options: KancaOptions = {}): Kanca {\n const debug = !!options.debug;\n const log = (msg: string, ...args: unknown[]) => {\n if (debug) console.debug(`[kanca] ${msg}`, ...args);\n };\n const warn = (msg: string) => console.warn(msg);\n const recheckDelayMs = clampInt(options.installRecheckDelayMs, INSTALL_RECHECK_DELAY_MS, 0, 60_000);\n\n if (options.disabled) {\n log(\"disabled; SDK is a no-op\");\n return noopKanca(recheckDelayMs);\n }\n const key = typeof options.key === \"string\" ? options.key.trim() : \"\";\n if (!key) {\n log(\"no key (KANCA_KEY) configured; SDK is a no-op\");\n return noopKanca(recheckDelayMs);\n }\n if (!key.startsWith(\"kk_\")) {\n warn(\n key.startsWith(\"kp_\")\n ? \"[kanca] createKanca() needs a server key (kk_...), got a public key (kp_...). Public keys are for reportVitals()/<KancaVitals/> only. SDK disabled.\"\n : \"[kanca] KANCA_KEY must start with kk_. SDK disabled.\",\n );\n return noopKanca(recheckDelayMs);\n }\n\n const maxBatchSize = clampInt(options.maxBatchSize, 100, 1, MAX_EVENTS_PER_BATCH);\n const transport = new Transport({\n key,\n endpoint: options.endpoint || DEFAULT_ENDPOINT,\n flushIntervalMs: clampInt(options.flushIntervalMs, 2000, 0, 60 * 60 * 1000),\n maxBatchSize,\n maxQueueSize: clampInt(options.maxQueueSize, 5000, maxBatchSize, 1_000_000),\n maxAttempts: 3,\n retryBaseMs: clampInt(options.retryBaseMs, 500, 0, 60_000),\n log,\n warn,\n getFetch: () => (typeof globalThis.fetch === \"function\" ? globalThis.fetch.bind(globalThis) : undefined),\n });\n\n // Observations still being finalised (e.g. waiting for a cloned body). flush() awaits them.\n const pending = new Set<Promise<unknown>>();\n const observe = (p: Promise<unknown>) => {\n const tracked = p.catch((e) => log(\"observation failed\", e)).finally(() => pending.delete(tracked));\n pending.add(tracked);\n };\n\n // With waitUntil, every event (not only webhooks) keeps the invocation alive until sent;\n // otherwise a serverless function can be frozen before the interval flush fires.\n let keepAlive = false;\n const holdUntilSent = () => {\n if (!options.waitUntil || keepAlive) return;\n keepAlive = true;\n try {\n options.waitUntil(\n new Promise<void>((r) => setTimeout(r, 0))\n .then(flush)\n .finally(() => {\n keepAlive = false;\n }),\n );\n } catch (e) {\n keepAlive = false;\n log(\"waitUntil threw\", e);\n }\n };\n\n const emit = (ev: Omit<KancaEvent, \"id\"> & { id?: string }) => {\n try {\n const clean = sanitizeEvent({ ...ev, id: ev.id ?? uuid() } as KancaEvent);\n transport.enqueue(clean);\n registerExitFlush(flush);\n holdUntilSent();\n log(`${clean.type} ${clean.name}`, { status: clean.status, ok: clean.ok, durationMs: clean.durationMs });\n } catch (e) {\n log(\"failed to record event\", e);\n }\n };\n\n const flush = async (): Promise<void> => {\n try {\n while (pending.size) await Promise.allSettled([...pending]);\n await transport.flush();\n } catch (e) {\n log(\"flush error\", e);\n }\n if (!transport.size && !pending.size) exitFlushers.delete(flush);\n };\n\n const afterDelivery = () => {\n transport.scheduleSoon();\n if (options.waitUntil) {\n try {\n options.waitUntil(flush());\n } catch (e) {\n log(\"waitUntil threw\", e);\n }\n }\n };\n\n // ---- webhook ---------------------------------------------------------------------------------\n\n const webhook: Kanca[\"webhook\"] = (handler) =>\n async function kancaWebhook(this: unknown, req, ...rest) {\n const ts = Date.now();\n const t0 = now();\n let meta: Promise<WebhookMeta | null>;\n try {\n // Clone before the handler touches the body; the original stays fully readable.\n meta = req.clone().text().then(parseWebhookMeta, () => null);\n } catch {\n meta = Promise.resolve(null);\n }\n\n const record = (status: number | undefined, error: KancaError | undefined, durationMs: number) => {\n observe(\n meta.then((m) => {\n const base = { ts, merchantId: m?.merchantId, deliveryId: m?.id };\n emit({\n ...base,\n type: \"webhook\",\n name: m?.scope ?? \"unknown\",\n status,\n ok: status === 200,\n durationMs,\n error,\n });\n }),\n );\n afterDelivery();\n };\n\n let res: Response;\n try {\n res = await handler.call(this, req, ...rest);\n } catch (err) {\n try {\n record(500, { code: \"HANDLER_THREW\", message: errorMessage(err) }, now() - t0);\n } catch {\n /* never mask the handler's error */\n }\n throw err;\n }\n try {\n const status = res && typeof (res as { status?: unknown }).status === \"number\" ? res.status : undefined;\n record(status, status === undefined ? { code: \"NO_RESPONSE\", message: \"handler did not return a Response\" } : status === 200 ? undefined : { code: `HTTP_${status}` }, now() - t0);\n } catch {\n /* ignore */\n }\n return res;\n };\n\n // ---- fetch -----------------------------------------------------------------------------------\n\n const kfetch = async function kancaFetch(input: RequestInfo | URL, init?: RequestInit): Promise<Response> {\n const base = globalThis.fetch;\n let url: URL | undefined;\n let method = \"GET\";\n let gql = false;\n let opName: Promise<string> | undefined;\n let tokenUrl: { storeName?: string } | undefined;\n try {\n url = toUrl(input);\n method = (init?.method ?? (isRequest(input) ? input.method : \"GET\")).toUpperCase();\n gql = !!url && isIkasGraphqlUrl(url);\n tokenUrl = url ? ikasTokenUrl(url) : undefined;\n if (tokenUrl) {\n if (init && init.body != null) opName = Promise.resolve(oauthNameFromBody(bodyToString(init.body) ?? init.body));\n else if (isRequest(input) && input.body) opName = input.clone().text().then(oauthNameFromBody, () => oauthNameFromBody(undefined));\n else opName = Promise.resolve(oauthNameFromBody(undefined));\n } else if (gql) {\n if (init && init.body != null) opName = Promise.resolve(operationNameFromBody(bodyToString(init.body)));\n else if (isRequest(input) && input.body) opName = input.clone().text().then(operationNameFromBody, () => \"anonymous\");\n else opName = Promise.resolve(\"anonymous\");\n }\n } catch {\n url = undefined;\n }\n if (!url) return base(input, init);\n\n const ts = Date.now();\n const t0 = now();\n let res: Response;\n try {\n res = await base(input, init);\n } catch (err) {\n const durationMs = now() - t0;\n try {\n const error = { code: \"NETWORK\", message: errorMessage(err) };\n if (tokenUrl) recordOAuth(opName, ts, durationMs, tokenUrl.storeName, { ok: false, error: { code: \"NETWORK\" } }, undefined);\n else if (gql) observe((opName ?? Promise.resolve(\"anonymous\")).then((name) => emit({ type: \"graphql\", name, ts, ok: false, durationMs, error })));\n else emit({ type: \"rest\", name: `${method} ${url.host}${url.pathname}`, ts, ok: false, durationMs, error });\n } catch {\n /* ignore */\n }\n throw err;\n }\n const durationMs = now() - t0;\n try {\n const status = res.status;\n if (tokenUrl) {\n let clone: Response | undefined;\n try {\n clone = status >= 400 ? res.clone() : undefined;\n } catch {\n clone = undefined;\n }\n recordOAuth(opName, ts, durationMs, tokenUrl.storeName, { status, ok: status < 400 }, clone);\n } else if (gql) {\n let clone: Response | undefined;\n try {\n clone = res.clone(); // synchronously, before the caller can consume the body\n } catch {\n clone = undefined;\n }\n observe(\n (async () => {\n const name = await (opName ?? Promise.resolve(\"anonymous\"));\n let json: unknown;\n try {\n json = clone ? JSON.parse(await clone.text()) : undefined;\n } catch {\n json = undefined;\n }\n const out = classifyGraphql(status, json);\n emit({\n type: \"graphql\",\n name,\n ts,\n status,\n ok: out.ok,\n durationMs,\n error: out.ok ? undefined : { code: out.code ?? `HTTP_${status}`, message: out.message },\n });\n })(),\n );\n } else {\n const ok = status < 400;\n emit({ type: \"rest\", name: `${method} ${url.host}${url.pathname}`, ts, status, ok, durationMs, error: ok ? undefined : { code: `HTTP_${status}` } });\n }\n } catch {\n /* ignore */\n }\n return res;\n } as typeof fetch;\n\n const recordOAuth = (\n name: Promise<string> | undefined,\n ts: number,\n durationMs: number,\n storeName: string | undefined,\n outcome: OAuthOutcome,\n errorBody: Response | undefined,\n ) => {\n observe(\n (async () => {\n let error = outcome.error;\n if (errorBody && typeof outcome.status === \"number\") {\n let data: unknown;\n try {\n data = JSON.parse(await errorBody.text());\n } catch {\n data = undefined;\n }\n error = { code: oauthErrorCode(data, outcome.status) };\n }\n emit({\n type: \"rest\",\n name: await (name ?? Promise.resolve(OAUTH_REFRESH)),\n ts,\n status: outcome.status,\n ok: outcome.ok,\n durationMs,\n error,\n attrs: storeName ? { storeName } : undefined,\n });\n })(),\n );\n };\n\n // ---- instrument (@ikas/admin-api-client) -----------------------------------------------------\n\n let warnedInstrument = false;\n const instrument: Kanca[\"instrument\"] = (client, opts) => {\n const c = client as unknown as {\n [MARK]?: boolean;\n _client?: { request?: unknown };\n createGraphQLClient?: (...a: unknown[]) => unknown;\n tokenData?: { merchantId?: unknown };\n };\n try {\n if (!c || typeof c !== \"object\" || typeof c.createGraphQLClient !== \"function\" || !c._client || typeof c._client.request !== \"function\") {\n if (!warnedInstrument) {\n warnedInstrument = true;\n warn(\n \"[kanca] instrument(): not an @ikas/admin-api-client client (expected createGraphQLClient() and _client.request()). Nothing will be recorded for it — use kanca.fetch instead.\",\n );\n }\n return client;\n }\n if (c[MARK]) return client;\n\n const merchantIdFor = (): string | undefined => {\n if (opts?.merchantId) return opts.merchantId;\n const m = c.tokenData?.merchantId;\n return typeof m === \"string\" && m ? m : undefined;\n };\n\n const wrap = (gc: unknown): unknown => {\n const g = gc as { [MARK]?: boolean; request?: (...a: unknown[]) => Promise<unknown> };\n if (!g || typeof g.request !== \"function\" || g[MARK]) return gc;\n const orig = g.request;\n g.request = async function (this: unknown, ...args: unknown[]) {\n let name = \"anonymous\";\n try {\n const a0 = args[0] as { document?: unknown; operationName?: unknown; loc?: { source?: { body?: unknown } } } | string | undefined;\n const doc = typeof a0 === \"string\" ? a0 : a0 && typeof a0 === \"object\" && \"document\" in a0 ? a0.document : a0;\n const text =\n typeof doc === \"string\" ? doc : (doc as { loc?: { source?: { body?: unknown } } } | undefined)?.loc?.source?.body;\n name = operationName(text, typeof a0 === \"object\" ? a0?.operationName : undefined);\n } catch {\n /* keep anonymous */\n }\n const ts = Date.now();\n const t0 = now();\n try {\n const data = await orig.apply(this, args);\n // Success means the library saw a 2xx without `errors`; the exact status is not exposed, so it is omitted.\n emit({ type: \"graphql\", name, ts, ok: true, durationMs: now() - t0, merchantId: merchantIdFor() });\n return data;\n } catch (err) {\n try {\n const durationMs = now() - t0;\n const resp = (err as { response?: { status?: unknown; errors?: unknown } } | undefined)?.response;\n if (resp && typeof resp.status === \"number\") {\n const out = classifyGraphql(resp.status, { errors: Array.isArray(resp.errors) ? resp.errors : [] });\n // A thrown ClientError is always a failure, even for a 2xx without parsable errors.\n emit({\n type: \"graphql\",\n name,\n ts,\n status: resp.status,\n ok: false,\n durationMs,\n merchantId: merchantIdFor(),\n error: { code: out.code ?? `HTTP_${resp.status}`, message: out.message },\n });\n } else {\n emit({ type: \"graphql\", name, ts, ok: false, durationMs, merchantId: merchantIdFor(), error: { code: \"NETWORK\", message: errorMessage(err) } });\n }\n } catch {\n /* ignore */\n }\n throw err;\n }\n };\n g[MARK] = true;\n return gc;\n };\n\n const origCreate = c.createGraphQLClient;\n // The client rebuilds `_client` after a token refresh, so wrap the factory too.\n c.createGraphQLClient = function (this: unknown, ...a: unknown[]) {\n return wrap(origCreate.apply(this, a));\n };\n c._client = wrap(c._client) as typeof c._client;\n c[MARK] = true;\n log(\"instrumented @ikas/admin-api-client\");\n } catch (e) {\n log(\"instrument failed\", e);\n }\n return client;\n };\n\n // ---- track -----------------------------------------------------------------------------------\n\n const track = (type: EventType, fields: TrackFields = {}): void => {\n try {\n const f = fields ?? {};\n let name = f.name;\n if (type === \"install\" || type === \"uninstall\") name = type;\n let ok = f.ok;\n if (ok === undefined && typeof f.status === \"number\") {\n if (type === \"webhook\") ok = f.status === 200;\n else if (type === \"graphql\") ok = f.status >= 200 && f.status < 300;\n else if (type === \"rest\") ok = f.status < 400;\n }\n const attrs = { ...(f.attrs ?? {}) };\n if (typeof f.storeName === \"string\" && f.storeName) attrs.storeName = f.storeName;\n emit({\n type,\n name: name ?? \"unknown\",\n ts: f.ts ?? Date.now(),\n status: f.status,\n ok,\n durationMs: f.durationMs,\n value: f.value,\n error: f.error,\n merchantId: f.merchantId,\n deliveryId: f.deliveryId,\n attrs,\n });\n if (type === \"install\") transport.scheduleSoon();\n } catch (e) {\n log(\"track failed\", e);\n }\n };\n\n const recordUninstall = (result: InstallCheck) => {\n if (result.state !== \"uninstalled\") return;\n emit({ type: \"uninstall\", name: \"uninstall\", ts: Date.now(), merchantId: result.merchantId, attrs: { source: \"check\" } });\n transport.scheduleSoon();\n };\n\n const checkInstall: Kanca[\"checkInstall\"] = async (client, fields) => {\n const result = await checkInstallState(fields?.merchantId, client, recheckDelayMs);\n recordUninstall(result);\n return result;\n };\n\n const checkInstalls: Kanca[\"checkInstalls\"] = async (stores, opts) => {\n const out = await checkInstallStates(stores, checkInstallsOptions(opts), recheckDelayMs);\n for (const r of out.results) recordUninstall(r);\n return out;\n };\n\n const trackRefresh: Kanca[\"trackRefresh\"] = async (call, fields) => {\n const ts = Date.now();\n const t0 = now();\n const record = (outcome: OAuthOutcome) => {\n try {\n const attrs: Record<string, AttrValue> = {};\n if (typeof fields?.storeName === \"string\" && fields.storeName) attrs.storeName = fields.storeName;\n emit({ type: \"rest\", name: OAUTH_REFRESH, ts, durationMs: now() - t0, merchantId: fields?.merchantId, attrs, ...outcome });\n } catch (e) {\n log(\"trackRefresh failed\", e);\n }\n };\n let result;\n try {\n result = await call();\n } catch (err) {\n record(refreshErrorOutcome(err));\n throw err;\n }\n record(refreshResultOutcome(result));\n return result;\n };\n\n log(`enabled → ${options.endpoint || DEFAULT_ENDPOINT}`);\n\n return {\n webhook,\n fetch: kfetch,\n instrument,\n track: track as Kanca[\"track\"],\n checkInstall,\n checkInstalls,\n trackRefresh,\n flush,\n enabled: true,\n };\n}\n"],"mappings":"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","names":["sleep","durationMs"]}
package/dist/react.cjs CHANGED
@@ -32,7 +32,7 @@ var import_web_vitals = require("web-vitals");
32
32
 
33
33
  // src/shared.ts
34
34
  var SDK_NAME = "@kanca-app/ikas";
35
- var SDK_VERSION = "0.2.0";
35
+ var SDK_VERSION = "0.3.0";
36
36
  var DEFAULT_ENDPOINT = "https://kanca.onurhan.dev";
37
37
  var MAX_NAME = 200;
38
38
  var MAX_ERROR_MESSAGE = 500;
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/react.ts","../src/browser.ts","../src/shared.ts"],"sourcesContent":["// @kanca-app/ikas/react — <KancaVitals /> client component. The build prepends \"use client\".\nimport { useEffect } from \"react\";\nimport { reportVitals, type ReportVitalsOptions } from \"./browser\";\n\nexport type KancaVitalsProps = ReportVitalsOptions;\n\n/**\n * Reports Core Web Vitals of the admin UI to Kanca. Render once, e.g. in the\n * root layout. Renders nothing.\n */\nexport function KancaVitals({ publicKey, endpoint, debug }: KancaVitalsProps): null {\n useEffect(() => {\n reportVitals({ publicKey, endpoint, debug });\n // reportVitals is idempotent; later prop changes are intentionally ignored.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n return null;\n}\n\nexport { reportVitals };\nexport type { ReportVitalsOptions };\n","// @kanca-app/ikas/browser — Core Web Vitals for the ikas admin iframe.\n// Sends only { name, value, rating, pathname } per metric. Uses a public key (kp_...).\n\nimport { onCLS, onFCP, onINP, onLCP, onTTFB, type Metric } from \"web-vitals\";\nimport { DEFAULT_ENDPOINT, SDK_NAME, SDK_VERSION, joinUrl, sanitizeEvent, uuid, type KancaEvent } from \"./shared\";\n\nexport interface ReportVitalsOptions {\n /** Public key (`kp_...`). Safe to expose in the browser; only accepts `vital` events. */\n publicKey: string | undefined;\n /** Kanca base URL. Default `https://kanca.onurhan.dev`. */\n endpoint?: string;\n debug?: boolean;\n}\n\nconst VITALS = [\"LCP\", \"INP\", \"CLS\", \"FCP\", \"TTFB\"] as const;\n\n// Global (not module) flag so duplicate copies (ESM + CJS, browser + react bundles) still start once.\nconst FLAG = \"__kancaVitalsStarted\";\nconst g = globalThis as { [FLAG]?: boolean };\n\n/**\n * Starts collecting LCP, INP, CLS, FCP and TTFB (final values, once per page\n * load per metric) and sends them when the page is hidden or unloaded.\n * Safe to call more than once — only the first call has an effect.\n */\nexport function reportVitals(options: ReportVitalsOptions): void {\n try {\n if (g[FLAG] || typeof window === \"undefined\" || typeof document === \"undefined\") return;\n const key = typeof options?.publicKey === \"string\" ? options.publicKey.trim() : \"\";\n const debug = !!options?.debug;\n if (!key.startsWith(\"kp_\")) {\n if (debug || key) console.warn(\"[kanca] reportVitals(): a public key (kp_...) is required; vitals disabled.\");\n return;\n }\n g[FLAG] = true;\n\n const url = `${joinUrl(options.endpoint || DEFAULT_ENDPOINT, \"/api/ingest/browser\")}?key=${encodeURIComponent(key)}`;\n const path = location.pathname;\n const ts =\n typeof performance !== \"undefined\" && typeof performance.timeOrigin === \"number\" && performance.timeOrigin > 0\n ? Math.round(performance.timeOrigin)\n : Date.now();\n const queue: KancaEvent[] = [];\n const seen = new Set<string>();\n\n const send = () => {\n if (!queue.length) return;\n const events = queue.splice(0, queue.length);\n const body = JSON.stringify({ sdk: { name: SDK_NAME, version: SDK_VERSION }, events });\n if (debug) console.debug(\"[kanca] vitals\", events);\n // fetch+keepalive first: content blockers drop sendBeacon (\"ping\") requests\n // while sendBeacon still returns true, so vitals would vanish silently.\n // A text/plain body keeps it a simple request (no CORS preflight).\n try {\n if (typeof fetch === \"function\") {\n void fetch(url, { method: \"POST\", body, keepalive: true, mode: \"cors\", credentials: \"omit\", headers: { \"content-type\": \"text/plain\" } }).catch(() => undefined);\n return;\n }\n } catch {\n /* fall through */\n }\n try {\n if (typeof navigator !== \"undefined\" && typeof navigator.sendBeacon === \"function\") navigator.sendBeacon(url, body);\n } catch {\n /* never throw */\n }\n };\n\n const onMetric = (m: Metric) => {\n try {\n if (!(VITALS as readonly string[]).includes(m.name) || seen.has(m.name)) return;\n seen.add(m.name);\n queue.push(sanitizeEvent({ id: uuid(), type: \"vital\", ts, name: m.name, value: m.value, attrs: { path, rating: m.rating } }));\n } catch {\n /* ignore */\n }\n };\n\n // Without reportAllChanges, web-vitals reports each metric's final value.\n onLCP(onMetric);\n onINP(onMetric);\n onCLS(onMetric);\n onFCP(onMetric);\n onTTFB(onMetric);\n\n // Registered after web-vitals' own listeners, so final values are queued before we send.\n addEventListener(\n \"visibilitychange\",\n () => {\n // Microtask: lets any report web-vitals triggers on this same event land first.\n if (document.visibilityState === \"hidden\") queueMicrotask(send);\n },\n true,\n );\n addEventListener(\"pagehide\", send, true);\n } catch {\n /* never break the host page */\n }\n}\n\n/** @internal for tests */\nexport function __resetVitalsForTests(): void {\n g[FLAG] = false;\n}\n","// Shared, runtime-agnostic helpers (used by both the Node and browser entries).\n// Wire format: see docs/PROTOCOL.md (v1).\n\nexport const SDK_NAME = \"@kanca-app/ikas\";\nexport const SDK_VERSION = \"0.2.0\";\nexport const DEFAULT_ENDPOINT = \"https://kanca.onurhan.dev\";\n\nexport const MAX_NAME = 200;\nexport const MAX_ERROR_MESSAGE = 500;\nexport const MAX_ERROR_CODE = 100;\nexport const MAX_ATTR_KEYS = 20;\nexport const MAX_ATTR_STRING = 500;\nexport const MAX_ATTR_KEY = 64;\nexport const MAX_EVENTS_PER_BATCH = 500;\n/** Protocol limit is 512 KB; keep headroom for the envelope. */\nexport const MAX_BATCH_BYTES = 480 * 1024;\n\nexport type EventType = \"webhook\" | \"graphql\" | \"rest\" | \"install\" | \"uninstall\" | \"vital\" | \"storefront\";\n\nexport type AttrValue = string | number | boolean;\n\nexport interface KancaError {\n code: string;\n message?: string;\n}\n\n/** A single wire event (PROTOCOL.md → Event). */\nexport interface KancaEvent {\n id: string;\n type: EventType;\n ts: number;\n name: string;\n status?: number;\n ok?: boolean;\n durationMs?: number;\n value?: number;\n error?: KancaError;\n merchantId?: string;\n deliveryId?: string;\n attrs?: Record<string, AttrValue>;\n}\n\nexport interface Batch {\n sdk: { name: string; version: string };\n events: KancaEvent[];\n}\n\nexport function truncate(s: string, max: number): string {\n return s.length > max ? s.slice(0, max) : s;\n}\n\nfunction hex(bytes: Uint8Array): string {\n let out = \"\";\n for (const b of bytes) out += b.toString(16).padStart(2, \"0\");\n return out;\n}\n\n/** UUID v4 via crypto.randomUUID, with a getRandomValues fallback (non-secure browser contexts). */\nexport function uuid(): string {\n const c = (globalThis as { crypto?: Crypto }).crypto;\n if (c && typeof c.randomUUID === \"function\") return c.randomUUID();\n const b = new Uint8Array(16);\n if (c && typeof c.getRandomValues === \"function\") c.getRandomValues(b);\n else for (let i = 0; i < 16; i++) b[i] = Math.floor(Math.random() * 256);\n b[6] = (b[6]! & 0x0f) | 0x40;\n b[8] = (b[8]! & 0x3f) | 0x80;\n const h = hex(b);\n return `${h.slice(0, 8)}-${h.slice(8, 12)}-${h.slice(12, 16)}-${h.slice(16, 20)}-${h.slice(20)}`;\n}\n\nfunction isFiniteNumber(v: unknown): v is number {\n return typeof v === \"number\" && Number.isFinite(v);\n}\n\nfunction cleanAttrs(attrs: unknown): Record<string, AttrValue> | undefined {\n if (!attrs || typeof attrs !== \"object\") return undefined;\n const out: Record<string, AttrValue> = {};\n let n = 0;\n for (const [k, v] of Object.entries(attrs as Record<string, unknown>)) {\n if (n >= MAX_ATTR_KEYS) break;\n if (v === undefined || v === null || !k) continue;\n let val: AttrValue;\n if (typeof v === \"boolean\") val = v;\n else if (typeof v === \"number\") {\n if (!Number.isFinite(v)) continue;\n val = v;\n } else if (typeof v === \"string\") val = truncate(v, MAX_ATTR_STRING);\n else continue; // attrs are flat: nested values are dropped, never stringified\n out[truncate(k, MAX_ATTR_KEY)] = val;\n n++;\n }\n return n > 0 ? out : undefined;\n}\n\n/**\n * Normalises an event so it always satisfies the protocol limits. Unknown\n * fields are dropped — only the whitelisted metadata below ever leaves the process.\n */\nexport function sanitizeEvent(e: KancaEvent): KancaEvent {\n const out: KancaEvent = {\n id: typeof e.id === \"string\" && e.id ? e.id : uuid(),\n type: e.type,\n ts: isFiniteNumber(e.ts) ? Math.round(e.ts) : Date.now(),\n name: truncate(typeof e.name === \"string\" && e.name ? e.name : \"unknown\", MAX_NAME),\n };\n if (isFiniteNumber(e.status) && e.status >= 100 && e.status <= 599) out.status = Math.trunc(e.status);\n if (typeof e.ok === \"boolean\") out.ok = e.ok;\n if (isFiniteNumber(e.durationMs)) out.durationMs = Math.min(86_400_000, Math.max(0, Math.round(e.durationMs * 10) / 10));\n if (isFiniteNumber(e.value)) out.value = e.value;\n if (e.error && typeof e.error.code === \"string\") {\n out.error = { code: truncate(e.error.code, MAX_ERROR_CODE) };\n if (typeof e.error.message === \"string\" && e.error.message) {\n out.error.message = truncate(e.error.message, MAX_ERROR_MESSAGE);\n }\n }\n if (typeof e.merchantId === \"string\" && e.merchantId) out.merchantId = truncate(e.merchantId, 200);\n if (typeof e.deliveryId === \"string\" && e.deliveryId) out.deliveryId = truncate(e.deliveryId, 200);\n const attrs = cleanAttrs(e.attrs);\n if (attrs) out.attrs = attrs;\n return out;\n}\n\nexport function errorMessage(err: unknown): string | undefined {\n try {\n if (err instanceof Error) return truncate(err.message || err.name, MAX_ERROR_MESSAGE);\n if (typeof err === \"string\") return truncate(err, MAX_ERROR_MESSAGE);\n } catch {\n /* ignore */\n }\n return undefined;\n}\n\nexport function joinUrl(endpoint: string, path: string): string {\n return endpoint.replace(/\\/+$/, \"\") + path;\n}\n\nconst encoder = typeof TextEncoder !== \"undefined\" ? /* @__PURE__ */ new TextEncoder() : undefined;\nexport function byteLength(s: string): number {\n return encoder ? encoder.encode(s).length : s.length * 3;\n}\n\n/**\n * Splits events into protocol-sized batches (<= maxEvents events and\n * <= MAX_BATCH_BYTES serialized). Returns serialized bodies.\n */\nexport function buildBodies(events: KancaEvent[], maxEvents: number): { body: string; events: KancaEvent[] }[] {\n const sdk = { name: SDK_NAME, version: SDK_VERSION };\n const envelope = byteLength(JSON.stringify({ sdk, events: [] }));\n const out: { body: string; events: KancaEvent[] }[] = [];\n let cur: KancaEvent[] = [];\n let size = envelope;\n const push = () => {\n if (cur.length) out.push({ body: JSON.stringify({ sdk, events: cur } satisfies Batch), events: cur });\n cur = [];\n size = envelope;\n };\n for (const ev of events) {\n const len = byteLength(JSON.stringify(ev)) + 1;\n if (cur.length && (cur.length >= maxEvents || size + len > MAX_BATCH_BYTES)) push();\n cur.push(ev);\n size += len;\n }\n push();\n return out;\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/react.ts","../src/browser.ts","../src/shared.ts"],"sourcesContent":["// @kanca-app/ikas/react — <KancaVitals /> client component. The build prepends \"use client\".\nimport { useEffect } from \"react\";\nimport { reportVitals, type ReportVitalsOptions } from \"./browser\";\n\nexport type KancaVitalsProps = ReportVitalsOptions;\n\n/**\n * Reports Core Web Vitals of the admin UI to Kanca. Render once, e.g. in the\n * root layout. Renders nothing.\n */\nexport function KancaVitals({ publicKey, endpoint, debug }: KancaVitalsProps): null {\n useEffect(() => {\n reportVitals({ publicKey, endpoint, debug });\n // reportVitals is idempotent; later prop changes are intentionally ignored.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n return null;\n}\n\nexport { reportVitals };\nexport type { ReportVitalsOptions };\n","// @kanca-app/ikas/browser — Core Web Vitals for the ikas admin iframe.\n// Sends only { name, value, rating, pathname } per metric. Uses a public key (kp_...).\n\nimport { onCLS, onFCP, onINP, onLCP, onTTFB, type Metric } from \"web-vitals\";\nimport { DEFAULT_ENDPOINT, SDK_NAME, SDK_VERSION, joinUrl, sanitizeEvent, uuid, type KancaEvent } from \"./shared\";\n\nexport interface ReportVitalsOptions {\n /** Public key (`kp_...`). Safe to expose in the browser; only accepts `vital` events. */\n publicKey: string | undefined;\n /** Kanca base URL. Default `https://kanca.onurhan.dev`. */\n endpoint?: string;\n debug?: boolean;\n}\n\nconst VITALS = [\"LCP\", \"INP\", \"CLS\", \"FCP\", \"TTFB\"] as const;\n\n// Global (not module) flag so duplicate copies (ESM + CJS, browser + react bundles) still start once.\nconst FLAG = \"__kancaVitalsStarted\";\nconst g = globalThis as { [FLAG]?: boolean };\n\n/**\n * Starts collecting LCP, INP, CLS, FCP and TTFB (final values, once per page\n * load per metric) and sends them when the page is hidden or unloaded.\n * Safe to call more than once — only the first call has an effect.\n */\nexport function reportVitals(options: ReportVitalsOptions): void {\n try {\n if (g[FLAG] || typeof window === \"undefined\" || typeof document === \"undefined\") return;\n const key = typeof options?.publicKey === \"string\" ? options.publicKey.trim() : \"\";\n const debug = !!options?.debug;\n if (!key.startsWith(\"kp_\")) {\n if (debug || key) console.warn(\"[kanca] reportVitals(): a public key (kp_...) is required; vitals disabled.\");\n return;\n }\n g[FLAG] = true;\n\n const url = `${joinUrl(options.endpoint || DEFAULT_ENDPOINT, \"/api/ingest/browser\")}?key=${encodeURIComponent(key)}`;\n const path = location.pathname;\n const ts =\n typeof performance !== \"undefined\" && typeof performance.timeOrigin === \"number\" && performance.timeOrigin > 0\n ? Math.round(performance.timeOrigin)\n : Date.now();\n const queue: KancaEvent[] = [];\n const seen = new Set<string>();\n\n const send = () => {\n if (!queue.length) return;\n const events = queue.splice(0, queue.length);\n const body = JSON.stringify({ sdk: { name: SDK_NAME, version: SDK_VERSION }, events });\n if (debug) console.debug(\"[kanca] vitals\", events);\n // fetch+keepalive first: content blockers drop sendBeacon (\"ping\") requests\n // while sendBeacon still returns true, so vitals would vanish silently.\n // A text/plain body keeps it a simple request (no CORS preflight).\n try {\n if (typeof fetch === \"function\") {\n void fetch(url, { method: \"POST\", body, keepalive: true, mode: \"cors\", credentials: \"omit\", headers: { \"content-type\": \"text/plain\" } }).catch(() => undefined);\n return;\n }\n } catch {\n /* fall through */\n }\n try {\n if (typeof navigator !== \"undefined\" && typeof navigator.sendBeacon === \"function\") navigator.sendBeacon(url, body);\n } catch {\n /* never throw */\n }\n };\n\n const onMetric = (m: Metric) => {\n try {\n if (!(VITALS as readonly string[]).includes(m.name) || seen.has(m.name)) return;\n seen.add(m.name);\n queue.push(sanitizeEvent({ id: uuid(), type: \"vital\", ts, name: m.name, value: m.value, attrs: { path, rating: m.rating } }));\n } catch {\n /* ignore */\n }\n };\n\n // Without reportAllChanges, web-vitals reports each metric's final value.\n onLCP(onMetric);\n onINP(onMetric);\n onCLS(onMetric);\n onFCP(onMetric);\n onTTFB(onMetric);\n\n // Registered after web-vitals' own listeners, so final values are queued before we send.\n addEventListener(\n \"visibilitychange\",\n () => {\n // Microtask: lets any report web-vitals triggers on this same event land first.\n if (document.visibilityState === \"hidden\") queueMicrotask(send);\n },\n true,\n );\n addEventListener(\"pagehide\", send, true);\n } catch {\n /* never break the host page */\n }\n}\n\n/** @internal for tests */\nexport function __resetVitalsForTests(): void {\n g[FLAG] = false;\n}\n","// Shared, runtime-agnostic helpers (used by both the Node and browser entries).\n// Wire format: see docs/PROTOCOL.md (v1).\n\nexport const SDK_NAME = \"@kanca-app/ikas\";\nexport const SDK_VERSION = \"0.3.0\";\nexport const DEFAULT_ENDPOINT = \"https://kanca.onurhan.dev\";\n\nexport const MAX_NAME = 200;\nexport const MAX_ERROR_MESSAGE = 500;\nexport const MAX_ERROR_CODE = 100;\nexport const MAX_ATTR_KEYS = 20;\nexport const MAX_ATTR_STRING = 500;\nexport const MAX_ATTR_KEY = 64;\nexport const MAX_EVENTS_PER_BATCH = 500;\n/** Protocol limit is 512 KB; keep headroom for the envelope. */\nexport const MAX_BATCH_BYTES = 480 * 1024;\n\nexport type EventType = \"webhook\" | \"graphql\" | \"rest\" | \"install\" | \"uninstall\" | \"vital\" | \"storefront\";\n\nexport type AttrValue = string | number | boolean;\n\nexport interface KancaError {\n code: string;\n message?: string;\n}\n\n/** A single wire event (PROTOCOL.md → Event). */\nexport interface KancaEvent {\n id: string;\n type: EventType;\n ts: number;\n name: string;\n status?: number;\n ok?: boolean;\n durationMs?: number;\n value?: number;\n error?: KancaError;\n merchantId?: string;\n deliveryId?: string;\n attrs?: Record<string, AttrValue>;\n}\n\nexport interface Batch {\n sdk: { name: string; version: string };\n events: KancaEvent[];\n}\n\nexport function truncate(s: string, max: number): string {\n return s.length > max ? s.slice(0, max) : s;\n}\n\nfunction hex(bytes: Uint8Array): string {\n let out = \"\";\n for (const b of bytes) out += b.toString(16).padStart(2, \"0\");\n return out;\n}\n\n/** UUID v4 via crypto.randomUUID, with a getRandomValues fallback (non-secure browser contexts). */\nexport function uuid(): string {\n const c = (globalThis as { crypto?: Crypto }).crypto;\n if (c && typeof c.randomUUID === \"function\") return c.randomUUID();\n const b = new Uint8Array(16);\n if (c && typeof c.getRandomValues === \"function\") c.getRandomValues(b);\n else for (let i = 0; i < 16; i++) b[i] = Math.floor(Math.random() * 256);\n b[6] = (b[6]! & 0x0f) | 0x40;\n b[8] = (b[8]! & 0x3f) | 0x80;\n const h = hex(b);\n return `${h.slice(0, 8)}-${h.slice(8, 12)}-${h.slice(12, 16)}-${h.slice(16, 20)}-${h.slice(20)}`;\n}\n\nfunction isFiniteNumber(v: unknown): v is number {\n return typeof v === \"number\" && Number.isFinite(v);\n}\n\nfunction cleanAttrs(attrs: unknown): Record<string, AttrValue> | undefined {\n if (!attrs || typeof attrs !== \"object\") return undefined;\n const out: Record<string, AttrValue> = {};\n let n = 0;\n for (const [k, v] of Object.entries(attrs as Record<string, unknown>)) {\n if (n >= MAX_ATTR_KEYS) break;\n if (v === undefined || v === null || !k) continue;\n let val: AttrValue;\n if (typeof v === \"boolean\") val = v;\n else if (typeof v === \"number\") {\n if (!Number.isFinite(v)) continue;\n val = v;\n } else if (typeof v === \"string\") val = truncate(v, MAX_ATTR_STRING);\n else continue; // attrs are flat: nested values are dropped, never stringified\n out[truncate(k, MAX_ATTR_KEY)] = val;\n n++;\n }\n return n > 0 ? out : undefined;\n}\n\n/**\n * Normalises an event so it always satisfies the protocol limits. Unknown\n * fields are dropped — only the whitelisted metadata below ever leaves the process.\n */\nexport function sanitizeEvent(e: KancaEvent): KancaEvent {\n const out: KancaEvent = {\n id: typeof e.id === \"string\" && e.id ? e.id : uuid(),\n type: e.type,\n ts: isFiniteNumber(e.ts) ? Math.round(e.ts) : Date.now(),\n name: truncate(typeof e.name === \"string\" && e.name ? e.name : \"unknown\", MAX_NAME),\n };\n if (isFiniteNumber(e.status) && e.status >= 100 && e.status <= 599) out.status = Math.trunc(e.status);\n if (typeof e.ok === \"boolean\") out.ok = e.ok;\n if (isFiniteNumber(e.durationMs)) out.durationMs = Math.min(86_400_000, Math.max(0, Math.round(e.durationMs * 10) / 10));\n if (isFiniteNumber(e.value)) out.value = e.value;\n if (e.error && typeof e.error.code === \"string\") {\n out.error = { code: truncate(e.error.code, MAX_ERROR_CODE) };\n if (typeof e.error.message === \"string\" && e.error.message) {\n out.error.message = truncate(e.error.message, MAX_ERROR_MESSAGE);\n }\n }\n if (typeof e.merchantId === \"string\" && e.merchantId) out.merchantId = truncate(e.merchantId, 200);\n if (typeof e.deliveryId === \"string\" && e.deliveryId) out.deliveryId = truncate(e.deliveryId, 200);\n const attrs = cleanAttrs(e.attrs);\n if (attrs) out.attrs = attrs;\n return out;\n}\n\nexport function errorMessage(err: unknown): string | undefined {\n try {\n if (err instanceof Error) return truncate(err.message || err.name, MAX_ERROR_MESSAGE);\n if (typeof err === \"string\") return truncate(err, MAX_ERROR_MESSAGE);\n } catch {\n /* ignore */\n }\n return undefined;\n}\n\nexport function joinUrl(endpoint: string, path: string): string {\n return endpoint.replace(/\\/+$/, \"\") + path;\n}\n\nconst encoder = typeof TextEncoder !== \"undefined\" ? /* @__PURE__ */ new TextEncoder() : undefined;\nexport function byteLength(s: string): number {\n return encoder ? encoder.encode(s).length : s.length * 3;\n}\n\n/**\n * Splits events into protocol-sized batches (<= maxEvents events and\n * <= MAX_BATCH_BYTES serialized). Returns serialized bodies.\n */\nexport function buildBodies(events: KancaEvent[], maxEvents: number): { body: string; events: KancaEvent[] }[] {\n const sdk = { name: SDK_NAME, version: SDK_VERSION };\n const envelope = byteLength(JSON.stringify({ sdk, events: [] }));\n const out: { body: string; events: KancaEvent[] }[] = [];\n let cur: KancaEvent[] = [];\n let size = envelope;\n const push = () => {\n if (cur.length) out.push({ body: JSON.stringify({ sdk, events: cur } satisfies Batch), events: cur });\n cur = [];\n size = envelope;\n };\n for (const ev of events) {\n const len = byteLength(JSON.stringify(ev)) + 1;\n if (cur.length && (cur.length >= maxEvents || size + len > MAX_BATCH_BYTES)) push();\n cur.push(ev);\n size += len;\n }\n push();\n return out;\n}\n"],"mappings":"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","names":[]}
package/dist/react.js CHANGED
@@ -8,7 +8,7 @@ import { onCLS, onFCP, onINP, onLCP, onTTFB } from "web-vitals";
8
8
 
9
9
  // src/shared.ts
10
10
  var SDK_NAME = "@kanca-app/ikas";
11
- var SDK_VERSION = "0.2.0";
11
+ var SDK_VERSION = "0.3.0";
12
12
  var DEFAULT_ENDPOINT = "https://kanca.onurhan.dev";
13
13
  var MAX_NAME = 200;
14
14
  var MAX_ERROR_MESSAGE = 500;
package/dist/react.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/react.ts","../src/browser.ts","../src/shared.ts"],"sourcesContent":["// @kanca-app/ikas/react — <KancaVitals /> client component. The build prepends \"use client\".\nimport { useEffect } from \"react\";\nimport { reportVitals, type ReportVitalsOptions } from \"./browser\";\n\nexport type KancaVitalsProps = ReportVitalsOptions;\n\n/**\n * Reports Core Web Vitals of the admin UI to Kanca. Render once, e.g. in the\n * root layout. Renders nothing.\n */\nexport function KancaVitals({ publicKey, endpoint, debug }: KancaVitalsProps): null {\n useEffect(() => {\n reportVitals({ publicKey, endpoint, debug });\n // reportVitals is idempotent; later prop changes are intentionally ignored.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n return null;\n}\n\nexport { reportVitals };\nexport type { ReportVitalsOptions };\n","// @kanca-app/ikas/browser — Core Web Vitals for the ikas admin iframe.\n// Sends only { name, value, rating, pathname } per metric. Uses a public key (kp_...).\n\nimport { onCLS, onFCP, onINP, onLCP, onTTFB, type Metric } from \"web-vitals\";\nimport { DEFAULT_ENDPOINT, SDK_NAME, SDK_VERSION, joinUrl, sanitizeEvent, uuid, type KancaEvent } from \"./shared\";\n\nexport interface ReportVitalsOptions {\n /** Public key (`kp_...`). Safe to expose in the browser; only accepts `vital` events. */\n publicKey: string | undefined;\n /** Kanca base URL. Default `https://kanca.onurhan.dev`. */\n endpoint?: string;\n debug?: boolean;\n}\n\nconst VITALS = [\"LCP\", \"INP\", \"CLS\", \"FCP\", \"TTFB\"] as const;\n\n// Global (not module) flag so duplicate copies (ESM + CJS, browser + react bundles) still start once.\nconst FLAG = \"__kancaVitalsStarted\";\nconst g = globalThis as { [FLAG]?: boolean };\n\n/**\n * Starts collecting LCP, INP, CLS, FCP and TTFB (final values, once per page\n * load per metric) and sends them when the page is hidden or unloaded.\n * Safe to call more than once — only the first call has an effect.\n */\nexport function reportVitals(options: ReportVitalsOptions): void {\n try {\n if (g[FLAG] || typeof window === \"undefined\" || typeof document === \"undefined\") return;\n const key = typeof options?.publicKey === \"string\" ? options.publicKey.trim() : \"\";\n const debug = !!options?.debug;\n if (!key.startsWith(\"kp_\")) {\n if (debug || key) console.warn(\"[kanca] reportVitals(): a public key (kp_...) is required; vitals disabled.\");\n return;\n }\n g[FLAG] = true;\n\n const url = `${joinUrl(options.endpoint || DEFAULT_ENDPOINT, \"/api/ingest/browser\")}?key=${encodeURIComponent(key)}`;\n const path = location.pathname;\n const ts =\n typeof performance !== \"undefined\" && typeof performance.timeOrigin === \"number\" && performance.timeOrigin > 0\n ? Math.round(performance.timeOrigin)\n : Date.now();\n const queue: KancaEvent[] = [];\n const seen = new Set<string>();\n\n const send = () => {\n if (!queue.length) return;\n const events = queue.splice(0, queue.length);\n const body = JSON.stringify({ sdk: { name: SDK_NAME, version: SDK_VERSION }, events });\n if (debug) console.debug(\"[kanca] vitals\", events);\n // fetch+keepalive first: content blockers drop sendBeacon (\"ping\") requests\n // while sendBeacon still returns true, so vitals would vanish silently.\n // A text/plain body keeps it a simple request (no CORS preflight).\n try {\n if (typeof fetch === \"function\") {\n void fetch(url, { method: \"POST\", body, keepalive: true, mode: \"cors\", credentials: \"omit\", headers: { \"content-type\": \"text/plain\" } }).catch(() => undefined);\n return;\n }\n } catch {\n /* fall through */\n }\n try {\n if (typeof navigator !== \"undefined\" && typeof navigator.sendBeacon === \"function\") navigator.sendBeacon(url, body);\n } catch {\n /* never throw */\n }\n };\n\n const onMetric = (m: Metric) => {\n try {\n if (!(VITALS as readonly string[]).includes(m.name) || seen.has(m.name)) return;\n seen.add(m.name);\n queue.push(sanitizeEvent({ id: uuid(), type: \"vital\", ts, name: m.name, value: m.value, attrs: { path, rating: m.rating } }));\n } catch {\n /* ignore */\n }\n };\n\n // Without reportAllChanges, web-vitals reports each metric's final value.\n onLCP(onMetric);\n onINP(onMetric);\n onCLS(onMetric);\n onFCP(onMetric);\n onTTFB(onMetric);\n\n // Registered after web-vitals' own listeners, so final values are queued before we send.\n addEventListener(\n \"visibilitychange\",\n () => {\n // Microtask: lets any report web-vitals triggers on this same event land first.\n if (document.visibilityState === \"hidden\") queueMicrotask(send);\n },\n true,\n );\n addEventListener(\"pagehide\", send, true);\n } catch {\n /* never break the host page */\n }\n}\n\n/** @internal for tests */\nexport function __resetVitalsForTests(): void {\n g[FLAG] = false;\n}\n","// Shared, runtime-agnostic helpers (used by both the Node and browser entries).\n// Wire format: see docs/PROTOCOL.md (v1).\n\nexport const SDK_NAME = \"@kanca-app/ikas\";\nexport const SDK_VERSION = \"0.2.0\";\nexport const DEFAULT_ENDPOINT = \"https://kanca.onurhan.dev\";\n\nexport const MAX_NAME = 200;\nexport const MAX_ERROR_MESSAGE = 500;\nexport const MAX_ERROR_CODE = 100;\nexport const MAX_ATTR_KEYS = 20;\nexport const MAX_ATTR_STRING = 500;\nexport const MAX_ATTR_KEY = 64;\nexport const MAX_EVENTS_PER_BATCH = 500;\n/** Protocol limit is 512 KB; keep headroom for the envelope. */\nexport const MAX_BATCH_BYTES = 480 * 1024;\n\nexport type EventType = \"webhook\" | \"graphql\" | \"rest\" | \"install\" | \"uninstall\" | \"vital\" | \"storefront\";\n\nexport type AttrValue = string | number | boolean;\n\nexport interface KancaError {\n code: string;\n message?: string;\n}\n\n/** A single wire event (PROTOCOL.md → Event). */\nexport interface KancaEvent {\n id: string;\n type: EventType;\n ts: number;\n name: string;\n status?: number;\n ok?: boolean;\n durationMs?: number;\n value?: number;\n error?: KancaError;\n merchantId?: string;\n deliveryId?: string;\n attrs?: Record<string, AttrValue>;\n}\n\nexport interface Batch {\n sdk: { name: string; version: string };\n events: KancaEvent[];\n}\n\nexport function truncate(s: string, max: number): string {\n return s.length > max ? s.slice(0, max) : s;\n}\n\nfunction hex(bytes: Uint8Array): string {\n let out = \"\";\n for (const b of bytes) out += b.toString(16).padStart(2, \"0\");\n return out;\n}\n\n/** UUID v4 via crypto.randomUUID, with a getRandomValues fallback (non-secure browser contexts). */\nexport function uuid(): string {\n const c = (globalThis as { crypto?: Crypto }).crypto;\n if (c && typeof c.randomUUID === \"function\") return c.randomUUID();\n const b = new Uint8Array(16);\n if (c && typeof c.getRandomValues === \"function\") c.getRandomValues(b);\n else for (let i = 0; i < 16; i++) b[i] = Math.floor(Math.random() * 256);\n b[6] = (b[6]! & 0x0f) | 0x40;\n b[8] = (b[8]! & 0x3f) | 0x80;\n const h = hex(b);\n return `${h.slice(0, 8)}-${h.slice(8, 12)}-${h.slice(12, 16)}-${h.slice(16, 20)}-${h.slice(20)}`;\n}\n\nfunction isFiniteNumber(v: unknown): v is number {\n return typeof v === \"number\" && Number.isFinite(v);\n}\n\nfunction cleanAttrs(attrs: unknown): Record<string, AttrValue> | undefined {\n if (!attrs || typeof attrs !== \"object\") return undefined;\n const out: Record<string, AttrValue> = {};\n let n = 0;\n for (const [k, v] of Object.entries(attrs as Record<string, unknown>)) {\n if (n >= MAX_ATTR_KEYS) break;\n if (v === undefined || v === null || !k) continue;\n let val: AttrValue;\n if (typeof v === \"boolean\") val = v;\n else if (typeof v === \"number\") {\n if (!Number.isFinite(v)) continue;\n val = v;\n } else if (typeof v === \"string\") val = truncate(v, MAX_ATTR_STRING);\n else continue; // attrs are flat: nested values are dropped, never stringified\n out[truncate(k, MAX_ATTR_KEY)] = val;\n n++;\n }\n return n > 0 ? out : undefined;\n}\n\n/**\n * Normalises an event so it always satisfies the protocol limits. Unknown\n * fields are dropped — only the whitelisted metadata below ever leaves the process.\n */\nexport function sanitizeEvent(e: KancaEvent): KancaEvent {\n const out: KancaEvent = {\n id: typeof e.id === \"string\" && e.id ? e.id : uuid(),\n type: e.type,\n ts: isFiniteNumber(e.ts) ? Math.round(e.ts) : Date.now(),\n name: truncate(typeof e.name === \"string\" && e.name ? e.name : \"unknown\", MAX_NAME),\n };\n if (isFiniteNumber(e.status) && e.status >= 100 && e.status <= 599) out.status = Math.trunc(e.status);\n if (typeof e.ok === \"boolean\") out.ok = e.ok;\n if (isFiniteNumber(e.durationMs)) out.durationMs = Math.min(86_400_000, Math.max(0, Math.round(e.durationMs * 10) / 10));\n if (isFiniteNumber(e.value)) out.value = e.value;\n if (e.error && typeof e.error.code === \"string\") {\n out.error = { code: truncate(e.error.code, MAX_ERROR_CODE) };\n if (typeof e.error.message === \"string\" && e.error.message) {\n out.error.message = truncate(e.error.message, MAX_ERROR_MESSAGE);\n }\n }\n if (typeof e.merchantId === \"string\" && e.merchantId) out.merchantId = truncate(e.merchantId, 200);\n if (typeof e.deliveryId === \"string\" && e.deliveryId) out.deliveryId = truncate(e.deliveryId, 200);\n const attrs = cleanAttrs(e.attrs);\n if (attrs) out.attrs = attrs;\n return out;\n}\n\nexport function errorMessage(err: unknown): string | undefined {\n try {\n if (err instanceof Error) return truncate(err.message || err.name, MAX_ERROR_MESSAGE);\n if (typeof err === \"string\") return truncate(err, MAX_ERROR_MESSAGE);\n } catch {\n /* ignore */\n }\n return undefined;\n}\n\nexport function joinUrl(endpoint: string, path: string): string {\n return endpoint.replace(/\\/+$/, \"\") + path;\n}\n\nconst encoder = typeof TextEncoder !== \"undefined\" ? /* @__PURE__ */ new TextEncoder() : undefined;\nexport function byteLength(s: string): number {\n return encoder ? encoder.encode(s).length : s.length * 3;\n}\n\n/**\n * Splits events into protocol-sized batches (<= maxEvents events and\n * <= MAX_BATCH_BYTES serialized). Returns serialized bodies.\n */\nexport function buildBodies(events: KancaEvent[], maxEvents: number): { body: string; events: KancaEvent[] }[] {\n const sdk = { name: SDK_NAME, version: SDK_VERSION };\n const envelope = byteLength(JSON.stringify({ sdk, events: [] }));\n const out: { body: string; events: KancaEvent[] }[] = [];\n let cur: KancaEvent[] = [];\n let size = envelope;\n const push = () => {\n if (cur.length) out.push({ body: JSON.stringify({ sdk, events: cur } satisfies Batch), events: cur });\n cur = [];\n size = envelope;\n };\n for (const ev of events) {\n const len = byteLength(JSON.stringify(ev)) + 1;\n if (cur.length && (cur.length >= maxEvents || size + len > MAX_BATCH_BYTES)) push();\n cur.push(ev);\n size += len;\n }\n push();\n return out;\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/react.ts","../src/browser.ts","../src/shared.ts"],"sourcesContent":["// @kanca-app/ikas/react — <KancaVitals /> client component. The build prepends \"use client\".\nimport { useEffect } from \"react\";\nimport { reportVitals, type ReportVitalsOptions } from \"./browser\";\n\nexport type KancaVitalsProps = ReportVitalsOptions;\n\n/**\n * Reports Core Web Vitals of the admin UI to Kanca. Render once, e.g. in the\n * root layout. Renders nothing.\n */\nexport function KancaVitals({ publicKey, endpoint, debug }: KancaVitalsProps): null {\n useEffect(() => {\n reportVitals({ publicKey, endpoint, debug });\n // reportVitals is idempotent; later prop changes are intentionally ignored.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n return null;\n}\n\nexport { reportVitals };\nexport type { ReportVitalsOptions };\n","// @kanca-app/ikas/browser — Core Web Vitals for the ikas admin iframe.\n// Sends only { name, value, rating, pathname } per metric. Uses a public key (kp_...).\n\nimport { onCLS, onFCP, onINP, onLCP, onTTFB, type Metric } from \"web-vitals\";\nimport { DEFAULT_ENDPOINT, SDK_NAME, SDK_VERSION, joinUrl, sanitizeEvent, uuid, type KancaEvent } from \"./shared\";\n\nexport interface ReportVitalsOptions {\n /** Public key (`kp_...`). Safe to expose in the browser; only accepts `vital` events. */\n publicKey: string | undefined;\n /** Kanca base URL. Default `https://kanca.onurhan.dev`. */\n endpoint?: string;\n debug?: boolean;\n}\n\nconst VITALS = [\"LCP\", \"INP\", \"CLS\", \"FCP\", \"TTFB\"] as const;\n\n// Global (not module) flag so duplicate copies (ESM + CJS, browser + react bundles) still start once.\nconst FLAG = \"__kancaVitalsStarted\";\nconst g = globalThis as { [FLAG]?: boolean };\n\n/**\n * Starts collecting LCP, INP, CLS, FCP and TTFB (final values, once per page\n * load per metric) and sends them when the page is hidden or unloaded.\n * Safe to call more than once — only the first call has an effect.\n */\nexport function reportVitals(options: ReportVitalsOptions): void {\n try {\n if (g[FLAG] || typeof window === \"undefined\" || typeof document === \"undefined\") return;\n const key = typeof options?.publicKey === \"string\" ? options.publicKey.trim() : \"\";\n const debug = !!options?.debug;\n if (!key.startsWith(\"kp_\")) {\n if (debug || key) console.warn(\"[kanca] reportVitals(): a public key (kp_...) is required; vitals disabled.\");\n return;\n }\n g[FLAG] = true;\n\n const url = `${joinUrl(options.endpoint || DEFAULT_ENDPOINT, \"/api/ingest/browser\")}?key=${encodeURIComponent(key)}`;\n const path = location.pathname;\n const ts =\n typeof performance !== \"undefined\" && typeof performance.timeOrigin === \"number\" && performance.timeOrigin > 0\n ? Math.round(performance.timeOrigin)\n : Date.now();\n const queue: KancaEvent[] = [];\n const seen = new Set<string>();\n\n const send = () => {\n if (!queue.length) return;\n const events = queue.splice(0, queue.length);\n const body = JSON.stringify({ sdk: { name: SDK_NAME, version: SDK_VERSION }, events });\n if (debug) console.debug(\"[kanca] vitals\", events);\n // fetch+keepalive first: content blockers drop sendBeacon (\"ping\") requests\n // while sendBeacon still returns true, so vitals would vanish silently.\n // A text/plain body keeps it a simple request (no CORS preflight).\n try {\n if (typeof fetch === \"function\") {\n void fetch(url, { method: \"POST\", body, keepalive: true, mode: \"cors\", credentials: \"omit\", headers: { \"content-type\": \"text/plain\" } }).catch(() => undefined);\n return;\n }\n } catch {\n /* fall through */\n }\n try {\n if (typeof navigator !== \"undefined\" && typeof navigator.sendBeacon === \"function\") navigator.sendBeacon(url, body);\n } catch {\n /* never throw */\n }\n };\n\n const onMetric = (m: Metric) => {\n try {\n if (!(VITALS as readonly string[]).includes(m.name) || seen.has(m.name)) return;\n seen.add(m.name);\n queue.push(sanitizeEvent({ id: uuid(), type: \"vital\", ts, name: m.name, value: m.value, attrs: { path, rating: m.rating } }));\n } catch {\n /* ignore */\n }\n };\n\n // Without reportAllChanges, web-vitals reports each metric's final value.\n onLCP(onMetric);\n onINP(onMetric);\n onCLS(onMetric);\n onFCP(onMetric);\n onTTFB(onMetric);\n\n // Registered after web-vitals' own listeners, so final values are queued before we send.\n addEventListener(\n \"visibilitychange\",\n () => {\n // Microtask: lets any report web-vitals triggers on this same event land first.\n if (document.visibilityState === \"hidden\") queueMicrotask(send);\n },\n true,\n );\n addEventListener(\"pagehide\", send, true);\n } catch {\n /* never break the host page */\n }\n}\n\n/** @internal for tests */\nexport function __resetVitalsForTests(): void {\n g[FLAG] = false;\n}\n","// Shared, runtime-agnostic helpers (used by both the Node and browser entries).\n// Wire format: see docs/PROTOCOL.md (v1).\n\nexport const SDK_NAME = \"@kanca-app/ikas\";\nexport const SDK_VERSION = \"0.3.0\";\nexport const DEFAULT_ENDPOINT = \"https://kanca.onurhan.dev\";\n\nexport const MAX_NAME = 200;\nexport const MAX_ERROR_MESSAGE = 500;\nexport const MAX_ERROR_CODE = 100;\nexport const MAX_ATTR_KEYS = 20;\nexport const MAX_ATTR_STRING = 500;\nexport const MAX_ATTR_KEY = 64;\nexport const MAX_EVENTS_PER_BATCH = 500;\n/** Protocol limit is 512 KB; keep headroom for the envelope. */\nexport const MAX_BATCH_BYTES = 480 * 1024;\n\nexport type EventType = \"webhook\" | \"graphql\" | \"rest\" | \"install\" | \"uninstall\" | \"vital\" | \"storefront\";\n\nexport type AttrValue = string | number | boolean;\n\nexport interface KancaError {\n code: string;\n message?: string;\n}\n\n/** A single wire event (PROTOCOL.md → Event). */\nexport interface KancaEvent {\n id: string;\n type: EventType;\n ts: number;\n name: string;\n status?: number;\n ok?: boolean;\n durationMs?: number;\n value?: number;\n error?: KancaError;\n merchantId?: string;\n deliveryId?: string;\n attrs?: Record<string, AttrValue>;\n}\n\nexport interface Batch {\n sdk: { name: string; version: string };\n events: KancaEvent[];\n}\n\nexport function truncate(s: string, max: number): string {\n return s.length > max ? s.slice(0, max) : s;\n}\n\nfunction hex(bytes: Uint8Array): string {\n let out = \"\";\n for (const b of bytes) out += b.toString(16).padStart(2, \"0\");\n return out;\n}\n\n/** UUID v4 via crypto.randomUUID, with a getRandomValues fallback (non-secure browser contexts). */\nexport function uuid(): string {\n const c = (globalThis as { crypto?: Crypto }).crypto;\n if (c && typeof c.randomUUID === \"function\") return c.randomUUID();\n const b = new Uint8Array(16);\n if (c && typeof c.getRandomValues === \"function\") c.getRandomValues(b);\n else for (let i = 0; i < 16; i++) b[i] = Math.floor(Math.random() * 256);\n b[6] = (b[6]! & 0x0f) | 0x40;\n b[8] = (b[8]! & 0x3f) | 0x80;\n const h = hex(b);\n return `${h.slice(0, 8)}-${h.slice(8, 12)}-${h.slice(12, 16)}-${h.slice(16, 20)}-${h.slice(20)}`;\n}\n\nfunction isFiniteNumber(v: unknown): v is number {\n return typeof v === \"number\" && Number.isFinite(v);\n}\n\nfunction cleanAttrs(attrs: unknown): Record<string, AttrValue> | undefined {\n if (!attrs || typeof attrs !== \"object\") return undefined;\n const out: Record<string, AttrValue> = {};\n let n = 0;\n for (const [k, v] of Object.entries(attrs as Record<string, unknown>)) {\n if (n >= MAX_ATTR_KEYS) break;\n if (v === undefined || v === null || !k) continue;\n let val: AttrValue;\n if (typeof v === \"boolean\") val = v;\n else if (typeof v === \"number\") {\n if (!Number.isFinite(v)) continue;\n val = v;\n } else if (typeof v === \"string\") val = truncate(v, MAX_ATTR_STRING);\n else continue; // attrs are flat: nested values are dropped, never stringified\n out[truncate(k, MAX_ATTR_KEY)] = val;\n n++;\n }\n return n > 0 ? out : undefined;\n}\n\n/**\n * Normalises an event so it always satisfies the protocol limits. Unknown\n * fields are dropped — only the whitelisted metadata below ever leaves the process.\n */\nexport function sanitizeEvent(e: KancaEvent): KancaEvent {\n const out: KancaEvent = {\n id: typeof e.id === \"string\" && e.id ? e.id : uuid(),\n type: e.type,\n ts: isFiniteNumber(e.ts) ? Math.round(e.ts) : Date.now(),\n name: truncate(typeof e.name === \"string\" && e.name ? e.name : \"unknown\", MAX_NAME),\n };\n if (isFiniteNumber(e.status) && e.status >= 100 && e.status <= 599) out.status = Math.trunc(e.status);\n if (typeof e.ok === \"boolean\") out.ok = e.ok;\n if (isFiniteNumber(e.durationMs)) out.durationMs = Math.min(86_400_000, Math.max(0, Math.round(e.durationMs * 10) / 10));\n if (isFiniteNumber(e.value)) out.value = e.value;\n if (e.error && typeof e.error.code === \"string\") {\n out.error = { code: truncate(e.error.code, MAX_ERROR_CODE) };\n if (typeof e.error.message === \"string\" && e.error.message) {\n out.error.message = truncate(e.error.message, MAX_ERROR_MESSAGE);\n }\n }\n if (typeof e.merchantId === \"string\" && e.merchantId) out.merchantId = truncate(e.merchantId, 200);\n if (typeof e.deliveryId === \"string\" && e.deliveryId) out.deliveryId = truncate(e.deliveryId, 200);\n const attrs = cleanAttrs(e.attrs);\n if (attrs) out.attrs = attrs;\n return out;\n}\n\nexport function errorMessage(err: unknown): string | undefined {\n try {\n if (err instanceof Error) return truncate(err.message || err.name, MAX_ERROR_MESSAGE);\n if (typeof err === \"string\") return truncate(err, MAX_ERROR_MESSAGE);\n } catch {\n /* ignore */\n }\n return undefined;\n}\n\nexport function joinUrl(endpoint: string, path: string): string {\n return endpoint.replace(/\\/+$/, \"\") + path;\n}\n\nconst encoder = typeof TextEncoder !== \"undefined\" ? /* @__PURE__ */ new TextEncoder() : undefined;\nexport function byteLength(s: string): number {\n return encoder ? encoder.encode(s).length : s.length * 3;\n}\n\n/**\n * Splits events into protocol-sized batches (<= maxEvents events and\n * <= MAX_BATCH_BYTES serialized). Returns serialized bodies.\n */\nexport function buildBodies(events: KancaEvent[], maxEvents: number): { body: string; events: KancaEvent[] }[] {\n const sdk = { name: SDK_NAME, version: SDK_VERSION };\n const envelope = byteLength(JSON.stringify({ sdk, events: [] }));\n const out: { body: string; events: KancaEvent[] }[] = [];\n let cur: KancaEvent[] = [];\n let size = envelope;\n const push = () => {\n if (cur.length) out.push({ body: JSON.stringify({ sdk, events: cur } satisfies Batch), events: cur });\n cur = [];\n size = envelope;\n };\n for (const ev of events) {\n const len = byteLength(JSON.stringify(ev)) + 1;\n if (cur.length && (cur.length >= maxEvents || size + len > MAX_BATCH_BYTES)) push();\n cur.push(ev);\n size += len;\n }\n push();\n return out;\n}\n"],"mappings":"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","names":[]}
@@ -34,7 +34,7 @@ module.exports = __toCommonJS(storefront_exports);
34
34
 
35
35
  // src/shared.ts
36
36
  var SDK_NAME = "@kanca-app/ikas";
37
- var SDK_VERSION = "0.2.0";
37
+ var SDK_VERSION = "0.3.0";
38
38
  var DEFAULT_ENDPOINT = "https://kanca.onurhan.dev";
39
39
  var MAX_BATCH_BYTES = 480 * 1024;
40
40
  function hex(bytes) {