onairos 2.2.1 → 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,179 @@
1
+ /**
2
+ * Onairos Vite Plugin for Laravel
3
+ *
4
+ * This plugin provides seamless integration of Onairos components
5
+ * within Laravel Vite applications, handling both development and
6
+ * production builds.
7
+ */
8
+
9
+ import { resolve } from 'path';
10
+
11
+ export function onairosLaravelPlugin(options = {}) {
12
+ const defaultOptions = {
13
+ autoImport: true,
14
+ injectGlobals: true,
15
+ optimizeDeps: true,
16
+ enableHMR: true,
17
+ bladeSupport: true
18
+ };
19
+
20
+ const config = { ...defaultOptions, ...options };
21
+
22
+ return {
23
+ name: 'onairos-laravel',
24
+ config(viteConfig, { command }) {
25
+ // Optimize dependencies for faster dev server startup
26
+ if (config.optimizeDeps) {
27
+ viteConfig.optimizeDeps = viteConfig.optimizeDeps || {};
28
+ viteConfig.optimizeDeps.include = viteConfig.optimizeDeps.include || [];
29
+ viteConfig.optimizeDeps.include.push('onairos');
30
+ }
31
+
32
+ // Add alias for easier imports
33
+ viteConfig.resolve = viteConfig.resolve || {};
34
+ viteConfig.resolve.alias = viteConfig.resolve.alias || {};
35
+ viteConfig.resolve.alias['@onairos'] = resolve('node_modules/onairos');
36
+
37
+ // Configure for Laravel Blade support
38
+ if (config.bladeSupport && command === 'serve') {
39
+ viteConfig.server = viteConfig.server || {};
40
+ viteConfig.server.watch = viteConfig.server.watch || {};
41
+ viteConfig.server.watch.include = viteConfig.server.watch.include || [];
42
+ viteConfig.server.watch.include.push('resources/views/**/*.blade.php');
43
+ }
44
+ },
45
+
46
+ configureServer(server) {
47
+ if (config.enableHMR) {
48
+ // Add custom HMR handling for Onairos components
49
+ server.ws.on('onairos:reload', () => {
50
+ server.ws.send({
51
+ type: 'full-reload'
52
+ });
53
+ });
54
+ }
55
+ },
56
+
57
+ transformIndexHtml: {
58
+ enforce: 'pre',
59
+ transform(html, context) {
60
+ if (config.injectGlobals && context.server) {
61
+ // Inject Onairos initialization script for development
62
+ const initScript = `
63
+ <script type="module">
64
+ import { initializeOnairosForBlade } from '/node_modules/onairos/src/laravel/blade-helpers.js';
65
+
66
+ // Initialize Onairos for Laravel Blade templates
67
+ initializeOnairosForBlade({
68
+ testMode: true,
69
+ autoDetectMobile: true,
70
+ globalStyles: true
71
+ });
72
+
73
+ // Enable HMR for Onairos components
74
+ if (import.meta.hot) {
75
+ import.meta.hot.on('onairos:update', () => {
76
+ console.log('🔥 Onairos components updated');
77
+ window.location.reload();
78
+ });
79
+ }
80
+ </script>
81
+ `;
82
+
83
+ return html.replace('<head>', `<head>${initScript}`);
84
+ }
85
+ return html;
86
+ }
87
+ },
88
+
89
+ generateBundle(options, bundle) {
90
+ // Add Onairos assets to the build output
91
+ if (config.autoImport) {
92
+ // Create a separate chunk for Laravel integration
93
+ this.emitFile({
94
+ type: 'chunk',
95
+ id: 'onairos-laravel-integration',
96
+ fileName: 'onairos-laravel.js'
97
+ });
98
+ }
99
+ },
100
+
101
+ writeBundle(options, bundle) {
102
+ // Create Laravel-specific integration files
103
+ const laravelIntegrationCode = `
104
+ // Onairos Laravel Integration
105
+ import { initializeOnairosForBlade, createOnairosButton } from 'onairos/blade';
106
+
107
+ // Auto-initialize for production
108
+ document.addEventListener('DOMContentLoaded', () => {
109
+ initializeOnairosForBlade({
110
+ testMode: false,
111
+ autoDetectMobile: true,
112
+ globalStyles: true
113
+ });
114
+ });
115
+
116
+ // Export for manual usage
117
+ window.Onairos = {
118
+ init: initializeOnairosForBlade,
119
+ createButton: createOnairosButton
120
+ };
121
+ `;
122
+
123
+ // Write the integration file
124
+ this.emitFile({
125
+ type: 'asset',
126
+ fileName: 'onairos-laravel-integration.js',
127
+ source: laravelIntegrationCode
128
+ });
129
+ }
130
+ };
131
+ }
132
+
133
+ // Vue.js specific plugin for Laravel
134
+ export function onairosVuePlugin(options = {}) {
135
+ return {
136
+ name: 'onairos-vue-laravel',
137
+ config(viteConfig) {
138
+ // Add Vue-specific optimizations for Onairos
139
+ viteConfig.optimizeDeps = viteConfig.optimizeDeps || {};
140
+ viteConfig.optimizeDeps.include = viteConfig.optimizeDeps.include || [];
141
+ viteConfig.optimizeDeps.include.push('onairos', 'vue');
142
+ },
143
+
144
+ transform(code, id) {
145
+ // Auto-import Onairos in Vue components
146
+ if (id.endsWith('.vue') && options.autoImport) {
147
+ if (code.includes('OnairosButton') && !code.includes('import.*OnairosButton')) {
148
+ return `import { OnairosButton } from 'onairos';\n${code}`;
149
+ }
150
+ }
151
+ return null;
152
+ }
153
+ };
154
+ }
155
+
156
+ // React specific plugin for Laravel
157
+ export function onairosReactPlugin(options = {}) {
158
+ return {
159
+ name: 'onairos-react-laravel',
160
+ config(viteConfig) {
161
+ // Add React-specific optimizations for Onairos
162
+ viteConfig.optimizeDeps = viteConfig.optimizeDeps || {};
163
+ viteConfig.optimizeDeps.include = viteConfig.optimizeDeps.include || [];
164
+ viteConfig.optimizeDeps.include.push('onairos', 'react', 'react-dom');
165
+ },
166
+
167
+ transform(code, id) {
168
+ // Auto-import Onairos in React components
169
+ if ((id.endsWith('.jsx') || id.endsWith('.tsx')) && options.autoImport) {
170
+ if (code.includes('OnairosButton') && !code.includes('import.*OnairosButton')) {
171
+ return `import { OnairosButton } from 'onairos';\n${code}`;
172
+ }
173
+ }
174
+ return null;
175
+ }
176
+ };
177
+ }
178
+
179
+ export default onairosLaravelPlugin;
@@ -0,0 +1,283 @@
1
+ /**
2
+ * Laravel Blade Integration Example Test
3
+ *
4
+ * This test simulates a real Laravel application using Blade templates
5
+ * with Onairos integration.
6
+ */
7
+
8
+ import { describe, test, expect, beforeEach } from 'vitest';
9
+ import { JSDOM } from 'jsdom';
10
+ import { initializeOnairosForBlade, createOnairosButton } from '../../../src/laravel/blade-helpers.js';
11
+
12
+ describe('Laravel Blade Real-World Example', () => {
13
+ let dom;
14
+ let window;
15
+ let document;
16
+
17
+ beforeEach(() => {
18
+ // Create a DOM that mimics a Laravel Blade template
19
+ dom = new JSDOM(`
20
+ <!DOCTYPE html>
21
+ <html lang="en">
22
+ <head>
23
+ <meta charset="UTF-8">
24
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
25
+ <title>Laravel App with Onairos</title>
26
+ <meta name="csrf-token" content="fake-csrf-token">
27
+ </head>
28
+ <body>
29
+ <div class="container">
30
+ <nav class="navbar">
31
+ <h1>My Laravel App</h1>
32
+ </nav>
33
+
34
+ <main class="content">
35
+ <div class="dashboard">
36
+ <h2>User Dashboard</h2>
37
+ <p>Connect your social accounts to enhance your experience:</p>
38
+
39
+ <!-- Onairos button will be inserted here -->
40
+ <div id="social-connect-button" class="mt-4"></div>
41
+
42
+ <div class="user-profile mt-6">
43
+ <h3>Profile Enhancement</h3>
44
+ <p>Let AI analyze your data for better recommendations:</p>
45
+
46
+ <!-- Another Onairos button for advanced features -->
47
+ <div id="profile-enhancement-button" class="mt-2"></div>
48
+ </div>
49
+ </div>
50
+ </main>
51
+ </div>
52
+ </body>
53
+ </html>
54
+ `);
55
+
56
+ window = dom.window;
57
+ document = dom.window.document;
58
+
59
+ // Set globals for testing
60
+ global.window = window;
61
+ global.document = document;
62
+ global.navigator = window.navigator;
63
+ });
64
+
65
+ test('should integrate Onairos into Laravel dashboard page', () => {
66
+ // 1. Initialize Onairos as would be done in Laravel app.js
67
+ initializeOnairosForBlade({
68
+ testMode: true,
69
+ baseUrl: 'https://api2.onairos.uk',
70
+ autoDetectMobile: true,
71
+ globalStyles: true
72
+ });
73
+
74
+ // 2. Verify initialization worked
75
+ expect(window.OnairosConfig).toBeDefined();
76
+ expect(window.OnairosConfig.testMode).toBe(true);
77
+ expect(document.getElementById('onairos-styles')).not.toBeNull();
78
+
79
+ // 3. Create social connect button (as would be in Blade template script)
80
+ createOnairosButton('social-connect-button', {
81
+ requestData: ['email', 'profile', 'social'],
82
+ webpageName: 'Laravel Dashboard',
83
+ buttonType: 'pill',
84
+ textColor: 'white',
85
+ onComplete: function(result) {
86
+ console.log('Social connection completed:', result);
87
+ // In real Laravel app, this might update the UI or make AJAX calls
88
+ }
89
+ });
90
+
91
+ // 4. Create profile enhancement button
92
+ createOnairosButton('profile-enhancement-button', {
93
+ requestData: {
94
+ basic: { type: "basic", reward: "10 tokens" },
95
+ personality: { type: "personality", reward: "25 tokens" },
96
+ preferences: { type: "preferences", reward: "15 tokens" }
97
+ },
98
+ webpageName: 'Laravel Profile Enhancement',
99
+ buttonType: 'rounded',
100
+ textColor: 'black',
101
+ onComplete: function(result) {
102
+ console.log('Profile enhancement completed:', result);
103
+ }
104
+ });
105
+
106
+ // 5. Verify both buttons were created successfully
107
+ const socialButton = document.getElementById('social-connect-button-btn');
108
+ const profileButton = document.getElementById('profile-enhancement-button-btn');
109
+
110
+ expect(socialButton).not.toBeNull();
111
+ expect(profileButton).not.toBeNull();
112
+
113
+ // 6. Verify button configurations
114
+ const socialConfig = JSON.parse(socialButton.getAttribute('data-onairos-config'));
115
+ const profileConfig = JSON.parse(profileButton.getAttribute('data-onairos-config'));
116
+
117
+ expect(socialConfig.requestData).toEqual(['email', 'profile', 'social']);
118
+ expect(socialConfig.webpageName).toBe('Laravel Dashboard');
119
+ expect(socialConfig.buttonType).toBe('pill');
120
+
121
+ expect(profileConfig.requestData).toHaveProperty('basic');
122
+ expect(profileConfig.requestData).toHaveProperty('personality');
123
+ expect(profileConfig.webpageName).toBe('Laravel Profile Enhancement');
124
+ expect(profileConfig.buttonType).toBe('rounded');
125
+
126
+ // 7. Verify styling was applied
127
+ expect(socialButton.className).toContain('onairos-btn-pill');
128
+ expect(profileButton.className).toContain('onairos-btn-rounded');
129
+ });
130
+
131
+ test('should handle Laravel CSRF protection', () => {
132
+ // Laravel apps typically have CSRF tokens in meta tags
133
+ const csrfToken = document.querySelector('meta[name="csrf-token"]').getAttribute('content');
134
+ expect(csrfToken).toBe('fake-csrf-token');
135
+
136
+ // Initialize Onairos with CSRF handling
137
+ initializeOnairosForBlade({
138
+ csrfToken: csrfToken,
139
+ testMode: true
140
+ });
141
+
142
+ expect(window.OnairosConfig.csrfToken).toBe('fake-csrf-token');
143
+ });
144
+
145
+ test('should support Laravel environment variables pattern', () => {
146
+ // Mock Laravel Vite environment variables
147
+ global.import = {
148
+ meta: {
149
+ env: {
150
+ DEV: false,
151
+ PROD: true,
152
+ VITE_APP_NAME: 'Laravel App',
153
+ VITE_ONAIROS_API_KEY: 'prod-api-key',
154
+ VITE_ONAIROS_TEST_MODE: 'false'
155
+ }
156
+ }
157
+ };
158
+
159
+ // Initialize using Laravel environment pattern
160
+ initializeOnairosForBlade({
161
+ testMode: global.import.meta.env.VITE_ONAIROS_TEST_MODE === 'true',
162
+ apiKey: global.import.meta.env.VITE_ONAIROS_API_KEY,
163
+ appName: global.import.meta.env.VITE_APP_NAME
164
+ });
165
+
166
+ expect(window.OnairosConfig.testMode).toBe(false);
167
+ expect(window.OnairosConfig.apiKey).toBe('prod-api-key');
168
+ expect(window.OnairosConfig.appName).toBe('Laravel App');
169
+ });
170
+
171
+ test('should handle mobile responsive behavior in Laravel context', () => {
172
+ // Mock mobile user agent (iPhone)
173
+ Object.defineProperty(window.navigator, 'userAgent', {
174
+ writable: true,
175
+ value: 'Mozilla/5.0 (iPhone; CPU iPhone OS 14_0 like Mac OS X) AppleWebKit/605.1.15'
176
+ });
177
+
178
+ // Mock mobile viewport
179
+ Object.defineProperty(window, 'innerWidth', {
180
+ writable: true,
181
+ value: 375
182
+ });
183
+
184
+ initializeOnairosForBlade({
185
+ autoDetectMobile: true
186
+ });
187
+
188
+ expect(window.OnairosUtils.isMobile).toBe(true);
189
+
190
+ // Create a button and verify mobile-specific behavior
191
+ createOnairosButton('mobile-test-button', {
192
+ requestData: ['email'],
193
+ webpageName: 'Mobile Laravel App'
194
+ });
195
+
196
+ const button = document.getElementById('mobile-test-button-btn');
197
+ expect(button).not.toBeNull();
198
+
199
+ // In mobile context, button should have mobile-optimized styling
200
+ const styles = window.getComputedStyle ? window.getComputedStyle(button) : {};
201
+ // Note: JSDOM doesn't compute styles, but we can verify the classes are applied
202
+ expect(button.className).toContain('onairos-btn');
203
+ });
204
+
205
+ test('should simulate Laravel Ajax integration', async () => {
206
+ // Mock Laravel AJAX setup (similar to what Laravel includes by default)
207
+ window.axios = {
208
+ defaults: {
209
+ headers: {
210
+ common: {
211
+ 'X-CSRF-TOKEN': 'fake-csrf-token',
212
+ 'X-Requested-With': 'XMLHttpRequest'
213
+ }
214
+ }
215
+ },
216
+ post: vi.fn(() => Promise.resolve({ data: { success: true } }))
217
+ };
218
+
219
+ initializeOnairosForBlade({
220
+ testMode: true,
221
+ onAuthComplete: async function(result) {
222
+ // Simulate Laravel AJAX call to store Onairos data
223
+ try {
224
+ const response = await window.axios.post('/onairos/callback', {
225
+ user_hash: result.userHash,
226
+ connections: result.connectedAccounts,
227
+ data_types: result.requestData
228
+ });
229
+
230
+ if (response.data.success) {
231
+ console.log('Laravel backend updated successfully');
232
+ }
233
+ } catch (error) {
234
+ console.error('Laravel integration error:', error);
235
+ }
236
+ }
237
+ });
238
+
239
+ // Trigger a simulated auth completion
240
+ const mockResult = {
241
+ userHash: 'test-hash-123',
242
+ connectedAccounts: ['youtube', 'linkedin'],
243
+ requestData: ['email', 'profile']
244
+ };
245
+
246
+ // Simulate the callback
247
+ if (window.OnairosConfig.onAuthComplete) {
248
+ await window.OnairosConfig.onAuthComplete(mockResult);
249
+ }
250
+
251
+ // Verify Laravel AJAX was called
252
+ expect(window.axios.post).toHaveBeenCalledWith('/onairos/callback', {
253
+ user_hash: 'test-hash-123',
254
+ connections: ['youtube', 'linkedin'],
255
+ data_types: ['email', 'profile']
256
+ });
257
+ });
258
+
259
+ test('should work with Laravel Blade @vite directive pattern', () => {
260
+ // Simulate what happens when Laravel's @vite directive loads assets
261
+ const viteScript = document.createElement('script');
262
+ viteScript.type = 'module';
263
+ viteScript.textContent = `
264
+ // Simulate Vite module loading
265
+ import { initializeOnairosForBlade } from '/resources/js/onairos-setup.js';
266
+
267
+ // Auto-initialize when loaded via Vite
268
+ document.addEventListener('DOMContentLoaded', () => {
269
+ initializeOnairosForBlade({
270
+ testMode: import.meta.env.DEV,
271
+ baseUrl: import.meta.env.VITE_ONAIROS_BASE_URL
272
+ });
273
+ });
274
+ `;
275
+
276
+ document.head.appendChild(viteScript);
277
+
278
+ // Verify script was added (simulating Vite asset injection)
279
+ const addedScript = document.querySelector('script[type="module"]');
280
+ expect(addedScript).not.toBeNull();
281
+ expect(addedScript.textContent).toContain('initializeOnairosForBlade');
282
+ });
283
+ });