@hexclave/react 1.0.29 → 1.0.30

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.
Files changed (26) hide show
  1. package/dist/esm/lib/hexclave-app/apps/implementations/common.js +1 -1
  2. package/dist/esm/lib/hexclave-app/apps/implementations/server-app-impl.d.ts +15 -1
  3. package/dist/esm/lib/hexclave-app/apps/implementations/server-app-impl.d.ts.map +1 -1
  4. package/dist/esm/lib/hexclave-app/apps/implementations/server-app-impl.js +20 -12
  5. package/dist/esm/lib/hexclave-app/apps/implementations/server-app-impl.js.map +1 -1
  6. package/dist/esm/lib/hexclave-app/apps/implementations/session-replay.d.ts.map +1 -1
  7. package/dist/esm/lib/hexclave-app/apps/implementations/session-replay.js +8 -1
  8. package/dist/esm/lib/hexclave-app/apps/implementations/session-replay.js.map +1 -1
  9. package/dist/esm/lib/hexclave-app/apps/interfaces/server-app.d.ts +13 -0
  10. package/dist/esm/lib/hexclave-app/apps/interfaces/server-app.d.ts.map +1 -1
  11. package/dist/esm/lib/hexclave-app/apps/interfaces/server-app.js.map +1 -1
  12. package/dist/lib/hexclave-app/apps/implementations/common.js +1 -1
  13. package/dist/lib/hexclave-app/apps/implementations/server-app-impl.d.ts +15 -1
  14. package/dist/lib/hexclave-app/apps/implementations/server-app-impl.d.ts.map +1 -1
  15. package/dist/lib/hexclave-app/apps/implementations/server-app-impl.js +20 -12
  16. package/dist/lib/hexclave-app/apps/implementations/server-app-impl.js.map +1 -1
  17. package/dist/lib/hexclave-app/apps/implementations/session-replay.d.ts.map +1 -1
  18. package/dist/lib/hexclave-app/apps/implementations/session-replay.js +8 -1
  19. package/dist/lib/hexclave-app/apps/implementations/session-replay.js.map +1 -1
  20. package/dist/lib/hexclave-app/apps/interfaces/server-app.d.ts +13 -0
  21. package/dist/lib/hexclave-app/apps/interfaces/server-app.d.ts.map +1 -1
  22. package/dist/lib/hexclave-app/apps/interfaces/server-app.js.map +1 -1
  23. package/package.json +3 -3
  24. package/src/lib/hexclave-app/apps/implementations/server-app-impl.ts +24 -12
  25. package/src/lib/hexclave-app/apps/implementations/session-replay.ts +16 -1
  26. package/src/lib/hexclave-app/apps/interfaces/server-app.ts +5 -0
@@ -60,6 +60,7 @@ const FLUSH_INTERVAL_MS = 5e3;
60
60
  const MAX_EVENTS_PER_BATCH = 200;
61
61
  const MAX_APPROX_BYTES_PER_BATCH = 512e3;
62
62
  const MAX_FLUSH_PAYLOAD_BYTES = 9e5;
63
+ const textEncoder = new TextEncoder();
63
64
  function safeParseStoredSession(raw) {
64
65
  if (!raw) return null;
65
66
  try {
@@ -186,6 +187,12 @@ var SessionRecorder = class {
186
187
  try {
187
188
  let offset = 0;
188
189
  while (offset < allEvents.length) {
190
+ const firstSize = allSizes[offset] ?? (0, _hexclave_shared_dist_utils_errors.throwErr)("_eventSizes out of sync with _events — this should never happen");
191
+ if (firstSize > MAX_FLUSH_PAYLOAD_BYTES) {
192
+ (0, _hexclave_shared_dist_utils_errors.captureWarning)("SessionRecorder.flush", /* @__PURE__ */ new Error(`Dropping oversized session replay event (${firstSize} bytes > ${MAX_FLUSH_PAYLOAD_BYTES} byte limit); it cannot be sent without a 413.`));
193
+ offset += 1;
194
+ continue;
195
+ }
189
196
  let batchBytes = 0;
190
197
  let batchEnd = offset;
191
198
  for (let i = offset; i < allEvents.length; i++) {
@@ -258,7 +265,7 @@ var SessionRecorder = class {
258
265
  this._takingSnapshot = false;
259
266
  }
260
267
  }
261
- const eventSize = JSON.stringify(event).length;
268
+ const eventSize = textEncoder.encode(JSON.stringify(event)).byteLength;
262
269
  this._events.push(event);
263
270
  this._eventSizes.push(eventSize);
264
271
  this._approxBytes += eventSize;
@@ -1 +1 @@
1
- {"version":3,"file":"session-replay.js","names":["KnownErrors","Result"],"sources":["../../../../../src/lib/hexclave-app/apps/implementations/session-replay.ts"],"sourcesContent":["\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY UNLESS YOU ALSO EDIT THE CORRESPONDING FILE IN packages/template\n//===========================================\nimport { KnownErrors } from \"@hexclave/shared/dist/known-errors\";\nimport { isBrowserLike } from \"@hexclave/shared/dist/utils/env\";\nimport { captureWarning, throwErr } from \"@hexclave/shared/dist/utils/errors\";\nimport { runAsynchronously } from \"@hexclave/shared/dist/utils/promises\";\nimport { Result } from \"@hexclave/shared/dist/utils/results\";\n\nexport type AnalyticsReplayOptions = {\n /**\n * Whether session replays are enabled.\n *\n * @default true\n */\n enabled?: boolean,\n /**\n * Whether to mask the content of all `<input>` elements.\n *\n * @default true\n */\n maskAllInputs?: boolean,\n /**\n * A CSS class name or RegExp. Elements with a matching class will be blocked\n * (replaced with a placeholder in the recording).\n *\n * @default undefined\n */\n blockClass?: string | RegExp,\n /**\n * A CSS selector string. Elements matching this selector will be blocked\n * (replaced with a placeholder in the recording).\n *\n * @default undefined\n */\n blockSelector?: string,\n};\n\nexport type AnalyticsOptions = {\n /**\n * Whether SDK-managed analytics capture is enabled.\n *\n * @default true\n */\n enabled?: boolean,\n /**\n * Options for session replay recording. Replays are enabled by default;\n * set `enabled: false` to opt out.\n */\n replays?: AnalyticsReplayOptions,\n};\n\nexport function getSessionReplayOptions(analyticsOptions: AnalyticsOptions | undefined): AnalyticsReplayOptions {\n return {\n ...analyticsOptions?.replays,\n enabled: analyticsOptions?.replays?.enabled ?? true,\n };\n}\n\n/**\n * Converts AnalyticsOptions to a JSON-safe representation.\n * RegExp blockClass values are serialized as `{ __regexp, __flags }` objects.\n * The return type is AnalyticsOptions to keep StackClientAppJson simple;\n * the actual runtime value is JSON-safe.\n */\nexport function analyticsOptionsToJson(options: AnalyticsOptions | undefined): AnalyticsOptions | undefined {\n if (!options?.replays?.blockClass) return options;\n const { blockClass, ...rest } = options.replays;\n if (!(blockClass instanceof RegExp)) return options;\n return {\n ...options,\n replays: {\n ...rest,\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n blockClass: { __regexp: blockClass.source, __flags: blockClass.flags } as any,\n },\n };\n}\n\n/**\n * Reconstructs AnalyticsOptions from a JSON-deserialized value.\n * Converts `{ __regexp, __flags }` objects back to RegExp instances.\n */\nexport function analyticsOptionsFromJson(json: AnalyticsOptions | undefined): AnalyticsOptions | undefined {\n if (!json?.replays?.blockClass) return json;\n const { blockClass, ...rest } = json.replays;\n if (typeof blockClass === 'object' && '__regexp' in blockClass) {\n const bc = blockClass as unknown as { __regexp: string, __flags: string };\n return {\n ...json,\n replays: {\n ...rest,\n blockClass: new RegExp(bc.__regexp, bc.__flags),\n },\n };\n }\n return json;\n}\n\n// ---------- Recording internals ----------\n\n// Hexclave rebrand: canonical localStorage prefix (colon delimiters preserved).\nconst LOCAL_STORAGE_PREFIX = \"hexclave:session-replay:v1\";\n// Hexclave rebrand: legacy prefix — dual-read only, so a recording session active\n// across an SDK upgrade is not orphaned. Never written.\nconst LEGACY_LOCAL_STORAGE_PREFIX = \"stack:session-replay:v1\";\nconst IDLE_TTL_MS = 3 * 60 * 1000;\n\nconst FLUSH_INTERVAL_MS = 5_000;\nconst MAX_EVENTS_PER_BATCH = 200;\nconst MAX_APPROX_BYTES_PER_BATCH = 512_000;\n// The server rejects payloads > 1MB. Stay well under to account for JSON\n// envelope overhead (browser_session_id, timestamps, wrapper keys, etc.).\nconst MAX_FLUSH_PAYLOAD_BYTES = 900_000;\n\nexport type StoredSession = {\n session_id: string,\n created_at_ms: number,\n last_activity_ms: number,\n};\n\nexport function safeParseStoredSession(raw: string | null): StoredSession | null {\n if (!raw) return null;\n try {\n const parsed = JSON.parse(raw);\n if (typeof parsed !== \"object\" || parsed === null) return null;\n if (typeof parsed.session_id !== \"string\") return null;\n if (typeof parsed.created_at_ms !== \"number\") return null;\n if (typeof parsed.last_activity_ms !== \"number\") return null;\n return parsed as StoredSession;\n } catch {\n return null;\n }\n}\n\nexport function makeStorageKey(projectId: string) {\n return `${LOCAL_STORAGE_PREFIX}:${projectId}`;\n}\n\n// Hexclave rebrand: legacy key, dual-read only (never written).\nexport function makeLegacyStorageKey(projectId: string) {\n return `${LEGACY_LOCAL_STORAGE_PREFIX}:${projectId}`;\n}\n\nexport function generateUuid() {\n return crypto.randomUUID();\n}\n\nexport function getOrRotateSession(options: { key: string, legacyKey?: string, nowMs: number }): StoredSession {\n // Hexclave rebrand: prefer the new key; fall back to the legacy key so a\n // recording session active across an SDK upgrade is not orphaned.\n const existing = safeParseStoredSession(localStorage.getItem(options.key))\n ?? (options.legacyKey ? safeParseStoredSession(localStorage.getItem(options.legacyKey)) : null);\n if (existing && options.nowMs - existing.last_activity_ms <= IDLE_TTL_MS) {\n return existing;\n }\n const next: StoredSession = {\n session_id: generateUuid(),\n created_at_ms: options.nowMs,\n last_activity_ms: options.nowMs,\n };\n localStorage.setItem(options.key, JSON.stringify(next));\n return next;\n}\n\nexport type SessionRecorderDeps = {\n projectId: string,\n sendBatch: (body: string, options: { keepalive: boolean }) => Promise<Result<Response, Error>>,\n};\n\nexport function isAnalyticsNotEnabledError(error: unknown): boolean {\n return KnownErrors.AnalyticsNotEnabled.isInstance(error);\n}\n\n/**\n * Whether the error looks like a network failure caused by an ad blocker or\n * similar extension blocking analytics requests. These are expected in\n * production and should be silently ignored rather than logged as warnings.\n */\nexport function isAdBlockerNetworkError(error: unknown): boolean {\n if (error instanceof Error) {\n return error.message.includes(\"Failed to fetch\")\n || error.message.includes(\"NetworkError\")\n || error.message.includes(\"Load failed\")\n || error.message.includes(\"network connection\");\n }\n return false;\n}\n\nexport class SessionRecorder {\n private _started = false;\n private _cancelled = false;\n private _disabled = false;\n private _stopRecording: (() => void) | null = null;\n private _detachListeners: (() => void) | null = null;\n private _flushTimer: ReturnType<typeof setInterval> | null = null;\n private _events: unknown[] = [];\n private _eventSizes: number[] = [];\n private _approxBytes = 0;\n private _lastPersistActivity = 0;\n private _recording = false;\n private _rrwebModule: typeof import(\"rrweb\") | null = null;\n private _lastBrowserSessionId: string | null = null;\n private _takingSnapshot = false;\n private _flushInProgress = false;\n private readonly _sessionReplaySegmentId: string;\n private readonly _storageKey: string;\n // Hexclave rebrand: legacy key used for dual-read fallback only.\n private readonly _legacyStorageKey: string;\n private readonly _deps: SessionRecorderDeps;\n private readonly _replayOptions: AnalyticsReplayOptions;\n\n constructor(deps: SessionRecorderDeps, replayOptions: AnalyticsReplayOptions) {\n this._deps = deps;\n this._replayOptions = replayOptions;\n this._sessionReplaySegmentId = generateUuid();\n this._storageKey = makeStorageKey(deps.projectId);\n this._legacyStorageKey = makeLegacyStorageKey(deps.projectId);\n }\n\n /**\n * Starts recording. Idempotent — calling multiple times is safe.\n */\n start() {\n if (this._started) return;\n if (!isBrowserLike()) return;\n this._started = true;\n\n // Kick off rrweb recording\n runAsynchronously(() => this._startRecording(), { noErrorLogging: true });\n\n // Periodic flush\n this._flushTimer = setInterval(() => this._tick(), FLUSH_INTERVAL_MS);\n }\n\n stop() {\n this._cancelled = true;\n if (this._flushTimer !== null) {\n clearInterval(this._flushTimer);\n this._flushTimer = null;\n }\n // Flush remaining events before cleanup\n runAsynchronously(() => this._flush({ keepalive: true }));\n this._stopCurrentRecording();\n }\n\n clearBuffer() {\n this._events = [];\n this._eventSizes = [];\n this._approxBytes = 0;\n }\n\n private _persistActivity(nowMs: number): StoredSession {\n const stored = getOrRotateSession({ key: this._storageKey, legacyKey: this._legacyStorageKey, nowMs });\n if (nowMs - this._lastPersistActivity < 5_000) return stored;\n this._lastPersistActivity = nowMs;\n const updated: StoredSession = { ...stored, last_activity_ms: nowMs };\n localStorage.setItem(this._storageKey, JSON.stringify(updated));\n return stored;\n }\n\n private async _flush(options: { keepalive: boolean }) {\n if (this._disabled) return;\n if (this._events.length === 0) return;\n // Prevent concurrent in-flight HTTP requests. When a flush is already\n // in-flight, a second batch could race on the server (both call\n // findRecentSessionReplay before either upsert commits) and create\n // duplicate SessionReplay records. Events stay in _events and will be\n // picked up by the next tick or batch-size check.\n if (this._flushInProgress) return;\n\n const nowMs = Date.now();\n const stored = getOrRotateSession({ key: this._storageKey, legacyKey: this._legacyStorageKey, nowMs });\n\n // Capture all buffered events upfront (before any await) so that\n // stop() / _stopCurrentRecording() clearing this._events cannot race\n // with the async send loop below and silently discard overflow batches.\n const allEvents = this._events;\n const allSizes = this._eventSizes;\n this._events = [];\n this._eventSizes = [];\n this._approxBytes = 0;\n\n this._flushInProgress = true;\n try {\n let offset = 0;\n while (offset < allEvents.length) {\n // Build a batch that fits under the server's payload limit.\n // When _flushInProgress blocked earlier flushes, events can accumulate\n // well past MAX_APPROX_BYTES_PER_BATCH; sending them all at once would\n // exceed the server's 1MB body limit (413).\n let batchBytes = 0;\n let batchEnd = offset;\n for (let i = offset; i < allEvents.length; i++) {\n const nextSize = allSizes[i] ?? throwErr(\"_eventSizes out of sync with _events — this should never happen\");\n if (batchBytes + nextSize > MAX_FLUSH_PAYLOAD_BYTES && batchEnd > offset) break;\n batchBytes += nextSize;\n batchEnd = i + 1;\n }\n\n const batchEvents = allEvents.slice(offset, batchEnd);\n offset = batchEnd;\n\n const batchId = generateUuid();\n const payload = {\n browser_session_id: stored.session_id,\n session_replay_segment_id: this._sessionReplaySegmentId,\n batch_id: batchId,\n started_at_ms: stored.created_at_ms,\n sent_at_ms: nowMs,\n events: batchEvents,\n };\n\n const res = await this._deps.sendBatch(\n JSON.stringify(payload),\n { keepalive: options.keepalive },\n );\n\n if (res.status === \"error\") {\n if (isAnalyticsNotEnabledError(res.error)) {\n this._disable();\n return;\n }\n // Ad blockers commonly block analytics endpoints, causing network\n // errors. These are expected and should not pollute the console.\n if (isAdBlockerNetworkError(res.error)) {\n return;\n }\n captureWarning(\"SessionRecorder.flush\", res.error);\n return;\n }\n\n if (!res.data.ok) {\n captureWarning(\"SessionRecorder.flush\", new Error(`SessionRecorder flush failed: ${res.data.status} ${await res.data.text()}`));\n return;\n }\n }\n } finally {\n this._flushInProgress = false;\n }\n }\n\n private _disable() {\n this._disabled = true;\n this.clearBuffer();\n if (this._flushTimer !== null) {\n clearInterval(this._flushTimer);\n this._flushTimer = null;\n }\n this._stopCurrentRecording();\n }\n\n private async _startRecording() {\n if (this._recording || this._cancelled) return;\n\n if (!this._rrwebModule) {\n const rrwebImport = await Result.fromPromise(import(\"rrweb\"));\n if (rrwebImport.status === \"error\") {\n console.warn(\"SessionRecorder: rrweb import failed. Is rrweb installed?\", rrwebImport.error);\n return;\n }\n this._rrwebModule = rrwebImport.data;\n }\n\n // cancelled may change during the await above\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n if (this._cancelled) return;\n\n this._stopRecording = this._rrwebModule.record({\n emit: (event) => {\n const nowMs = Date.now();\n const stored = this._persistActivity(nowMs);\n\n // Detect session rotation: after 3+ minutes idle, getOrRotateSession\n // creates a new session ID. We need to inject a FullSnapshot so the\n // new server-side SessionReplay record is playable.\n if (this._lastBrowserSessionId === null) {\n this._lastBrowserSessionId = stored.session_id;\n } else if (stored.session_id !== this._lastBrowserSessionId && !this._takingSnapshot) {\n this._lastBrowserSessionId = stored.session_id;\n // Inject a FullSnapshot for the new session (calls emit synchronously)\n this._takingSnapshot = true;\n try {\n this._rrwebModule!.record.takeFullSnapshot();\n } finally {\n this._takingSnapshot = false;\n }\n }\n\n const eventSize = JSON.stringify(event).length;\n this._events.push(event);\n this._eventSizes.push(eventSize);\n this._approxBytes += eventSize;\n if (this._events.length >= MAX_EVENTS_PER_BATCH || this._approxBytes >= MAX_APPROX_BYTES_PER_BATCH) {\n runAsynchronously(() => this._flush({ keepalive: false }));\n }\n },\n maskAllInputs: this._replayOptions.maskAllInputs ?? true,\n ...(this._replayOptions.blockClass !== undefined ? { blockClass: this._replayOptions.blockClass } : {}),\n ...(this._replayOptions.blockSelector !== undefined ? { blockSelector: this._replayOptions.blockSelector } : {}),\n }) ?? null;\n\n this._recording = true;\n\n const onPageHide = () => {\n runAsynchronously(() => this._flush({ keepalive: true }));\n };\n window.addEventListener(\"pagehide\", onPageHide);\n document.addEventListener(\"visibilitychange\", onPageHide);\n this._detachListeners = () => {\n window.removeEventListener(\"pagehide\", onPageHide);\n document.removeEventListener(\"visibilitychange\", onPageHide);\n };\n }\n\n private _stopCurrentRecording() {\n if (this._detachListeners) {\n this._detachListeners();\n this._detachListeners = null;\n }\n if (this._stopRecording) {\n this._stopRecording();\n this._stopRecording = null;\n }\n this._events = [];\n this._eventSizes = [];\n this._approxBytes = 0;\n this._recording = false;\n }\n\n private _tick() {\n if (this._cancelled) return;\n if (this._events.length > 0) {\n runAsynchronously(() => this._flush({ keepalive: false }));\n }\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"session-replay.js","names":["KnownErrors","Result"],"sources":["../../../../../src/lib/hexclave-app/apps/implementations/session-replay.ts"],"sourcesContent":["\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY UNLESS YOU ALSO EDIT THE CORRESPONDING FILE IN packages/template\n//===========================================\nimport { KnownErrors } from \"@hexclave/shared/dist/known-errors\";\nimport { isBrowserLike } from \"@hexclave/shared/dist/utils/env\";\nimport { captureWarning, throwErr } from \"@hexclave/shared/dist/utils/errors\";\nimport { runAsynchronously } from \"@hexclave/shared/dist/utils/promises\";\nimport { Result } from \"@hexclave/shared/dist/utils/results\";\n\nexport type AnalyticsReplayOptions = {\n /**\n * Whether session replays are enabled.\n *\n * @default true\n */\n enabled?: boolean,\n /**\n * Whether to mask the content of all `<input>` elements.\n *\n * @default true\n */\n maskAllInputs?: boolean,\n /**\n * A CSS class name or RegExp. Elements with a matching class will be blocked\n * (replaced with a placeholder in the recording).\n *\n * @default undefined\n */\n blockClass?: string | RegExp,\n /**\n * A CSS selector string. Elements matching this selector will be blocked\n * (replaced with a placeholder in the recording).\n *\n * @default undefined\n */\n blockSelector?: string,\n};\n\nexport type AnalyticsOptions = {\n /**\n * Whether SDK-managed analytics capture is enabled.\n *\n * @default true\n */\n enabled?: boolean,\n /**\n * Options for session replay recording. Replays are enabled by default;\n * set `enabled: false` to opt out.\n */\n replays?: AnalyticsReplayOptions,\n};\n\nexport function getSessionReplayOptions(analyticsOptions: AnalyticsOptions | undefined): AnalyticsReplayOptions {\n return {\n ...analyticsOptions?.replays,\n enabled: analyticsOptions?.replays?.enabled ?? true,\n };\n}\n\n/**\n * Converts AnalyticsOptions to a JSON-safe representation.\n * RegExp blockClass values are serialized as `{ __regexp, __flags }` objects.\n * The return type is AnalyticsOptions to keep StackClientAppJson simple;\n * the actual runtime value is JSON-safe.\n */\nexport function analyticsOptionsToJson(options: AnalyticsOptions | undefined): AnalyticsOptions | undefined {\n if (!options?.replays?.blockClass) return options;\n const { blockClass, ...rest } = options.replays;\n if (!(blockClass instanceof RegExp)) return options;\n return {\n ...options,\n replays: {\n ...rest,\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n blockClass: { __regexp: blockClass.source, __flags: blockClass.flags } as any,\n },\n };\n}\n\n/**\n * Reconstructs AnalyticsOptions from a JSON-deserialized value.\n * Converts `{ __regexp, __flags }` objects back to RegExp instances.\n */\nexport function analyticsOptionsFromJson(json: AnalyticsOptions | undefined): AnalyticsOptions | undefined {\n if (!json?.replays?.blockClass) return json;\n const { blockClass, ...rest } = json.replays;\n if (typeof blockClass === 'object' && '__regexp' in blockClass) {\n const bc = blockClass as unknown as { __regexp: string, __flags: string };\n return {\n ...json,\n replays: {\n ...rest,\n blockClass: new RegExp(bc.__regexp, bc.__flags),\n },\n };\n }\n return json;\n}\n\n// ---------- Recording internals ----------\n\n// Hexclave rebrand: canonical localStorage prefix (colon delimiters preserved).\nconst LOCAL_STORAGE_PREFIX = \"hexclave:session-replay:v1\";\n// Hexclave rebrand: legacy prefix — dual-read only, so a recording session active\n// across an SDK upgrade is not orphaned. Never written.\nconst LEGACY_LOCAL_STORAGE_PREFIX = \"stack:session-replay:v1\";\nconst IDLE_TTL_MS = 3 * 60 * 1000;\n\nconst FLUSH_INTERVAL_MS = 5_000;\nconst MAX_EVENTS_PER_BATCH = 200;\nconst MAX_APPROX_BYTES_PER_BATCH = 512_000;\n// The server rejects payloads > 1MB. Stay well under to account for JSON\n// envelope overhead (browser_session_id, timestamps, wrapper keys, etc.).\nconst MAX_FLUSH_PAYLOAD_BYTES = 900_000;\n\n// Reused across the emit hot path to avoid per-event allocation.\nconst textEncoder = new TextEncoder();\n\nexport type StoredSession = {\n session_id: string,\n created_at_ms: number,\n last_activity_ms: number,\n};\n\nexport function safeParseStoredSession(raw: string | null): StoredSession | null {\n if (!raw) return null;\n try {\n const parsed = JSON.parse(raw);\n if (typeof parsed !== \"object\" || parsed === null) return null;\n if (typeof parsed.session_id !== \"string\") return null;\n if (typeof parsed.created_at_ms !== \"number\") return null;\n if (typeof parsed.last_activity_ms !== \"number\") return null;\n return parsed as StoredSession;\n } catch {\n return null;\n }\n}\n\nexport function makeStorageKey(projectId: string) {\n return `${LOCAL_STORAGE_PREFIX}:${projectId}`;\n}\n\n// Hexclave rebrand: legacy key, dual-read only (never written).\nexport function makeLegacyStorageKey(projectId: string) {\n return `${LEGACY_LOCAL_STORAGE_PREFIX}:${projectId}`;\n}\n\nexport function generateUuid() {\n return crypto.randomUUID();\n}\n\nexport function getOrRotateSession(options: { key: string, legacyKey?: string, nowMs: number }): StoredSession {\n // Hexclave rebrand: prefer the new key; fall back to the legacy key so a\n // recording session active across an SDK upgrade is not orphaned.\n const existing = safeParseStoredSession(localStorage.getItem(options.key))\n ?? (options.legacyKey ? safeParseStoredSession(localStorage.getItem(options.legacyKey)) : null);\n if (existing && options.nowMs - existing.last_activity_ms <= IDLE_TTL_MS) {\n return existing;\n }\n const next: StoredSession = {\n session_id: generateUuid(),\n created_at_ms: options.nowMs,\n last_activity_ms: options.nowMs,\n };\n localStorage.setItem(options.key, JSON.stringify(next));\n return next;\n}\n\nexport type SessionRecorderDeps = {\n projectId: string,\n sendBatch: (body: string, options: { keepalive: boolean }) => Promise<Result<Response, Error>>,\n};\n\nexport function isAnalyticsNotEnabledError(error: unknown): boolean {\n return KnownErrors.AnalyticsNotEnabled.isInstance(error);\n}\n\n/**\n * Whether the error looks like a network failure caused by an ad blocker or\n * similar extension blocking analytics requests. These are expected in\n * production and should be silently ignored rather than logged as warnings.\n */\nexport function isAdBlockerNetworkError(error: unknown): boolean {\n if (error instanceof Error) {\n return error.message.includes(\"Failed to fetch\")\n || error.message.includes(\"NetworkError\")\n || error.message.includes(\"Load failed\")\n || error.message.includes(\"network connection\");\n }\n return false;\n}\n\nexport class SessionRecorder {\n private _started = false;\n private _cancelled = false;\n private _disabled = false;\n private _stopRecording: (() => void) | null = null;\n private _detachListeners: (() => void) | null = null;\n private _flushTimer: ReturnType<typeof setInterval> | null = null;\n private _events: unknown[] = [];\n private _eventSizes: number[] = [];\n private _approxBytes = 0;\n private _lastPersistActivity = 0;\n private _recording = false;\n private _rrwebModule: typeof import(\"rrweb\") | null = null;\n private _lastBrowserSessionId: string | null = null;\n private _takingSnapshot = false;\n private _flushInProgress = false;\n private readonly _sessionReplaySegmentId: string;\n private readonly _storageKey: string;\n // Hexclave rebrand: legacy key used for dual-read fallback only.\n private readonly _legacyStorageKey: string;\n private readonly _deps: SessionRecorderDeps;\n private readonly _replayOptions: AnalyticsReplayOptions;\n\n constructor(deps: SessionRecorderDeps, replayOptions: AnalyticsReplayOptions) {\n this._deps = deps;\n this._replayOptions = replayOptions;\n this._sessionReplaySegmentId = generateUuid();\n this._storageKey = makeStorageKey(deps.projectId);\n this._legacyStorageKey = makeLegacyStorageKey(deps.projectId);\n }\n\n /**\n * Starts recording. Idempotent — calling multiple times is safe.\n */\n start() {\n if (this._started) return;\n if (!isBrowserLike()) return;\n this._started = true;\n\n // Kick off rrweb recording\n runAsynchronously(() => this._startRecording(), { noErrorLogging: true });\n\n // Periodic flush\n this._flushTimer = setInterval(() => this._tick(), FLUSH_INTERVAL_MS);\n }\n\n stop() {\n this._cancelled = true;\n if (this._flushTimer !== null) {\n clearInterval(this._flushTimer);\n this._flushTimer = null;\n }\n // Flush remaining events before cleanup\n runAsynchronously(() => this._flush({ keepalive: true }));\n this._stopCurrentRecording();\n }\n\n clearBuffer() {\n this._events = [];\n this._eventSizes = [];\n this._approxBytes = 0;\n }\n\n private _persistActivity(nowMs: number): StoredSession {\n const stored = getOrRotateSession({ key: this._storageKey, legacyKey: this._legacyStorageKey, nowMs });\n if (nowMs - this._lastPersistActivity < 5_000) return stored;\n this._lastPersistActivity = nowMs;\n const updated: StoredSession = { ...stored, last_activity_ms: nowMs };\n localStorage.setItem(this._storageKey, JSON.stringify(updated));\n return stored;\n }\n\n private async _flush(options: { keepalive: boolean }) {\n if (this._disabled) return;\n if (this._events.length === 0) return;\n // Prevent concurrent in-flight HTTP requests. When a flush is already\n // in-flight, a second batch could race on the server (both call\n // findRecentSessionReplay before either upsert commits) and create\n // duplicate SessionReplay records. Events stay in _events and will be\n // picked up by the next tick or batch-size check.\n if (this._flushInProgress) return;\n\n const nowMs = Date.now();\n const stored = getOrRotateSession({ key: this._storageKey, legacyKey: this._legacyStorageKey, nowMs });\n\n // Capture all buffered events upfront (before any await) so that\n // stop() / _stopCurrentRecording() clearing this._events cannot race\n // with the async send loop below and silently discard overflow batches.\n const allEvents = this._events;\n const allSizes = this._eventSizes;\n this._events = [];\n this._eventSizes = [];\n this._approxBytes = 0;\n\n this._flushInProgress = true;\n try {\n let offset = 0;\n while (offset < allEvents.length) {\n // Build a batch that fits under the server's payload limit.\n // When _flushInProgress blocked earlier flushes, events can accumulate\n // well past MAX_APPROX_BYTES_PER_BATCH; sending them all at once would\n // exceed the server's 1MB body limit (413).\n // A single event over the limit can't be sent (rrweb events aren't splittable); drop it and move on.\n const firstSize = allSizes[offset] ?? throwErr(\"_eventSizes out of sync with _events — this should never happen\");\n if (firstSize > MAX_FLUSH_PAYLOAD_BYTES) {\n captureWarning(\n \"SessionRecorder.flush\",\n new Error(`Dropping oversized session replay event (${firstSize} bytes > ${MAX_FLUSH_PAYLOAD_BYTES} byte limit); it cannot be sent without a 413.`),\n );\n offset += 1;\n continue;\n }\n\n let batchBytes = 0;\n let batchEnd = offset;\n for (let i = offset; i < allEvents.length; i++) {\n const nextSize = allSizes[i] ?? throwErr(\"_eventSizes out of sync with _events — this should never happen\");\n if (batchBytes + nextSize > MAX_FLUSH_PAYLOAD_BYTES && batchEnd > offset) break;\n batchBytes += nextSize;\n batchEnd = i + 1;\n }\n\n const batchEvents = allEvents.slice(offset, batchEnd);\n offset = batchEnd;\n\n const batchId = generateUuid();\n const payload = {\n browser_session_id: stored.session_id,\n session_replay_segment_id: this._sessionReplaySegmentId,\n batch_id: batchId,\n started_at_ms: stored.created_at_ms,\n sent_at_ms: nowMs,\n events: batchEvents,\n };\n\n const res = await this._deps.sendBatch(\n JSON.stringify(payload),\n { keepalive: options.keepalive },\n );\n\n if (res.status === \"error\") {\n if (isAnalyticsNotEnabledError(res.error)) {\n this._disable();\n return;\n }\n // Ad blockers commonly block analytics endpoints, causing network\n // errors. These are expected and should not pollute the console.\n if (isAdBlockerNetworkError(res.error)) {\n return;\n }\n captureWarning(\"SessionRecorder.flush\", res.error);\n return;\n }\n\n if (!res.data.ok) {\n captureWarning(\"SessionRecorder.flush\", new Error(`SessionRecorder flush failed: ${res.data.status} ${await res.data.text()}`));\n return;\n }\n }\n } finally {\n this._flushInProgress = false;\n }\n }\n\n private _disable() {\n this._disabled = true;\n this.clearBuffer();\n if (this._flushTimer !== null) {\n clearInterval(this._flushTimer);\n this._flushTimer = null;\n }\n this._stopCurrentRecording();\n }\n\n private async _startRecording() {\n if (this._recording || this._cancelled) return;\n\n if (!this._rrwebModule) {\n const rrwebImport = await Result.fromPromise(import(\"rrweb\"));\n if (rrwebImport.status === \"error\") {\n console.warn(\"SessionRecorder: rrweb import failed. Is rrweb installed?\", rrwebImport.error);\n return;\n }\n this._rrwebModule = rrwebImport.data;\n }\n\n // cancelled may change during the await above\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n if (this._cancelled) return;\n\n this._stopRecording = this._rrwebModule.record({\n emit: (event) => {\n const nowMs = Date.now();\n const stored = this._persistActivity(nowMs);\n\n // Detect session rotation: after 3+ minutes idle, getOrRotateSession\n // creates a new session ID. We need to inject a FullSnapshot so the\n // new server-side SessionReplay record is playable.\n if (this._lastBrowserSessionId === null) {\n this._lastBrowserSessionId = stored.session_id;\n } else if (stored.session_id !== this._lastBrowserSessionId && !this._takingSnapshot) {\n this._lastBrowserSessionId = stored.session_id;\n // Inject a FullSnapshot for the new session (calls emit synchronously)\n this._takingSnapshot = true;\n try {\n this._rrwebModule!.record.takeFullSnapshot();\n } finally {\n this._takingSnapshot = false;\n }\n }\n\n // Measure UTF-8 byte length to match the server's byte limit (.length counts UTF-16 units, undercounting multibyte content).\n const eventSize = textEncoder.encode(JSON.stringify(event)).byteLength;\n this._events.push(event);\n this._eventSizes.push(eventSize);\n this._approxBytes += eventSize;\n if (this._events.length >= MAX_EVENTS_PER_BATCH || this._approxBytes >= MAX_APPROX_BYTES_PER_BATCH) {\n runAsynchronously(() => this._flush({ keepalive: false }));\n }\n },\n maskAllInputs: this._replayOptions.maskAllInputs ?? true,\n ...(this._replayOptions.blockClass !== undefined ? { blockClass: this._replayOptions.blockClass } : {}),\n ...(this._replayOptions.blockSelector !== undefined ? { blockSelector: this._replayOptions.blockSelector } : {}),\n }) ?? null;\n\n this._recording = true;\n\n const onPageHide = () => {\n runAsynchronously(() => this._flush({ keepalive: true }));\n };\n window.addEventListener(\"pagehide\", onPageHide);\n document.addEventListener(\"visibilitychange\", onPageHide);\n this._detachListeners = () => {\n window.removeEventListener(\"pagehide\", onPageHide);\n document.removeEventListener(\"visibilitychange\", onPageHide);\n };\n }\n\n private _stopCurrentRecording() {\n if (this._detachListeners) {\n this._detachListeners();\n this._detachListeners = null;\n }\n if (this._stopRecording) {\n this._stopRecording();\n this._stopRecording = null;\n }\n this._events = [];\n this._eventSizes = [];\n this._approxBytes = 0;\n this._recording = false;\n }\n\n private _tick() {\n if (this._cancelled) return;\n if (this._events.length > 0) {\n runAsynchronously(() => this._flush({ keepalive: false }));\n }\n }\n}\n"],"mappings":"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"}
@@ -35,6 +35,19 @@ type StackServerApp<HasTokenStore extends boolean = boolean, ProjectId extends s
35
35
  }) & {
36
36
  quantity?: number;
37
37
  })): Promise<void>;
38
+ createCheckoutUrl(options: (({
39
+ userId: string;
40
+ } | {
41
+ teamId: string;
42
+ } | {
43
+ customCustomerId: string;
44
+ }) & ({
45
+ productId: string;
46
+ } | {
47
+ product: InlineProduct;
48
+ }) & {
49
+ returnUrl?: string;
50
+ })): Promise<string>;
38
51
  useUser(options: GetCurrentUserOptions<HasTokenStore> & {
39
52
  or: 'redirect';
40
53
  }): ProjectCurrentServerUser<ProjectId>;
@@ -1 +1 @@
1
- {"version":3,"file":"server-app.d.ts","names":[],"sources":["../../../../../src/lib/hexclave-app/apps/interfaces/server-app.ts"],"mappings":"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"}
1
+ {"version":3,"file":"server-app.d.ts","names":[],"sources":["../../../../../src/lib/hexclave-app/apps/interfaces/server-app.ts"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"server-app.js","names":["_HexclaveServerAppImpl"],"sources":["../../../../../src/lib/hexclave-app/apps/interfaces/server-app.ts"],"sourcesContent":["\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY UNLESS YOU ALSO EDIT THE CORRESPONDING FILE IN packages/template\n//===========================================\nimport { KnownErrors } from \"@hexclave/shared\";\nimport { Result } from \"@hexclave/shared/dist/utils/results\";\nimport type { GenericQueryCtx } from \"convex/server\";\nimport { AsyncStoreProperty, GetCurrentPartialUserOptions, GetCurrentUserOptions } from \"../../common\";\nimport { CustomerProductsList, CustomerProductsRequestOptions, InlineProduct, ServerItem } from \"../../customers\";\nimport { DataVaultStore } from \"../../data-vault\";\nimport { EmailDeliveryInfo, SendEmailOptions } from \"../../email\";\nimport { ServerListTeamsOptions, ServerListUsersOptions, ServerTeam, ServerTeamCreateOptions } from \"../../teams\";\nimport { ProjectCurrentServerUser, ServerOAuthProvider, ServerUser, ServerUserCreateOptions, SyncedPartialServerUser, TokenPartialUser } from \"../../users\";\nimport { _HexclaveServerAppImpl } from \"../implementations\";\nimport { StackClientApp, StackClientAppConstructorOptions } from \"./client-app\";\n\n\n/** @deprecated Use `HexclaveServerAppConstructorOptions` from the `@hexclave/*` package instead — same symbol, new brand name. See https://docs.hexclave.com/migration. */\nexport type StackServerAppConstructorOptions<HasTokenStore extends boolean, ProjectId extends string> = StackClientAppConstructorOptions<HasTokenStore, ProjectId> & {\n secretServerKey?: string,\n};\n\n/** @deprecated Use `HexclaveServerApp` from the `@hexclave/*` package instead — same symbol, new brand name. See https://docs.hexclave.com/migration. */\nexport type StackServerApp<HasTokenStore extends boolean = boolean, ProjectId extends string = string> = (\n & {\n createTeam(data: ServerTeamCreateOptions): Promise<ServerTeam>,\n /**\n * @deprecated use `getUser()` instead\n */\n getServerUser(): Promise<ProjectCurrentServerUser<ProjectId> | null>,\n\n createUser(options: ServerUserCreateOptions): Promise<ServerUser>,\n grantProduct(options: (\n ({ userId: string } | { teamId: string } | { customCustomerId: string }) &\n ({ productId: string } | { product: InlineProduct }) &\n { quantity?: number }\n )): Promise<void>,\n\n useUser(options: GetCurrentUserOptions<HasTokenStore> & { or: 'redirect' }): ProjectCurrentServerUser<ProjectId>,\n useUser(options: GetCurrentUserOptions<HasTokenStore> & { or: 'throw' }): ProjectCurrentServerUser<ProjectId>,\n useUser(options: GetCurrentUserOptions<HasTokenStore> & { or: 'anonymous' }): ProjectCurrentServerUser<ProjectId>,\n useUser(options?: GetCurrentUserOptions<HasTokenStore>): ProjectCurrentServerUser<ProjectId> | null,\n useUser(id: string): ServerUser | null,\n useUser(options: { apiKey: string, or?: \"return-null\" | \"anonymous\" }): ServerUser | null,\n useUser(options: { from: \"convex\", ctx: GenericQueryCtx<any>, or?: \"return-null\" | \"anonymous\" }): ServerUser | null,\n\n getUser(options: GetCurrentUserOptions<HasTokenStore> & { or: 'redirect' }): Promise<ProjectCurrentServerUser<ProjectId>>,\n getUser(options: GetCurrentUserOptions<HasTokenStore> & { or: 'throw' }): Promise<ProjectCurrentServerUser<ProjectId>>,\n getUser(options: GetCurrentUserOptions<HasTokenStore> & { or: 'anonymous' }): Promise<ProjectCurrentServerUser<ProjectId>>,\n getUser(options?: GetCurrentUserOptions<HasTokenStore>): Promise<ProjectCurrentServerUser<ProjectId> | null>,\n getUser(id: string): Promise<ServerUser | null>,\n getUser(options: { apiKey: string, or?: \"return-null\" | \"anonymous\" }): Promise<ServerUser | null>,\n getUser(options: { from: \"convex\", ctx: GenericQueryCtx<any>, or?: \"return-null\" | \"anonymous\" }): Promise<ServerUser | null>,\n\n // note: we don't special-case 'anonymous' here to return non-null, see GetPartialUserOptions for more details\n getPartialUser(options: GetCurrentPartialUserOptions<HasTokenStore> & { from: 'token' }): Promise<TokenPartialUser | null>,\n getPartialUser(options: GetCurrentPartialUserOptions<HasTokenStore> & { from: 'convex' }): Promise<TokenPartialUser | null>,\n getPartialUser(options: GetCurrentPartialUserOptions<HasTokenStore>): Promise<SyncedPartialServerUser | TokenPartialUser | null>,\n usePartialUser(options: GetCurrentPartialUserOptions<HasTokenStore> & { from: 'token' }): TokenPartialUser | null,\n usePartialUser(options: GetCurrentPartialUserOptions<HasTokenStore> & { from: 'convex' }): TokenPartialUser | null,\n usePartialUser(options: GetCurrentPartialUserOptions<HasTokenStore>): SyncedPartialServerUser | TokenPartialUser | null,\n useTeam(id: string): ServerTeam | null,\n useTeam(options: { apiKey: string }): ServerTeam | null,\n getTeam(id: string): Promise<ServerTeam | null>,\n getTeam(options: { apiKey: string }): Promise<ServerTeam | null>,\n\n\n useUsers(options?: ServerListUsersOptions): ServerUser[] & { nextCursor: string | null }, // THIS_LINE_PLATFORM react-like\n listUsers(options?: ServerListUsersOptions): Promise<ServerUser[] & { nextCursor: string | null }>,\n\n /**\n * Returns every direct (or recursive) team permission grant for every\n * member of the given team in one request. Use this instead of calling\n * `user.listPermissions(team)` per row when rendering a roster — that\n * pattern produces an N+1 over the team-member endpoint.\n */\n listTeamMemberPermissions(teamId: string, options?: { recursive?: boolean }): Promise<{ userId: string, permissionId: string }[]>,\n useTeamMemberPermissions(teamId: string, options?: { recursive?: boolean }): { userId: string, permissionId: string }[],\n\n // TODO this should actually be on ServerUser\n createOAuthProvider(options: {\n userId: string,\n accountId: string,\n providerConfigId: string,\n email: string,\n allowSignIn: boolean,\n allowConnectedAccounts: boolean,\n }): Promise<Result<ServerOAuthProvider, InstanceType<typeof KnownErrors.OAuthProviderAccountIdAlreadyUsedForSignIn>>>,\n\n sendEmail(options: SendEmailOptions): Promise<void>,\n\n getEmailDeliveryStats(): Promise<EmailDeliveryInfo>,\n useEmailDeliveryStats(): EmailDeliveryInfo,\n\n activateEmailCapacityBoost(): Promise<void>,\n }\n & AsyncStoreProperty<\"user\", [id: string], ServerUser | null, false>\n & Omit<AsyncStoreProperty<\"users\", [], ServerUser[], true>, \"listUsers\" | \"useUsers\">\n & AsyncStoreProperty<\"teams\", [options?: ServerListTeamsOptions], ServerTeam[] & { nextCursor: string | null }, true>\n & AsyncStoreProperty<\"dataVaultStore\", [id: string], DataVaultStore, false>\n & AsyncStoreProperty<\n \"item\",\n [{ itemId: string, userId: string } | { itemId: string, teamId: string } | { itemId: string, customCustomerId: string }],\n ServerItem,\n false\n >\n & AsyncStoreProperty<\n \"products\",\n [options: CustomerProductsRequestOptions],\n CustomerProductsList,\n true\n >\n & StackClientApp<HasTokenStore, ProjectId>\n);\n/** @deprecated Use `HexclaveServerAppConstructor` from the `@hexclave/*` package instead — same symbol, new brand name. See https://docs.hexclave.com/migration. */\nexport type StackServerAppConstructor = {\n new <\n TokenStoreType extends string,\n HasTokenStore extends (TokenStoreType extends {} ? true : boolean),\n ProjectId extends string\n >(options: StackServerAppConstructorOptions<HasTokenStore, ProjectId>): StackServerApp<HasTokenStore, ProjectId>,\n new (options: StackServerAppConstructorOptions<boolean, string>): StackServerApp<boolean, string>,\n};\nexport type HexclaveServerAppConstructorOptions<HasTokenStore extends boolean, ProjectId extends string> = StackServerAppConstructorOptions<HasTokenStore, ProjectId>;\nexport type HexclaveServerApp<HasTokenStore extends boolean = boolean, ProjectId extends string = string> = StackServerApp<HasTokenStore, ProjectId>;\nexport type HexclaveServerAppConstructor = StackServerAppConstructor;\nexport const HexclaveServerApp: HexclaveServerAppConstructor = _HexclaveServerAppImpl;\n/** @deprecated Use `HexclaveServerApp` from the `@hexclave/*` package instead — same symbol, new brand name. See https://docs.hexclave.com/migration. */\nexport const StackServerApp: StackServerAppConstructor = HexclaveServerApp;\n"],"mappings":";;;;;AA8HA,MAAa,oBAAkDA;;AAE/D,MAAa,iBAA4C"}
1
+ {"version":3,"file":"server-app.js","names":["_HexclaveServerAppImpl"],"sources":["../../../../../src/lib/hexclave-app/apps/interfaces/server-app.ts"],"sourcesContent":["\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY UNLESS YOU ALSO EDIT THE CORRESPONDING FILE IN packages/template\n//===========================================\nimport { KnownErrors } from \"@hexclave/shared\";\nimport { Result } from \"@hexclave/shared/dist/utils/results\";\nimport type { GenericQueryCtx } from \"convex/server\";\nimport { AsyncStoreProperty, GetCurrentPartialUserOptions, GetCurrentUserOptions } from \"../../common\";\nimport { CustomerProductsList, CustomerProductsRequestOptions, InlineProduct, ServerItem } from \"../../customers\";\nimport { DataVaultStore } from \"../../data-vault\";\nimport { EmailDeliveryInfo, SendEmailOptions } from \"../../email\";\nimport { ServerListTeamsOptions, ServerListUsersOptions, ServerTeam, ServerTeamCreateOptions } from \"../../teams\";\nimport { ProjectCurrentServerUser, ServerOAuthProvider, ServerUser, ServerUserCreateOptions, SyncedPartialServerUser, TokenPartialUser } from \"../../users\";\nimport { _HexclaveServerAppImpl } from \"../implementations\";\nimport { StackClientApp, StackClientAppConstructorOptions } from \"./client-app\";\n\n\n/** @deprecated Use `HexclaveServerAppConstructorOptions` from the `@hexclave/*` package instead — same symbol, new brand name. See https://docs.hexclave.com/migration. */\nexport type StackServerAppConstructorOptions<HasTokenStore extends boolean, ProjectId extends string> = StackClientAppConstructorOptions<HasTokenStore, ProjectId> & {\n secretServerKey?: string,\n};\n\n/** @deprecated Use `HexclaveServerApp` from the `@hexclave/*` package instead — same symbol, new brand name. See https://docs.hexclave.com/migration. */\nexport type StackServerApp<HasTokenStore extends boolean = boolean, ProjectId extends string = string> = (\n & {\n createTeam(data: ServerTeamCreateOptions): Promise<ServerTeam>,\n /**\n * @deprecated use `getUser()` instead\n */\n getServerUser(): Promise<ProjectCurrentServerUser<ProjectId> | null>,\n\n createUser(options: ServerUserCreateOptions): Promise<ServerUser>,\n grantProduct(options: (\n ({ userId: string } | { teamId: string } | { customCustomerId: string }) &\n ({ productId: string } | { product: InlineProduct }) &\n { quantity?: number }\n )): Promise<void>,\n createCheckoutUrl(options: (\n ({ userId: string } | { teamId: string } | { customCustomerId: string }) &\n ({ productId: string } | { product: InlineProduct }) &\n { returnUrl?: string }\n )): Promise<string>,\n\n useUser(options: GetCurrentUserOptions<HasTokenStore> & { or: 'redirect' }): ProjectCurrentServerUser<ProjectId>,\n useUser(options: GetCurrentUserOptions<HasTokenStore> & { or: 'throw' }): ProjectCurrentServerUser<ProjectId>,\n useUser(options: GetCurrentUserOptions<HasTokenStore> & { or: 'anonymous' }): ProjectCurrentServerUser<ProjectId>,\n useUser(options?: GetCurrentUserOptions<HasTokenStore>): ProjectCurrentServerUser<ProjectId> | null,\n useUser(id: string): ServerUser | null,\n useUser(options: { apiKey: string, or?: \"return-null\" | \"anonymous\" }): ServerUser | null,\n useUser(options: { from: \"convex\", ctx: GenericQueryCtx<any>, or?: \"return-null\" | \"anonymous\" }): ServerUser | null,\n\n getUser(options: GetCurrentUserOptions<HasTokenStore> & { or: 'redirect' }): Promise<ProjectCurrentServerUser<ProjectId>>,\n getUser(options: GetCurrentUserOptions<HasTokenStore> & { or: 'throw' }): Promise<ProjectCurrentServerUser<ProjectId>>,\n getUser(options: GetCurrentUserOptions<HasTokenStore> & { or: 'anonymous' }): Promise<ProjectCurrentServerUser<ProjectId>>,\n getUser(options?: GetCurrentUserOptions<HasTokenStore>): Promise<ProjectCurrentServerUser<ProjectId> | null>,\n getUser(id: string): Promise<ServerUser | null>,\n getUser(options: { apiKey: string, or?: \"return-null\" | \"anonymous\" }): Promise<ServerUser | null>,\n getUser(options: { from: \"convex\", ctx: GenericQueryCtx<any>, or?: \"return-null\" | \"anonymous\" }): Promise<ServerUser | null>,\n\n // note: we don't special-case 'anonymous' here to return non-null, see GetPartialUserOptions for more details\n getPartialUser(options: GetCurrentPartialUserOptions<HasTokenStore> & { from: 'token' }): Promise<TokenPartialUser | null>,\n getPartialUser(options: GetCurrentPartialUserOptions<HasTokenStore> & { from: 'convex' }): Promise<TokenPartialUser | null>,\n getPartialUser(options: GetCurrentPartialUserOptions<HasTokenStore>): Promise<SyncedPartialServerUser | TokenPartialUser | null>,\n usePartialUser(options: GetCurrentPartialUserOptions<HasTokenStore> & { from: 'token' }): TokenPartialUser | null,\n usePartialUser(options: GetCurrentPartialUserOptions<HasTokenStore> & { from: 'convex' }): TokenPartialUser | null,\n usePartialUser(options: GetCurrentPartialUserOptions<HasTokenStore>): SyncedPartialServerUser | TokenPartialUser | null,\n useTeam(id: string): ServerTeam | null,\n useTeam(options: { apiKey: string }): ServerTeam | null,\n getTeam(id: string): Promise<ServerTeam | null>,\n getTeam(options: { apiKey: string }): Promise<ServerTeam | null>,\n\n\n useUsers(options?: ServerListUsersOptions): ServerUser[] & { nextCursor: string | null }, // THIS_LINE_PLATFORM react-like\n listUsers(options?: ServerListUsersOptions): Promise<ServerUser[] & { nextCursor: string | null }>,\n\n /**\n * Returns every direct (or recursive) team permission grant for every\n * member of the given team in one request. Use this instead of calling\n * `user.listPermissions(team)` per row when rendering a roster — that\n * pattern produces an N+1 over the team-member endpoint.\n */\n listTeamMemberPermissions(teamId: string, options?: { recursive?: boolean }): Promise<{ userId: string, permissionId: string }[]>,\n useTeamMemberPermissions(teamId: string, options?: { recursive?: boolean }): { userId: string, permissionId: string }[],\n\n // TODO this should actually be on ServerUser\n createOAuthProvider(options: {\n userId: string,\n accountId: string,\n providerConfigId: string,\n email: string,\n allowSignIn: boolean,\n allowConnectedAccounts: boolean,\n }): Promise<Result<ServerOAuthProvider, InstanceType<typeof KnownErrors.OAuthProviderAccountIdAlreadyUsedForSignIn>>>,\n\n sendEmail(options: SendEmailOptions): Promise<void>,\n\n getEmailDeliveryStats(): Promise<EmailDeliveryInfo>,\n useEmailDeliveryStats(): EmailDeliveryInfo,\n\n activateEmailCapacityBoost(): Promise<void>,\n }\n & AsyncStoreProperty<\"user\", [id: string], ServerUser | null, false>\n & Omit<AsyncStoreProperty<\"users\", [], ServerUser[], true>, \"listUsers\" | \"useUsers\">\n & AsyncStoreProperty<\"teams\", [options?: ServerListTeamsOptions], ServerTeam[] & { nextCursor: string | null }, true>\n & AsyncStoreProperty<\"dataVaultStore\", [id: string], DataVaultStore, false>\n & AsyncStoreProperty<\n \"item\",\n [{ itemId: string, userId: string } | { itemId: string, teamId: string } | { itemId: string, customCustomerId: string }],\n ServerItem,\n false\n >\n & AsyncStoreProperty<\n \"products\",\n [options: CustomerProductsRequestOptions],\n CustomerProductsList,\n true\n >\n & StackClientApp<HasTokenStore, ProjectId>\n);\n/** @deprecated Use `HexclaveServerAppConstructor` from the `@hexclave/*` package instead — same symbol, new brand name. See https://docs.hexclave.com/migration. */\nexport type StackServerAppConstructor = {\n new <\n TokenStoreType extends string,\n HasTokenStore extends (TokenStoreType extends {} ? true : boolean),\n ProjectId extends string\n >(options: StackServerAppConstructorOptions<HasTokenStore, ProjectId>): StackServerApp<HasTokenStore, ProjectId>,\n new (options: StackServerAppConstructorOptions<boolean, string>): StackServerApp<boolean, string>,\n};\nexport type HexclaveServerAppConstructorOptions<HasTokenStore extends boolean, ProjectId extends string> = StackServerAppConstructorOptions<HasTokenStore, ProjectId>;\nexport type HexclaveServerApp<HasTokenStore extends boolean = boolean, ProjectId extends string = string> = StackServerApp<HasTokenStore, ProjectId>;\nexport type HexclaveServerAppConstructor = StackServerAppConstructor;\nexport const HexclaveServerApp: HexclaveServerAppConstructor = _HexclaveServerAppImpl;\n/** @deprecated Use `HexclaveServerApp` from the `@hexclave/*` package instead — same symbol, new brand name. See https://docs.hexclave.com/migration. */\nexport const StackServerApp: StackServerAppConstructor = HexclaveServerApp;\n"],"mappings":";;;;;AAmIA,MAAa,oBAAkDA;;AAE/D,MAAa,iBAA4C"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "//": "THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY UNLESS YOU ALSO EDIT THE CORRESPONDING FILE IN packages/template (FOR package.json FILES, PLEASE EDIT package-template.json)",
3
3
  "name": "@hexclave/react",
4
- "version": "1.0.29",
4
+ "version": "1.0.30",
5
5
  "repository": "https://github.com/hexclave/hexclave",
6
6
  "sideEffects": false,
7
7
  "main": "./dist/index.js",
@@ -74,8 +74,8 @@
74
74
  "tailwindcss-animate": "^1.0.7",
75
75
  "rrweb": "^1.1.3",
76
76
  "yup": "^1.7.1",
77
- "@hexclave/shared": "1.0.29",
78
- "@hexclave/ui": "1.0.29"
77
+ "@hexclave/shared": "1.0.30",
78
+ "@hexclave/ui": "1.0.30"
79
79
  },
80
80
  "peerDependencies": {
81
81
  "@types/react": ">=18.0.0",
@@ -1426,23 +1426,24 @@ export class _HexclaveServerAppImplIncomplete<HasTokenStore extends boolean, Pro
1426
1426
  const result = useAsyncCache(cache, cacheKey, debugLabel);
1427
1427
  return useMemo(() => this._serverItemFromCrud({ type, id }, result), [result]);
1428
1428
  }
1429
+ private _resolveCustomer(
1430
+ options: { userId: string } | { teamId: string } | { customCustomerId: string }
1431
+ ): { customerType: "user" | "team" | "custom", customerId: string } {
1432
+ if ("userId" in options) {
1433
+ return { customerType: "user", customerId: options.userId };
1434
+ }
1435
+ if ("teamId" in options) {
1436
+ return { customerType: "team", customerId: options.teamId };
1437
+ }
1438
+ return { customerType: "custom", customerId: options.customCustomerId };
1439
+ }
1440
+
1429
1441
  async grantProduct(options: (
1430
1442
  ({ userId: string } | { teamId: string } | { customCustomerId: string }) &
1431
1443
  ({ productId: string } | { product: InlineProduct }) &
1432
1444
  { quantity?: number }
1433
1445
  )): Promise<void> {
1434
- let customerType: "user" | "team" | "custom";
1435
- let customerId: string;
1436
- if ("userId" in options) {
1437
- customerType = "user";
1438
- customerId = options.userId;
1439
- } else if ("teamId" in options) {
1440
- customerType = "team";
1441
- customerId = options.teamId;
1442
- } else {
1443
- customerType = "custom";
1444
- customerId = options.customCustomerId;
1445
- }
1446
+ const { customerType, customerId } = this._resolveCustomer(options);
1446
1447
 
1447
1448
  await this._interface.grantProduct({
1448
1449
  customerType,
@@ -1460,6 +1461,17 @@ export class _HexclaveServerAppImplIncomplete<HasTokenStore extends boolean, Pro
1460
1461
  await cache.refresh([customerId, null, null]);
1461
1462
  }
1462
1463
 
1464
+ async createCheckoutUrl(options: (
1465
+ ({ userId: string } | { teamId: string } | { customCustomerId: string }) &
1466
+ ({ productId: string } | { product: InlineProduct }) &
1467
+ { returnUrl?: string }
1468
+ )): Promise<string> {
1469
+ const { customerType, customerId } = this._resolveCustomer(options);
1470
+
1471
+ const productIdOrInline = "productId" in options ? options.productId : options.product;
1472
+ return await this._interface.createCheckoutUrl(customerType, customerId, productIdOrInline, null, options.returnUrl, "server");
1473
+ }
1474
+
1463
1475
  async createTeam(data: ServerTeamCreateOptions): Promise<ServerTeam> {
1464
1476
  const team = await this._interface.createServerTeam(serverTeamCreateOptionsToCrud(data));
1465
1477
  await this._serverTeamsCache.refreshWhere(() => true);
@@ -114,6 +114,9 @@ const MAX_APPROX_BYTES_PER_BATCH = 512_000;
114
114
  // envelope overhead (browser_session_id, timestamps, wrapper keys, etc.).
115
115
  const MAX_FLUSH_PAYLOAD_BYTES = 900_000;
116
116
 
117
+ // Reused across the emit hot path to avoid per-event allocation.
118
+ const textEncoder = new TextEncoder();
119
+
117
120
  export type StoredSession = {
118
121
  session_id: string,
119
122
  created_at_ms: number,
@@ -290,6 +293,17 @@ export class SessionRecorder {
290
293
  // When _flushInProgress blocked earlier flushes, events can accumulate
291
294
  // well past MAX_APPROX_BYTES_PER_BATCH; sending them all at once would
292
295
  // exceed the server's 1MB body limit (413).
296
+ // A single event over the limit can't be sent (rrweb events aren't splittable); drop it and move on.
297
+ const firstSize = allSizes[offset] ?? throwErr("_eventSizes out of sync with _events — this should never happen");
298
+ if (firstSize > MAX_FLUSH_PAYLOAD_BYTES) {
299
+ captureWarning(
300
+ "SessionRecorder.flush",
301
+ new Error(`Dropping oversized session replay event (${firstSize} bytes > ${MAX_FLUSH_PAYLOAD_BYTES} byte limit); it cannot be sent without a 413.`),
302
+ );
303
+ offset += 1;
304
+ continue;
305
+ }
306
+
293
307
  let batchBytes = 0;
294
308
  let batchEnd = offset;
295
309
  for (let i = offset; i < allEvents.length; i++) {
@@ -388,7 +402,8 @@ export class SessionRecorder {
388
402
  }
389
403
  }
390
404
 
391
- const eventSize = JSON.stringify(event).length;
405
+ // Measure UTF-8 byte length to match the server's byte limit (.length counts UTF-16 units, undercounting multibyte content).
406
+ const eventSize = textEncoder.encode(JSON.stringify(event)).byteLength;
392
407
  this._events.push(event);
393
408
  this._eventSizes.push(eventSize);
394
409
  this._approxBytes += eventSize;
@@ -35,6 +35,11 @@ export type StackServerApp<HasTokenStore extends boolean = boolean, ProjectId ex
35
35
  ({ productId: string } | { product: InlineProduct }) &
36
36
  { quantity?: number }
37
37
  )): Promise<void>,
38
+ createCheckoutUrl(options: (
39
+ ({ userId: string } | { teamId: string } | { customCustomerId: string }) &
40
+ ({ productId: string } | { product: InlineProduct }) &
41
+ { returnUrl?: string }
42
+ )): Promise<string>,
38
43
 
39
44
  useUser(options: GetCurrentUserOptions<HasTokenStore> & { or: 'redirect' }): ProjectCurrentServerUser<ProjectId>,
40
45
  useUser(options: GetCurrentUserOptions<HasTokenStore> & { or: 'throw' }): ProjectCurrentServerUser<ProjectId>,