@iankibetsh/vue-streamline 1.3.3 β†’ 1.3.4

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 CHANGED
@@ -2,71 +2,58 @@
2
2
 
3
3
  A robust Vue 3 plugin designed for seamless integration with Streamline backend services. It delivers reactive state management, intelligent caching, and dynamic action invocation.
4
4
 
5
- ## Installation
5
+ ## πŸš€ Key Features
6
+
7
+ - **Zero-Config Actions**: Invoke backend methods directly via a JavaScript Proxy.
8
+ - **Stateful Streams**: Initial arguments are persisted across all subsequent action calls.
9
+ - **Object Argument Mapping**: Pass objects that automatically populate backend `request()` data and map to method parameters.
10
+ - **Reactive State**: Automatically syncs public properties from your backend Stream classes.
11
+ - **Smart Caching**: Optional local storage caching for instant data availability.
12
+ - **Security & Privacy**: Automatically filters out internal methods and properties from the response payload.
13
+
14
+ ## πŸ“¦ Installation
6
15
 
7
16
  ```bash
8
17
  npm install @iankibetsh/vue-streamline
9
18
  ```
10
19
 
11
- ## Setup
20
+ ## βš™οΈ Setup
12
21
 
13
22
  ### 1. Register the Plugin
14
23
 
15
24
  ```javascript
16
- import { createApp } from 'vue';
17
- import { streamline } from '@iankibetsh/vue-streamline';
18
- import App from './App.vue';
25
+ import { createApp } from "vue";
26
+ import { streamline } from "@iankibetsh/vue-streamline";
27
+ import App from "./App.vue";
19
28
 
20
29
  const app = createApp(App);
21
30
 
22
- // Define authentication headers
23
- const streamlineHeaders = {
24
- Authorization: `Bearer ${localStorage.getItem('access_token')}`
25
- };
26
-
27
- // Construct the Streamline endpoint URL
28
- const streamlineUrl = `${import.meta.env.VITE_APP_API_URL}streamline`;
29
-
30
31
  app.use(streamline, {
31
- streamlineHeaders,
32
- streamlineUrl,
33
- enableCache: true // Optional: enables local storage caching
32
+ streamlineUrl: "https://your-api.com/api/streamline",
33
+ enableCache: true, // Optional
34
34
  });
35
35
 
36
- app.mount('#app');
36
+ app.mount("#app");
37
37
  ```
38
38
 
39
39
  ### 2. Use in Components
40
40
 
41
- #### Option A: Full Streamline Integration
42
-
43
41
  ```vue
44
42
  <script setup>
45
- import { useStreamline } from '@iankibetsh/vue-streamline';
46
-
47
- const { service, loading, props, getActionUrl } = useStreamline('users', 1);
48
- </script>
49
- ```
50
-
51
- #### Option B: Standalone `getActionUrl`
43
+ import { useStreamline } from "@iankibetsh/vue-streamline";
52
44
 
53
- If you only need to generate action URLs without the full reactive service:
54
-
55
- ```vue
56
- <script setup>
57
- import { getActionUrl } from '@iankibetsh/vue-streamline';
45
+ // Pass initial arguments (e.g., ID 42)
46
+ // These are PERSISTED for all future actions on this service
47
+ const { service, loading, props } = useStreamline("project", 42);
58
48
 
59
- // Use directly in template or script
60
- const downloadUrl = getActionUrl('reports:download', reportId, 'pdf');
49
+ const update = async () => {
50
+ // Backend receives taskId=42 in constructor automatically
51
+ await service.updateStatus("active");
52
+ };
61
53
  </script>
62
-
63
- <template>
64
- <a :href="getActionUrl('users:export', userId, 'csv')">Export User</a>
65
- <h3>Action URL: {{ getActionUrl('users:listUsers', 'admin', 'active') }}</h3>
66
- </template>
67
54
  ```
68
55
 
69
- ## API Reference
56
+ ## πŸ“– API Reference
70
57
 
71
58
  ### `useStreamline(stream, ...initialArgs)`
72
59
 
@@ -74,364 +61,80 @@ Primary composable for interfacing with Streamline services.
74
61
 
75
62
  #### Parameters
76
63
 
77
- - **`stream`** (`string`): Name of the target stream or service.
78
- - **`...initialArgs`** (`any`): Optional arguments passed to the stream’s `onMounted` action.
64
+ - **`stream`** (`string`): Name of the target stream class on the backend.
65
+ - **`...initialArgs`** (`any`): Arguments passed to the backend constructor. **These are persisted for every subsequent action call.**
79
66
 
80
67
  #### Returns
81
68
 
82
- An object with the following reactive properties and utilities:
83
-
84
- | Property | Type | Description |
85
- |------------------|-----------------------|-------------|
86
- | `service` | Reactive Proxy | Proxy for invoking stream actions dynamically. |
87
- | `loading` | `ref<boolean>` | Indicates ongoing operations. |
88
- | `props` | Reactive Proxy | Holds properties fetched from the stream. |
89
- | `getActionUrl` | `Function` | Generates URLs for specific actions. |
90
- | `confirmAction` | `Function` | Displays confirmation dialogs before actions. |
69
+ | Property | Type | Description |
70
+ | --------------- | -------------- | -------------------------------------------------------------- |
71
+ | `service` | Reactive Proxy | Proxy for invoking backend methods. |
72
+ | `loading` | `ref<boolean>` | `true` when a request is in flight. |
73
+ | `props` | Reactive Proxy | Holds public properties fetched from the backend. |
74
+ | `getActionUrl` | `Function` | Generates URLs for actions (downloads, exports). |
75
+ | `confirmAction` | `Function` | Wrapper for triggering a confirmation dialog before an action. |
91
76
 
92
77
  ---
93
78
 
94
- ### `getActionUrl(action, ...args)`
79
+ ## πŸ§™ Advanced Features
95
80
 
96
- Standalone function for generating action URLs without reactive features.
81
+ ### 1. Persistent State
97
82
 
98
- #### Parameters
99
-
100
- - **`action`** (`string`): Action name, optionally prefixed with stream name (e.g., `'stream:action'`).
101
- - **`...args`** (`any`): Arguments to pass as URL parameters.
102
-
103
- #### Returns
104
-
105
- - **`string`**: Fully qualified URL for the specified action.
106
-
107
- #### Usage
83
+ Unlike traditional APIs where you send the ID every time, Streamline remembers:
108
84
 
109
85
  ```javascript
110
- import { getActionUrl } from '@iankibetsh/vue-streamline';
86
+ const { service } = useStreamline("user", 123);
111
87
 
112
- // Simple action URL
113
- const url = getActionUrl('download', fileId);
88
+ // Backend runs: new UserStream(123)->changePassword('secret')
89
+ await service.changePassword("secret");
114
90
 
115
- // Cross-stream action
116
- const analyticsUrl = getActionUrl('analytics:track', eventName, userId);
117
-
118
- // Multiple parameters
119
- const reportUrl = getActionUrl('reports:generate', reportId, 'pdf', '2024');
91
+ // Backend still runs: new UserStream(123)->getLogs()
92
+ await service.getLogs();
120
93
  ```
121
94
 
122
- ---
123
-
124
- ## Features
95
+ ### 2. Method Object Mapping
125
96
 
126
- ### 1. Dynamic Action Calling
97
+ You can pass objects to methods. Streamline will:
127
98
 
128
- Invoke backend actions via the `service` proxy:
99
+ 1. Merge the object into the backend `request()` data.
100
+ 2. Automatically map object keys to specific method parameters if they match by name.
129
101
 
130
102
  ```javascript
131
- const { service, loading } = useStreamline('users');
132
-
133
- // Standard CRUD operations
134
- await service.fetchAll();
135
- await service.create({ name: 'John', email: 'john@example.com' });
136
- await service.update(1, { name: 'Jane' });
137
- await service.delete(5);
138
-
139
- // Custom actions with multiple arguments
140
- await service.customAction(arg1, arg2, arg3);
103
+ // Frontend
104
+ service.updateProfile(123, { bio: 'New bio', age: 30 });
105
+
106
+ // Backend
107
+ public function updateProfile($userId, $bio) {
108
+ // $userId = 123
109
+ // $bio = 'New bio' (auto-mapped from object)
110
+ // request('age') = 30
111
+ }
141
112
  ```
142
113
 
143
- ### 2. Reactive Properties
114
+ ### 3. Confirmation Dialogs
144
115
 
145
- Properties are fetched automatically upon component mount or first access:
116
+ Prompt users before destructive actions:
146
117
 
147
118
  ```javascript
148
- const { props, loading } = useStreamline('dashboard', userId);
149
-
150
- // Properties trigger fetch on access if not yet loaded
151
- console.log(props.statistics);
152
- console.log(props.userInfo);
153
- console.log(props.settings);
154
- ```
155
-
156
- **Auto-fetch triggers:**
157
- - Component `onMounted` (when `initialArgs` are provided)
158
- - First property access on the `props` proxy
159
-
160
- ### 3. Loading States
161
-
162
- Monitor operation status reactively:
163
-
164
- ```vue
165
- <template>
166
- <div>
167
- <div v-if="loading">Loading...</div>
168
- <div v-else>
169
- <button @click="service.fetchData()">Fetch Data</button>
170
- </div>
171
- </div>
172
- </template>
173
-
174
- <script setup>
175
- import { useStreamline } from '@iankibetsh/vue-streamline';
176
-
177
- const { service, loading } = useStreamline('data');
178
- </script>
119
+ await service.confirm("Delete this user?").delete();
179
120
  ```
180
121
 
181
- ### 4. Local Storage Caching
122
+ ### 4. Cross-Stream Actions
182
123
 
183
- Enable persistent caching across sessions:
124
+ Execute actions on different streams using colon notation:
184
125
 
185
126
  ```javascript
186
- app.use(streamline, {
187
- streamlineUrl: 'https://your-api.com/streamline',
188
- enableCache: true
189
- });
127
+ await service["logs:clear"]();
190
128
  ```
191
129
 
192
- **Behavior when enabled:**
193
- - Data is stored in `localStorage` using a unique key (stream + arguments).
194
- - Cached data loads instantly on mount.
195
- - Cache updates after successful fetch operations.
196
- - Keys are deterministic and scoped per stream and arguments.
197
-
198
130
  ### 5. Manual Refresh
199
131
 
200
132
  Force reload of stream properties:
201
133
 
202
134
  ```javascript
203
- const { service } = useStreamline('products', categoryId);
204
-
205
- // Refresh properties
206
135
  await service.refresh(); // or service.reload()
207
136
  ```
208
137
 
209
- ### 6. Confirmation Dialogs
210
-
211
- Prompt users before destructive actions:
212
-
213
- ```javascript
214
- const { service } = useStreamline('users');
215
-
216
- // Default confirmation message
217
- await service.confirm().delete(userId);
218
-
219
- // Custom message
220
- await service.confirm('Are you sure you want to delete this user?').delete(userId);
221
- ```
222
-
223
- > Uses `shRepo.runPlainRequest` from the SH Framework for native confirmation dialogs.
224
-
225
- ### 7. Action URLs
226
-
227
- Generate fully qualified action URLs:
228
-
229
- #### Using `getActionUrl` from `useStreamline`
230
-
231
- ```javascript
232
- const { getActionUrl } = useStreamline('reports');
233
-
234
- const downloadUrl = getActionUrl('download', reportId, 'pdf');
235
- // β†’ https://your-api.com/streamline?action=download&stream=reports&params=reportId,pdf
236
-
237
- // Cross-stream actions
238
- const analyticsUrl = getActionUrl('analytics:trackEvent', eventName, eventData);
239
- ```
240
-
241
- #### Using Standalone `getActionUrl`
242
-
243
- For scenarios where you only need URL generation without reactive state management:
244
-
245
- ```vue
246
- <script setup>
247
- import { getActionUrl } from '@iankibetsh/vue-streamline';
248
-
249
- // Generate URLs directly
250
- const exportUrl = getActionUrl('users:export', userId, 'csv');
251
- const reportUrl = getActionUrl('reports:generate', reportId, 'pdf');
252
- </script>
253
-
254
- <template>
255
- <!-- Use in templates -->
256
- <a :href="getActionUrl('users:export', userId, 'csv')" download>
257
- Export User
258
- </a>
259
-
260
- <!-- Dynamic URLs -->
261
- <div>
262
- <h3>API Endpoint: {{ getActionUrl('users:listUsers', 'admin', 'active') }}</h3>
263
- </div>
264
- </template>
265
- ```
266
-
267
- **Benefits of Standalone Import:**
268
- - Lighter weight when you don't need reactive features
269
- - Can be used in utility files or non-component contexts
270
- - Still respects the global `streamlineUrl` configuration
271
- - Supports all the same features (cross-stream notation, multiple parameters)
272
-
273
- ### 8. Cross-Stream Actions
274
-
275
- Execute actions on different streams using colon notation:
276
-
277
- ```javascript
278
- const { service } = useStreamline('users');
279
-
280
- await service['analytics:trackEvent'](eventName, eventData);
281
- ```
282
-
283
- ## Advanced Usage
284
-
285
- ### Immediate Property Access
286
-
287
- Access properties before loading β€” fetch triggers automatically:
288
-
289
- ```javascript
290
- const { props } = useStreamline('dashboard', userId);
291
-
292
- watchEffect(() => {
293
- console.log(props.stats); // Triggers fetch if needed
294
- });
295
- ```
296
-
297
- ### Error Handling
298
-
299
- All action calls return promises:
300
-
301
- ```javascript
302
- const { service } = useStreamline('users');
303
-
304
- try {
305
- const result = await service.create(userData);
306
- console.log('User created:', result);
307
- } catch (error) {
308
- console.error('Creation failed:', error);
309
- }
310
- ```
311
-
312
- ### Form Data Integration
313
-
314
- Pass structured data to actions:
315
-
316
- ```javascript
317
- const { service } = useStreamline('posts');
318
-
319
- const formData = {
320
- title: 'My Post',
321
- content: 'Post content',
322
- tags: ['vue', 'javascript']
323
- };
324
-
325
- await service.create(formData);
326
- ```
327
-
328
- ## Complete Example
329
-
330
- ```vue
331
- <template>
332
- <div class="user-management">
333
- <div v-if="loading" class="loading">Loading...</div>
334
-
335
- <div v-else>
336
- <h1>Total Users: {{ props.totalUsers }}</h1>
337
-
338
- <div class="users-list">
339
- <div v-for="user in props.users" :key="user.id" class="user-card">
340
- <h3>{{ user.name }}</h3>
341
- <p>{{ user.email }}</p>
342
-
343
- <button @click="editUser(user)">Edit</button>
344
- <button @click="deleteUser(user.id)">Delete</button>
345
-
346
- <a :href="getActionUrl('exportUser', user.id)" target="_blank" rel="noopener">
347
- Export Profile
348
- </a>
349
- </div>
350
- </div>
351
-
352
- <button @click="refreshData">Refresh</button>
353
- <button @click="addNewUser">Add User</button>
354
- </div>
355
- </div>
356
- </template>
357
-
358
- <script setup>
359
- import { useStreamline } from '@iankibetsh/vue-streamline';
360
-
361
- const { service, loading, props, getActionUrl } = useStreamline('users', 'active');
362
-
363
- const editUser = async (user) => {
364
- try {
365
- const result = await service.update(user.id, {
366
- name: user.name,
367
- email: user.email
368
- });
369
- console.log('User updated:', result);
370
- } catch (error) {
371
- console.error('Update failed:', error);
372
- }
373
- };
374
-
375
- const deleteUser = async (userId) => {
376
- try {
377
- await service.confirm('Delete this user permanently?').delete(userId);
378
- await service.refresh();
379
- } catch (error) {
380
- console.error('Delete failed:', error);
381
- }
382
- };
383
-
384
- const refreshData = () => service.refresh();
385
-
386
- const addNewUser = async () => {
387
- const newUser = { name: 'New User', email: 'newuser@example.com' };
388
- await service.create(newUser);
389
- await service.refresh();
390
- };
391
- </script>
392
- ```
393
-
394
- ## Best Practices
395
-
396
- 1. **Use descriptive stream names** aligned with backend services.
397
- 2. **Enable caching** for infrequently updated data.
398
- 3. **Handle errors gracefully** in component logic.
399
- 4. **Display loading states** for better user experience.
400
- 5. **Use confirmation dialogs** for irreversible actions.
401
- 6. **Leverage `getActionUrl`** for links and downloads.
402
- 7. **Call `refresh()`** after mutations to synchronize UI.
403
-
404
- ## `getActionUrl` Function Details
405
-
406
- ```javascript
407
- const getActionUrl = (action, ...args) => {
408
- let targetStream = stream;
409
- let targetAction = action;
410
-
411
- if (action.includes(':')) {
412
- [targetStream, targetAction] = action.split(':');
413
- }
414
-
415
- const payload = {
416
- action: targetAction,
417
- stream: targetStream,
418
- params: args
419
- };
420
-
421
- return `${streamlineUrl}?${new URLSearchParams(payload).toString()}`;
422
- };
423
- ```
424
-
425
- **Key Capabilities:**
426
- - Serializes arguments into query parameters.
427
- - Supports cross-stream routing via `stream:action`.
428
- - Produces ready-to-use, fully qualified URLs.
429
-
430
- ## Dependencies
431
-
432
- - **Vue 3**: `reactive`, `ref`, `inject`, `onMounted`
433
- - **@iankibetsh/shframework**: `shApis`, `shRepo`
434
-
435
- ## License
138
+ ## πŸ“„ License
436
139
 
437
- MIT
140
+ MIT
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@iankibetsh/vue-streamline",
3
- "version": "1.3.3",
3
+ "version": "1.3.4",
4
4
  "description": "Vue library for streamlining laravel backend services with @iankibet/streamline composer package",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -15,8 +15,8 @@
15
15
  "author": "Iankibet",
16
16
  "license": "ISC",
17
17
  "devDependencies": {
18
- "@vitejs/plugin-vue": "^5.1.2",
19
- "vite": "^5.4.1"
18
+ "@vitejs/plugin-vue": "^6",
19
+ "vite": "^7"
20
20
  },
21
21
  "peerDependencies": {
22
22
  "@iankibetsh/shframework": "^5",
package/src/App.vue CHANGED
@@ -3,7 +3,7 @@ import useStreamline from './composables/useStreamline.js'
3
3
  import { onMounted, ref, toRefs } from 'vue'
4
4
  import SimpleGetActionUrl from './SimpleGetActionUrl.vue'
5
5
 
6
- const {loading, service:paybillService, getActionUrl,props,confirmAction} = useStreamline('mpesa/paybill',true)
6
+ const {loading, service:paybillService, getActionUrl,props,confirmAction} = useStreamline('mpesa/paybill',32)
7
7
 
8
8
 
9
9
  const foundPaybill = ref(null)
@@ -18,7 +18,7 @@ const findPaybill = async ()=>{
18
18
  // res.getPaybill(32).then(res=>{
19
19
  // foundPaybill.value = res
20
20
  // })
21
- foundPaybill.value = await paybillService.confirm().getPaybill(32)
21
+ foundPaybill.value = await paybillService.confirm().getPaybill(32, {name: 'ian'})
22
22
  .catch(err=>{
23
23
  console.log(err)
24
24
  })
@@ -75,8 +75,14 @@ const useStreamline = (stream, ...initialArgs) => {
75
75
  action: prop,
76
76
  stream,
77
77
  ...formData,
78
- params: args
78
+ params: args,
79
+ initialParams: initialArgs
79
80
  }
81
+ args.forEach(arg => {
82
+ if (arg && typeof arg === 'object' && !Array.isArray(arg)) {
83
+ Object.assign(data, arg)
84
+ }
85
+ })
80
86
  if (confirmationMessage.value) {
81
87
  repo = shRepo.runPlainRequest(streamlineUrl,null, confirmationMessage.value,data)
82
88
  } else {