@oxy-hq/sdk 0.2.5 → 1.0.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/README.md +61 -45
- package/dist/index.cjs +58 -21
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +30 -2
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +30 -2
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +42 -20
- package/dist/index.mjs.map +1 -1
- package/dist/{postMessage-B1J0jDRN.cjs → postMessage-Cb5PCtcE.cjs} +12 -11
- package/dist/{postMessage-B1J0jDRN.cjs.map → postMessage-Cb5PCtcE.cjs.map} +1 -1
- package/dist/{postMessage-BxdgtX8j.mjs → postMessage-Gnhr_wnw.mjs} +25 -2
- package/dist/{postMessage-BxdgtX8j.mjs.map → postMessage-Gnhr_wnw.mjs.map} +1 -1
- package/package.json +78 -79
- package/dist/postMessage-BSNS3ccd.cjs +0 -5
- package/dist/postMessage-D5wWgwcO.mjs +0 -4
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"postMessage-BxdgtX8j.mjs","names":[],"sources":["../src/types.ts","../src/auth/postMessage.ts"],"sourcesContent":["/**\n * Represents an app item in the project\n */\nexport interface AppItem {\n name: string;\n path: string;\n}\n\n/**\n * Reference to a data file (usually parquet)\n */\nexport interface FileReference {\n file_path: string;\n}\n\n/**\n * Table data structure for in-memory tables\n * (used when data is fetched and parsed)\n */\nexport interface TableData {\n columns: string[];\n rows: unknown[][];\n total_rows?: number;\n}\n\nexport type DataContainer = Record<string, FileReference>;\n\n/**\n * Response from app data endpoints\n */\nexport interface AppDataResponse {\n data: DataContainer | null;\n error: string | null;\n}\n\n/**\n * Display with potential error\n */\nexport interface DisplayWithError {\n display?: DisplayData;\n error?: string;\n}\n\n/**\n * Display data structure\n */\nexport interface DisplayData {\n type: string;\n content: unknown;\n}\n\n/**\n * Response from get displays endpoint\n */\nexport interface GetDisplaysResponse {\n displays: DisplayWithError[];\n}\n\n/**\n * Error response from the API\n */\nexport interface ApiError {\n message: string;\n status: number;\n details?: unknown;\n}\n\n/**\n * PostMessage authentication protocol types\n */\n\n/**\n * Request message sent from iframe to parent window\n */\nexport interface OxyAuthRequestMessage {\n type: \"OXY_AUTH_REQUEST\";\n version: \"1.0\";\n timestamp: number;\n requestId: string;\n}\n\n/**\n * Response message sent from parent window to iframe\n */\nexport interface OxyAuthResponseMessage {\n type: \"OXY_AUTH_RESPONSE\";\n version: \"1.0\";\n requestId: string;\n apiKey?: string;\n projectId?: string;\n baseUrl?: string;\n}\n\n/**\n * Options for postMessage authentication\n */\nexport interface PostMessageAuthOptions {\n /** Required parent window origin for security (e.g., 'https://app.example.com'). Use '*' only in development! */\n parentOrigin?: string;\n /** Timeout in milliseconds (default: 5000) */\n timeout?: number;\n /** Number of retry attempts (default: 0) */\n retries?: number;\n}\n\n/**\n * Result from successful postMessage authentication\n */\nexport interface PostMessageAuthResult {\n apiKey?: string;\n projectId?: string;\n baseUrl?: string;\n source: \"postmessage\";\n}\n\n/**\n * Custom error classes for postMessage authentication\n */\n\n/**\n * Error thrown when postMessage authentication times out\n */\nexport class PostMessageAuthTimeoutError extends Error {\n constructor(timeout: number) {\n super(\n `Parent window did not respond to authentication request within ${timeout}ms.\\n\\n` +\n `Possible causes:\\n` +\n `- Parent window is not listening for 'OXY_AUTH_REQUEST' messages\\n` +\n `- Parent origin mismatch\\n` +\n `- Network/browser issues\\n\\n` +\n `Troubleshooting:\\n` +\n `1. Verify parent window has message listener set up\\n` +\n `2. Check parentOrigin configuration matches parent window origin\\n` +\n `3. Open browser console in parent window for errors`,\n );\n this.name = \"PostMessageAuthTimeoutError\";\n }\n}\n\n/**\n * Error thrown when authentication response comes from unauthorized origin\n */\nexport class PostMessageAuthInvalidOriginError extends Error {\n constructor(expected: string, actual: string) {\n super(\n `Received authentication response from unauthorized origin.\\n\\n` +\n `Expected: ${expected}\\n` +\n `Actual: ${actual}\\n\\n` +\n `This is a security error. Verify your parentOrigin configuration.`,\n );\n this.name = \"PostMessageAuthInvalidOriginError\";\n }\n}\n\n/**\n * Error thrown when postMessage authentication is attempted outside iframe context\n */\nexport class PostMessageAuthNotInIframeError extends Error {\n constructor() {\n const currentContext = getCurrentContext();\n super(\n `PostMessage authentication is only available when running in an iframe context.\\n\\n` +\n `Current context: ${currentContext}\\n\\n` +\n `If you're running in a regular browser window, use direct configuration instead:\\n` +\n `const client = new OxyClient({ apiKey: 'your-key', ... })`,\n );\n this.name = \"PostMessageAuthNotInIframeError\";\n }\n}\n\nfunction getCurrentContext(): string {\n if (typeof window === \"undefined\") {\n return \"non-browser (Node.js)\";\n }\n if (window === window.parent) {\n return \"top-level window\";\n }\n return \"iframe\";\n}\n\n/**\n * Error thrown when authentication response is malformed or invalid\n */\nexport class PostMessageAuthInvalidResponseError extends Error {\n constructor(reason: string) {\n super(`Invalid authentication response from parent: ${reason}`);\n this.name = \"PostMessageAuthInvalidResponseError\";\n }\n}\n","/**\n * PostMessage-based authentication for iframe scenarios\n *\n * This module enables secure cross-origin authentication between an iframe\n * (running the Oxy SDK) and its parent window (holding the API key).\n */\n\nimport type {\n OxyAuthRequestMessage,\n OxyAuthResponseMessage,\n PostMessageAuthOptions,\n PostMessageAuthResult,\n} from \"../types\";\n\nimport {\n PostMessageAuthTimeoutError,\n PostMessageAuthNotInIframeError,\n PostMessageAuthInvalidResponseError,\n} from \"../types\";\n\n/**\n * Check if the current context is running inside an iframe\n *\n * @returns true if running in an iframe, false otherwise (including Node.js)\n */\nexport function isInIframe(): boolean {\n // Check if we're in a browser environment\n if (typeof window === \"undefined\") {\n return false;\n }\n\n // Check if window has a parent and it's different from itself\n try {\n return window !== window.parent && window.parent !== null;\n } catch {\n // Cross-origin access might throw an error, but we're still in an iframe\n return true;\n }\n}\n\n/**\n * Generate a unique request ID for tracking auth requests\n *\n * @returns A unique identifier string\n */\nexport function generateRequestId(): string {\n // Use crypto.randomUUID if available (modern browsers)\n if (typeof crypto !== \"undefined\" && crypto.randomUUID) {\n return crypto.randomUUID();\n }\n\n // Fallback for older environments: timestamp + random\n // Note: Math.random() is used here as a fallback for environments without crypto.randomUUID\n // This is acceptable for non-security-critical request IDs (used only for message correlation)\n /* eslint-disable sonarjs/pseudo-random */\n return `${Date.now()}-${Math.random().toString(36).substring(2, 11)}`;\n /* eslint-enable sonarjs/pseudo-random */\n}\n\n/**\n * Validate that a message origin matches the expected origin\n *\n * @param messageOrigin - The origin from the message event\n * @param allowedOrigin - The expected/allowed origin\n * @returns true if origin is valid, false otherwise\n */\nexport function validateOrigin(\n messageOrigin: string,\n allowedOrigin?: string,\n): boolean {\n // If no allowed origin is specified, reject (security)\n if (!allowedOrigin) {\n return false;\n }\n\n // Wildcard - allow any origin (development only!)\n if (allowedOrigin === \"*\") {\n return true;\n }\n\n // Exact match\n if (messageOrigin === allowedOrigin) {\n return true;\n }\n\n // Try URL parsing for more flexible matching\n try {\n const messageUrl = new URL(messageOrigin);\n const allowedUrl = new URL(allowedOrigin);\n return messageUrl.origin === allowedUrl.origin;\n } catch {\n // If URL parsing fails, do simple string match\n return messageOrigin === allowedOrigin;\n }\n}\n\n/**\n * Create a promise that listens for an authentication response\n *\n * @param requestId - The request ID to match against\n * @param origin - The expected parent origin\n * @param timeout - Timeout in milliseconds\n * @returns Promise that resolves with the auth response\n */\nfunction createAuthListener(\n requestId: string,\n origin: string | undefined,\n timeout: number,\n): Promise<OxyAuthResponseMessage> {\n return new Promise((resolve, reject) => {\n // Set up message listener\n const listener = (event: MessageEvent) => {\n // Validate origin\n if (!validateOrigin(event.origin, origin)) {\n // Don't reject here - might be other postMessage traffic\n // Just ignore messages from wrong origins\n return;\n }\n\n // Check message type\n if (!event.data || event.data.type !== \"OXY_AUTH_RESPONSE\") {\n // Not our message type, ignore\n return;\n }\n\n const response = event.data as OxyAuthResponseMessage;\n\n // Validate request ID matches\n if (response.requestId !== requestId) {\n // Wrong request ID, ignore (might be another auth in progress)\n return;\n }\n\n // Validate version\n if (response.version !== \"1.0\") {\n clearTimeout(timeoutId);\n window.removeEventListener(\"message\", listener);\n reject(\n new PostMessageAuthInvalidResponseError(\n `Unsupported protocol version: ${response.version}`,\n ),\n );\n return;\n }\n\n // Success!\n clearTimeout(timeoutId);\n window.removeEventListener(\"message\", listener);\n resolve(response);\n };\n\n // Set up timeout\n const timeoutId = setTimeout(() => {\n window.removeEventListener(\"message\", listener);\n reject(new PostMessageAuthTimeoutError(timeout));\n }, timeout);\n\n // Start listening\n window.addEventListener(\"message\", listener);\n });\n}\n\n/**\n * Request authentication from parent window via postMessage\n *\n * This is the main entry point for iframe-based authentication.\n * It sends a request to the parent window and waits for a response.\n *\n * @param options - Configuration options for the auth request\n * @returns Promise that resolves with authentication credentials\n * @throws {PostMessageAuthNotInIframeError} If not in an iframe\n * @throws {PostMessageAuthTimeoutError} If parent doesn't respond in time\n * @throws {PostMessageAuthInvalidOriginError} If response from wrong origin\n * @throws {PostMessageAuthInvalidResponseError} If response is malformed\n *\n * @example\n * ```typescript\n * const auth = await requestAuthFromParent({\n * parentOrigin: 'https://app.example.com',\n * timeout: 5000\n * });\n * console.log('Received API key:', auth.apiKey);\n * ```\n */\nexport async function requestAuthFromParent(\n options: PostMessageAuthOptions = {},\n): Promise<PostMessageAuthResult> {\n const { parentOrigin, timeout = 5000, retries = 0 } = options;\n\n // Validate we're in an iframe\n if (!isInIframe()) {\n throw new PostMessageAuthNotInIframeError();\n }\n\n // Validate we're in a browser environment\n if (typeof window === \"undefined\") {\n throw new PostMessageAuthNotInIframeError();\n }\n\n // Generate request ID\n const requestId = generateRequestId();\n\n // Create request message\n const request: OxyAuthRequestMessage = {\n type: \"OXY_AUTH_REQUEST\",\n version: \"1.0\",\n timestamp: Date.now(),\n requestId,\n };\n\n // Attempt authentication with retries\n let lastError: Error | null = null;\n const maxAttempts = retries + 1;\n\n for (let attempt = 0; attempt < maxAttempts; attempt++) {\n try {\n // Set up listener before sending request to avoid race condition\n const responsePromise = createAuthListener(\n requestId,\n parentOrigin,\n timeout,\n );\n\n // Send request to parent\n const targetOrigin = parentOrigin || \"*\";\n window.parent.postMessage(request, targetOrigin);\n\n // Wait for response\n const response = await responsePromise;\n\n // Return successful result\n return {\n apiKey: response.apiKey,\n projectId: response.projectId,\n baseUrl: response.baseUrl,\n source: \"postmessage\",\n };\n } catch (error) {\n lastError = error as Error;\n\n // If this isn't a timeout, don't retry\n if (!(error instanceof PostMessageAuthTimeoutError)) {\n throw error;\n }\n\n // If we have more attempts, continue\n if (attempt < maxAttempts - 1) {\n // Optional: Add a small delay before retry\n await new Promise((resolve) => setTimeout(resolve, 100));\n continue;\n }\n\n // All retries exhausted\n throw error;\n }\n }\n\n // Should never reach here, but TypeScript needs this\n throw lastError || new Error(\"Authentication failed\");\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"postMessage-Gnhr_wnw.mjs","names":[],"sources":["../src/types.ts","../src/auth/postMessage.ts"],"sourcesContent":["/**\n * Represents an app item in the project\n */\nexport interface AppItem {\n name: string;\n path: string;\n}\n\n/**\n * Reference to a data file (usually parquet)\n */\nexport interface FileReference {\n file_path: string;\n}\n\n/**\n * Table data structure for in-memory tables\n * (used when data is fetched and parsed)\n */\nexport interface TableData {\n columns: string[];\n rows: unknown[][];\n total_rows?: number;\n}\n\nexport type DataContainer = Record<string, FileReference>;\n\n/**\n * Response from app data endpoints\n */\nexport interface AppDataResponse {\n data: DataContainer | null;\n error: string | null;\n}\n\n/**\n * Display with potential error\n */\nexport interface DisplayWithError {\n display?: DisplayData;\n error?: string;\n}\n\n/**\n * Display data structure\n */\nexport interface DisplayData {\n type: string;\n content: unknown;\n}\n\n/**\n * Response from get displays endpoint\n */\nexport interface GetDisplaysResponse {\n displays: DisplayWithError[];\n}\n\n/**\n * Error response from the API\n */\nexport interface ApiError {\n message: string;\n status: number;\n details?: unknown;\n}\n\n/**\n * PostMessage authentication protocol types\n */\n\n/**\n * Request message sent from iframe to parent window\n */\nexport interface OxyAuthRequestMessage {\n type: \"OXY_AUTH_REQUEST\";\n version: \"1.0\";\n timestamp: number;\n requestId: string;\n}\n\n/**\n * Response message sent from parent window to iframe\n */\nexport interface OxyAuthResponseMessage {\n type: \"OXY_AUTH_RESPONSE\";\n version: \"1.0\";\n requestId: string;\n apiKey?: string;\n projectId?: string;\n baseUrl?: string;\n}\n\n/**\n * Options for postMessage authentication\n */\nexport interface PostMessageAuthOptions {\n /** Required parent window origin for security (e.g., 'https://app.example.com'). Use '*' only in development! */\n parentOrigin?: string;\n /** Timeout in milliseconds (default: 5000) */\n timeout?: number;\n /** Number of retry attempts (default: 0) */\n retries?: number;\n}\n\n/**\n * Result from successful postMessage authentication\n */\nexport interface PostMessageAuthResult {\n apiKey?: string;\n projectId?: string;\n baseUrl?: string;\n source: \"postmessage\";\n}\n\n/**\n * Custom error classes for postMessage authentication\n */\n\n/**\n * Error thrown when postMessage authentication times out\n */\nexport class PostMessageAuthTimeoutError extends Error {\n constructor(timeout: number) {\n super(\n `Parent window did not respond to authentication request within ${timeout}ms.\\n\\n` +\n `Possible causes:\\n` +\n `- Parent window is not listening for 'OXY_AUTH_REQUEST' messages\\n` +\n `- Parent origin mismatch\\n` +\n `- Network/browser issues\\n\\n` +\n `Troubleshooting:\\n` +\n `1. Verify parent window has message listener set up\\n` +\n `2. Check parentOrigin configuration matches parent window origin\\n` +\n `3. Open browser console in parent window for errors`,\n );\n this.name = \"PostMessageAuthTimeoutError\";\n }\n}\n\n/**\n * Error thrown when authentication response comes from unauthorized origin\n */\nexport class PostMessageAuthInvalidOriginError extends Error {\n constructor(expected: string, actual: string) {\n super(\n `Received authentication response from unauthorized origin.\\n\\n` +\n `Expected: ${expected}\\n` +\n `Actual: ${actual}\\n\\n` +\n `This is a security error. Verify your parentOrigin configuration.`,\n );\n this.name = \"PostMessageAuthInvalidOriginError\";\n }\n}\n\n/**\n * Error thrown when postMessage authentication is attempted outside iframe context\n */\nexport class PostMessageAuthNotInIframeError extends Error {\n constructor() {\n const currentContext = getCurrentContext();\n super(\n `PostMessage authentication is only available when running in an iframe context.\\n\\n` +\n `Current context: ${currentContext}\\n\\n` +\n `If you're running in a regular browser window, use direct configuration instead:\\n` +\n `const client = new OxyClient({ apiKey: 'your-key', ... })`,\n );\n this.name = \"PostMessageAuthNotInIframeError\";\n }\n}\n\nfunction getCurrentContext(): string {\n if (typeof window === \"undefined\") {\n return \"non-browser (Node.js)\";\n }\n if (window === window.parent) {\n return \"top-level window\";\n }\n return \"iframe\";\n}\n\n/**\n * Error thrown when authentication response is malformed or invalid\n */\nexport class PostMessageAuthInvalidResponseError extends Error {\n constructor(reason: string) {\n super(`Invalid authentication response from parent: ${reason}`);\n this.name = \"PostMessageAuthInvalidResponseError\";\n }\n}\n","/**\n * PostMessage-based authentication for iframe scenarios\n *\n * This module enables secure cross-origin authentication between an iframe\n * (running the Oxy SDK) and its parent window (holding the API key).\n */\n\nimport type {\n OxyAuthRequestMessage,\n OxyAuthResponseMessage,\n PostMessageAuthOptions,\n PostMessageAuthResult,\n} from \"../types\";\n\nimport {\n PostMessageAuthTimeoutError,\n PostMessageAuthNotInIframeError,\n PostMessageAuthInvalidResponseError,\n} from \"../types\";\n\n/**\n * Check if the current context is running inside an iframe\n *\n * @returns true if running in an iframe, false otherwise (including Node.js)\n */\nexport function isInIframe(): boolean {\n // Check if we're in a browser environment\n if (typeof window === \"undefined\") {\n return false;\n }\n\n // Check if window has a parent and it's different from itself\n try {\n return window !== window.parent && window.parent !== null;\n } catch {\n // Cross-origin access might throw an error, but we're still in an iframe\n return true;\n }\n}\n\n/**\n * Generate a unique request ID for tracking auth requests\n *\n * @returns A unique identifier string\n */\nexport function generateRequestId(): string {\n // Use crypto.randomUUID if available (modern browsers)\n if (typeof crypto !== \"undefined\" && crypto.randomUUID) {\n return crypto.randomUUID();\n }\n\n // Fallback for older environments: timestamp + random\n // Note: Math.random() is used here as a fallback for environments without crypto.randomUUID\n // This is acceptable for non-security-critical request IDs (used only for message correlation)\n /* eslint-disable sonarjs/pseudo-random */\n return `${Date.now()}-${Math.random().toString(36).substring(2, 11)}`;\n /* eslint-enable sonarjs/pseudo-random */\n}\n\n/**\n * Validate that a message origin matches the expected origin\n *\n * @param messageOrigin - The origin from the message event\n * @param allowedOrigin - The expected/allowed origin\n * @returns true if origin is valid, false otherwise\n */\nexport function validateOrigin(\n messageOrigin: string,\n allowedOrigin?: string,\n): boolean {\n // If no allowed origin is specified, reject (security)\n if (!allowedOrigin) {\n return false;\n }\n\n // Wildcard - allow any origin (development only!)\n if (allowedOrigin === \"*\") {\n return true;\n }\n\n // Exact match\n if (messageOrigin === allowedOrigin) {\n return true;\n }\n\n // Try URL parsing for more flexible matching\n try {\n const messageUrl = new URL(messageOrigin);\n const allowedUrl = new URL(allowedOrigin);\n return messageUrl.origin === allowedUrl.origin;\n } catch {\n // If URL parsing fails, do simple string match\n return messageOrigin === allowedOrigin;\n }\n}\n\n/**\n * Create a promise that listens for an authentication response\n *\n * @param requestId - The request ID to match against\n * @param origin - The expected parent origin\n * @param timeout - Timeout in milliseconds\n * @returns Promise that resolves with the auth response\n */\nfunction createAuthListener(\n requestId: string,\n origin: string | undefined,\n timeout: number,\n): Promise<OxyAuthResponseMessage> {\n return new Promise((resolve, reject) => {\n // Set up message listener\n const listener = (event: MessageEvent) => {\n // Validate origin\n if (!validateOrigin(event.origin, origin)) {\n // Don't reject here - might be other postMessage traffic\n // Just ignore messages from wrong origins\n return;\n }\n\n // Check message type\n if (!event.data || event.data.type !== \"OXY_AUTH_RESPONSE\") {\n // Not our message type, ignore\n return;\n }\n\n const response = event.data as OxyAuthResponseMessage;\n\n // Validate request ID matches\n if (response.requestId !== requestId) {\n // Wrong request ID, ignore (might be another auth in progress)\n return;\n }\n\n // Validate version\n if (response.version !== \"1.0\") {\n clearTimeout(timeoutId);\n window.removeEventListener(\"message\", listener);\n reject(\n new PostMessageAuthInvalidResponseError(\n `Unsupported protocol version: ${response.version}`,\n ),\n );\n return;\n }\n\n // Success!\n clearTimeout(timeoutId);\n window.removeEventListener(\"message\", listener);\n resolve(response);\n };\n\n // Set up timeout\n const timeoutId = setTimeout(() => {\n window.removeEventListener(\"message\", listener);\n reject(new PostMessageAuthTimeoutError(timeout));\n }, timeout);\n\n // Start listening\n window.addEventListener(\"message\", listener);\n });\n}\n\n/**\n * Request authentication from parent window via postMessage\n *\n * This is the main entry point for iframe-based authentication.\n * It sends a request to the parent window and waits for a response.\n *\n * @param options - Configuration options for the auth request\n * @returns Promise that resolves with authentication credentials\n * @throws {PostMessageAuthNotInIframeError} If not in an iframe\n * @throws {PostMessageAuthTimeoutError} If parent doesn't respond in time\n * @throws {PostMessageAuthInvalidOriginError} If response from wrong origin\n * @throws {PostMessageAuthInvalidResponseError} If response is malformed\n *\n * @example\n * ```typescript\n * const auth = await requestAuthFromParent({\n * parentOrigin: 'https://app.example.com',\n * timeout: 5000\n * });\n * console.log('Received API key:', auth.apiKey);\n * ```\n */\nexport async function requestAuthFromParent(\n options: PostMessageAuthOptions = {},\n): Promise<PostMessageAuthResult> {\n const { parentOrigin, timeout = 5000, retries = 0 } = options;\n\n // Validate we're in an iframe\n if (!isInIframe()) {\n throw new PostMessageAuthNotInIframeError();\n }\n\n // Validate we're in a browser environment\n if (typeof window === \"undefined\") {\n throw new PostMessageAuthNotInIframeError();\n }\n\n // Generate request ID\n const requestId = generateRequestId();\n\n // Create request message\n const request: OxyAuthRequestMessage = {\n type: \"OXY_AUTH_REQUEST\",\n version: \"1.0\",\n timestamp: Date.now(),\n requestId,\n };\n\n // Attempt authentication with retries\n let lastError: Error | null = null;\n const maxAttempts = retries + 1;\n\n for (let attempt = 0; attempt < maxAttempts; attempt++) {\n try {\n // Set up listener before sending request to avoid race condition\n const responsePromise = createAuthListener(\n requestId,\n parentOrigin,\n timeout,\n );\n\n // Send request to parent\n const targetOrigin = parentOrigin || \"*\";\n window.parent.postMessage(request, targetOrigin);\n\n // Wait for response\n const response = await responsePromise;\n\n // Return successful result\n return {\n apiKey: response.apiKey,\n projectId: response.projectId,\n baseUrl: response.baseUrl,\n source: \"postmessage\",\n };\n } catch (error) {\n lastError = error as Error;\n\n // If this isn't a timeout, don't retry\n if (!(error instanceof PostMessageAuthTimeoutError)) {\n throw error;\n }\n\n // If we have more attempts, continue\n if (attempt < maxAttempts - 1) {\n // Optional: Add a small delay before retry\n await new Promise((resolve) => setTimeout(resolve, 100));\n continue;\n }\n\n // All retries exhausted\n throw error;\n }\n }\n\n // Should never reach here, but TypeScript needs this\n throw lastError || new Error(\"Authentication failed\");\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,79 +1,78 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@oxy-hq/sdk",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "TypeScript SDK for interacting with Oxy data platform",
|
|
5
|
-
"main": "./dist/index.js",
|
|
6
|
-
"module": "./dist/index.mjs",
|
|
7
|
-
"types": "./dist/index.d.ts",
|
|
8
|
-
"type": "module",
|
|
9
|
-
"exports": {
|
|
10
|
-
".": {
|
|
11
|
-
"types": "./dist/index.d.ts",
|
|
12
|
-
"require": "./dist/index.js",
|
|
13
|
-
"import": "./dist/index.mjs"
|
|
14
|
-
}
|
|
15
|
-
},
|
|
16
|
-
"files": [
|
|
17
|
-
"dist",
|
|
18
|
-
"README.md",
|
|
19
|
-
"LICENSE"
|
|
20
|
-
],
|
|
21
|
-
"scripts": {
|
|
22
|
-
"build": "tsdown",
|
|
23
|
-
"build:watch": "tsdown --watch",
|
|
24
|
-
"dev": "tsdown --watch",
|
|
25
|
-
"typecheck": "tsc --noEmit",
|
|
26
|
-
"lint": "ESLINT_USE_FLAT_CONFIG=false eslint src --ext .ts",
|
|
27
|
-
"test": "vitest run",
|
|
28
|
-
"test:watch": "vitest",
|
|
29
|
-
"prepublishOnly": "pnpm build",
|
|
30
|
-
"publish:beta": "npm publish --tag beta",
|
|
31
|
-
"publish:latest": "npm publish --tag latest",
|
|
32
|
-
"demo:react": "cd examples/react-vite-demo && pnpm install --ignore-workspace && pnpm dev",
|
|
33
|
-
"demo:setup": "cd examples/react-vite-demo && pnpm install --ignore-workspace"
|
|
34
|
-
},
|
|
35
|
-
"keywords": [
|
|
36
|
-
"oxy",
|
|
37
|
-
"data",
|
|
38
|
-
"analytics",
|
|
39
|
-
"sdk",
|
|
40
|
-
"parquet",
|
|
41
|
-
"duckdb"
|
|
42
|
-
],
|
|
43
|
-
"author": "Oxy Team",
|
|
44
|
-
"license": "MIT",
|
|
45
|
-
"repository": {
|
|
46
|
-
"type": "git",
|
|
47
|
-
"url": "https://github.com/dataframehq/oxy-internal.git",
|
|
48
|
-
"directory": "sdk/typescript"
|
|
49
|
-
},
|
|
50
|
-
"bugs": {
|
|
51
|
-
"url": "https://github.com/dataframehq/oxy-internal/issues"
|
|
52
|
-
},
|
|
53
|
-
"homepage": "https://oxy.tech",
|
|
54
|
-
"peerDependencies": {
|
|
55
|
-
"@duckdb/duckdb-wasm": "^1.29.0",
|
|
56
|
-
"react": "^19.0.0"
|
|
57
|
-
},
|
|
58
|
-
"peerDependenciesMeta": {
|
|
59
|
-
"react": {
|
|
60
|
-
"optional": true
|
|
61
|
-
}
|
|
62
|
-
},
|
|
63
|
-
"
|
|
64
|
-
|
|
65
|
-
"@
|
|
66
|
-
"@types/
|
|
67
|
-
"@
|
|
68
|
-
"@typescript-eslint/
|
|
69
|
-
"
|
|
70
|
-
"
|
|
71
|
-
"
|
|
72
|
-
"
|
|
73
|
-
"
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@oxy-hq/sdk",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "TypeScript SDK for interacting with Oxy data platform",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"module": "./dist/index.mjs",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"type": "module",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"require": "./dist/index.js",
|
|
13
|
+
"import": "./dist/index.mjs"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist",
|
|
18
|
+
"README.md",
|
|
19
|
+
"LICENSE"
|
|
20
|
+
],
|
|
21
|
+
"scripts": {
|
|
22
|
+
"build": "tsdown",
|
|
23
|
+
"build:watch": "tsdown --watch",
|
|
24
|
+
"dev": "tsdown --watch",
|
|
25
|
+
"typecheck": "tsc --noEmit",
|
|
26
|
+
"lint": "ESLINT_USE_FLAT_CONFIG=false eslint src --ext .ts",
|
|
27
|
+
"test": "vitest run",
|
|
28
|
+
"test:watch": "vitest",
|
|
29
|
+
"prepublishOnly": "pnpm build",
|
|
30
|
+
"publish:beta": "npm publish --tag beta",
|
|
31
|
+
"publish:latest": "npm publish --tag latest",
|
|
32
|
+
"demo:react": "cd examples/react-vite-demo && pnpm install --ignore-workspace && pnpm dev",
|
|
33
|
+
"demo:setup": "cd examples/react-vite-demo && pnpm install --ignore-workspace"
|
|
34
|
+
},
|
|
35
|
+
"keywords": [
|
|
36
|
+
"oxy",
|
|
37
|
+
"data",
|
|
38
|
+
"analytics",
|
|
39
|
+
"sdk",
|
|
40
|
+
"parquet",
|
|
41
|
+
"duckdb"
|
|
42
|
+
],
|
|
43
|
+
"author": "Oxy Team",
|
|
44
|
+
"license": "MIT",
|
|
45
|
+
"repository": {
|
|
46
|
+
"type": "git",
|
|
47
|
+
"url": "https://github.com/dataframehq/oxy-internal.git",
|
|
48
|
+
"directory": "sdk/typescript"
|
|
49
|
+
},
|
|
50
|
+
"bugs": {
|
|
51
|
+
"url": "https://github.com/dataframehq/oxy-internal/issues"
|
|
52
|
+
},
|
|
53
|
+
"homepage": "https://oxy.tech",
|
|
54
|
+
"peerDependencies": {
|
|
55
|
+
"@duckdb/duckdb-wasm": "^1.29.0",
|
|
56
|
+
"react": "^19.0.0"
|
|
57
|
+
},
|
|
58
|
+
"peerDependenciesMeta": {
|
|
59
|
+
"react": {
|
|
60
|
+
"optional": true
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
"devDependencies": {
|
|
64
|
+
"@duckdb/duckdb-wasm": "^1.32.0",
|
|
65
|
+
"@types/node": "^25.2.3",
|
|
66
|
+
"@types/react": "^19.2.14",
|
|
67
|
+
"@typescript-eslint/eslint-plugin": "^8.55.0",
|
|
68
|
+
"@typescript-eslint/parser": "^8.55.0",
|
|
69
|
+
"eslint": "^10.0.0",
|
|
70
|
+
"react": "^19.2.4",
|
|
71
|
+
"tsdown": "^0.20.3",
|
|
72
|
+
"typescript": "^5.9.3",
|
|
73
|
+
"vitest": "^4.0.18"
|
|
74
|
+
},
|
|
75
|
+
"engines": {
|
|
76
|
+
"node": ">=18.0.0"
|
|
77
|
+
}
|
|
78
|
+
}
|