@qafka/react-native 2.1.1 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -86,11 +86,12 @@ const { width: SCREEN_WIDTH } = react_native_1.Dimensions.get('window');
86
86
  * />
87
87
  * ```
88
88
  */
89
- exports.Qafka = (0, react_1.forwardRef)(function Qafka({ style, apiUrl: apiUrlProp, subProjectId, projectId, locale, mode = 'fullscreen', theme: themeName = 'light', customTheme, themeOverride, isAuthenticated, endUserId, endUserData, enableStreaming = true, voiceEnabled: voiceEnabledProp = true, context: userContext, contextDescription, components: customComponents, showTimestamps = true, placeholder = 'Type a message...', maxMessageLength = 500, greetingMessage, onReady, onMessageSent, onResponseReceived, onError, onNavigationSuggest, onNavigationAction, onExternalSuggestion, onCardDeepLink, onCardSuggestMessage, onCardExternalNavigation, onCardShare, onCardCopy, onCardToolTrigger, onCardCTAClick, onToolSuggested, onToolDataRequested, onActionResult, onStepCompleted, onFileUploadRequest, onExtractionResult, onClose, onBack, CloseComponent, BackComponent, navigationLabelFormat, NavigationButtonComponent, voiceComponents, voiceTranscript = 'centered', toolRenderMode, }, ref) {
89
+ exports.Qafka = (0, react_1.forwardRef)(function Qafka({ style, apiUrl: apiUrlProp, devConfig, subProjectId, projectId, locale, mode = 'fullscreen', theme: themeName = 'light', customTheme, themeOverride, isAuthenticated, endUserId, endUserData, enableStreaming = true, voiceEnabled: voiceEnabledProp = true, context: userContext, contextDescription, components: customComponents, showTimestamps = true, placeholder = 'Type a message...', maxMessageLength = 500, greetingMessage, onReady, onMessageSent, onResponseReceived, onError, onNavigationSuggest, onNavigationAction, onExternalSuggestion, onCardDeepLink, onCardSuggestMessage, onCardExternalNavigation, onCardShare, onCardCopy, onCardToolTrigger, onCardCTAClick, onToolSuggested, onToolDataRequested, onActionResult, onStepCompleted, onFileUploadRequest, onExtractionResult, onClose, onBack, CloseComponent, BackComponent, navigationLabelFormat, NavigationButtonComponent, voiceComponents, voiceTranscript = 'centered', toolRenderMode, }, ref) {
90
90
  // === ALL HOOKS FIRST (Rules of Hooks compliance) ===
91
91
  const insets = (0, react_native_safe_area_context_1.useSafeAreaInsets)();
92
92
  const { sdkReady, error: sdkError, resolvedApiKey, resolvedApiUrl } = (0, useSDK_1.useSDK)({
93
93
  apiUrl: apiUrlProp,
94
+ devConfig,
94
95
  subProjectId,
95
96
  projectId,
96
97
  locale,
@@ -4,6 +4,7 @@ import { ComponentRegistry } from '../types/components';
4
4
  import { ExternalSuggestion } from '../types/external-navigation';
5
5
  import { NavigationSuggestion } from '../types/navigation';
6
6
  import { VoiceChatState } from './VoicePage';
7
+ import type { RuntimeConfig } from '../runtime-config-loader';
7
8
  /**
8
9
  * Augmentation hook for consumer-side projectId literal types.
9
10
  *
@@ -256,6 +257,16 @@ export interface QafkaProps {
256
257
  * Backend API URL (OPTIONAL — advanced, leave unset for production).
257
258
  */
258
259
  apiUrl?: string;
260
+ /**
261
+ * Dev-only runtime config the SDK reads for the simulator/emulator
262
+ * `__DEV__` auth path. Pass the CLI-managed file:
263
+ * `devConfig={__DEV__ ? require('../.qafka/qafka-runtime') : undefined}`
264
+ * Omit on real devices / production — attestation handles auth there, and
265
+ * `require` under `__DEV__` is dead-code-eliminated from release bundles so
266
+ * no key reaches the prod build. When omitted, the SDK falls back to the
267
+ * legacy `node_modules` config (backward compatible).
268
+ */
269
+ devConfig?: RuntimeConfig | null;
259
270
  /**
260
271
  * Sub-Project Identifier (OPTIONAL)
261
272
  *
@@ -569,7 +569,9 @@ function VoicePage({ state, transcript, userTranscript, transcriptHistory, trans
569
569
 
570
570
  {showMute && !hasToolUI ? (<react_native_1.View style={{
571
571
  position: 'absolute',
572
- top: 12,
572
+ // Absolute children aren't pushed by the parent's safe-area paddingTop,
573
+ // so add the inset here to keep the button clear of the status bar / notch.
574
+ top: insets.top + 12,
573
575
  right: 16,
574
576
  }}>
575
577
  <MuteButton isMuted={isMuted} onToggle={onToggleMute} theme={theme} state={state}/>
@@ -10,12 +10,15 @@ export interface UseSDKOptions {
10
10
  locale?: string;
11
11
  onReady?: () => void;
12
12
  onError?: (error: Error) => void;
13
+ devConfig?: unknown;
13
14
  }
14
15
  export interface UseSDKResult {
15
16
  sdkReady: boolean;
16
17
  error: string | null;
17
18
  /**
18
- * Dev-only: the resolved developmentKey from `qafka.config.js`. Null in
19
+ * Dev-only: the resolved developmentKey from the consumer dev config the
20
+ * `devConfig` prop (`.qafka/qafka-runtime.js`) or the legacy node_modules
21
+ * fallback. Null in
19
22
  * production builds (Metro dead-code-eliminates the dev config branch).
20
23
  * Used by `useVoiceChat` whose WebSocket auth still relies on apiKey.
21
24
  */
@@ -28,4 +31,4 @@ export interface UseSDKResult {
28
31
  /**
29
32
  * Custom hook for managing SDK initialization and lifecycle
30
33
  */
31
- export declare const useSDK: ({ apiUrl, subProjectId, projectId, locale, onReady, onError, }: UseSDKOptions) => UseSDKResult;
34
+ export declare const useSDK: ({ apiUrl, subProjectId, projectId, locale, onReady, onError, devConfig, }: UseSDKOptions) => UseSDKResult;
@@ -5,10 +5,11 @@ const react_1 = require("react");
5
5
  const QafkaSDK_1 = require("../QafkaSDK");
6
6
  const runtime_config_loader_1 = require("../runtime-config-loader");
7
7
  const resolve_project_config_1 = require("../resolve-project-config");
8
+ const resolve_runtime_config_source_1 = require("../resolve-runtime-config-source");
8
9
  /**
9
10
  * Custom hook for managing SDK initialization and lifecycle
10
11
  */
11
- const useSDK = ({ apiUrl, subProjectId, projectId, locale, onReady, onError, }) => {
12
+ const useSDK = ({ apiUrl, subProjectId, projectId, locale, onReady, onError, devConfig, }) => {
12
13
  const [sdkReady, setSdkReady] = (0, react_1.useState)(false);
13
14
  const [error, setError] = (0, react_1.useState)(null);
14
15
  const [resolvedApiKey, setResolvedApiKey] = (0, react_1.useState)(null);
@@ -27,18 +28,16 @@ const useSDK = ({ apiUrl, subProjectId, projectId, locale, onReady, onError, })
27
28
  let apiKey = null;
28
29
  let resolvedUrl = apiUrl;
29
30
  if (__DEV__) {
30
- // Dev path: load qafka.config.js, pick developmentKey for the current project.
31
- // Metro dead-code elimination removes this branch entirely in production
32
- // builds, so qafka.config.js never lands in the prod bundle.
31
+ // Dev path: prefer the consumer-supplied .qafka/ config, else the legacy
32
+ // node_modules config. Metro dead-code elimination removes this whole
33
+ // branch (and both requires) from production builds.
33
34
  try {
34
- const cfg = (0, resolve_project_config_1.resolveProjectConfig)({ apiUrl, projectId }, (0, runtime_config_loader_1.loadRuntimeConfig)());
35
+ const cfg = (0, resolve_project_config_1.resolveProjectConfig)({ apiUrl, projectId }, (0, resolve_runtime_config_source_1.resolveRuntimeConfigSource)(devConfig, (0, runtime_config_loader_1.loadRuntimeConfig)()));
35
36
  apiKey = cfg.apiKey;
36
37
  resolvedUrl = cfg.apiUrl;
37
38
  }
38
39
  catch (err) {
39
- if (__DEV__) {
40
- console.warn('[Qafka] Dev config missing or invalid:', err instanceof Error ? err.message : err);
41
- }
40
+ console.warn('[Qafka] Dev config missing or invalid:', err instanceof Error ? err.message : err);
42
41
  }
43
42
  }
44
43
  // Expose resolved values so `useVoiceChat` (WebSocket auth still
@@ -0,0 +1,9 @@
1
+ import { type RuntimeConfig } from './runtime-config-loader';
2
+ /**
3
+ * Pick the runtime config the SDK should use in dev builds.
4
+ *
5
+ * Precedence: a valid `devConfig` (the consumer's `require('../.qafka/qafka-runtime')`,
6
+ * shape-validated) wins; otherwise the value loaded from the SDK package
7
+ * (`loadRuntimeConfig()`). Returns null when neither yields a usable config.
8
+ */
9
+ export declare function resolveRuntimeConfigSource(devConfig: unknown, loaded: RuntimeConfig | null): RuntimeConfig | null;
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.resolveRuntimeConfigSource = resolveRuntimeConfigSource;
4
+ const runtime_config_loader_1 = require("./runtime-config-loader");
5
+ /**
6
+ * Pick the runtime config the SDK should use in dev builds.
7
+ *
8
+ * Precedence: a valid `devConfig` (the consumer's `require('../.qafka/qafka-runtime')`,
9
+ * shape-validated) wins; otherwise the value loaded from the SDK package
10
+ * (`loadRuntimeConfig()`). Returns null when neither yields a usable config.
11
+ */
12
+ function resolveRuntimeConfigSource(devConfig, loaded) {
13
+ const provided = devConfig != null ? (0, runtime_config_loader_1.normalizeRuntimeConfig)(devConfig) : null;
14
+ return provided ?? loaded;
15
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@qafka/react-native",
3
- "version": "2.1.1",
3
+ "version": "2.2.0",
4
4
  "description": "Drop-in AI assistant for React Native: chat, voice, and tool execution with screen-aware navigation.",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",