@iankibetsh/vue-streamline 1.3.3 β†’ 1.3.6

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,61 @@
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**: IndexedDB caching for instant data availability (enabled by default).
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, // Enabled by default, set to false to disable
34
34
  });
35
35
 
36
- app.mount('#app');
36
+ app.mount("#app");
37
37
  ```
38
38
 
39
- ### 2. Use in Components
39
+ > [!NOTE]
40
+ > Caching uses **IndexedDB** (`streamline_cache` database) for persistence, offering higher storage limits and better performance than standard local storage.
40
41
 
41
- #### Option A: Full Streamline Integration
42
+ ### 2. Use in Components
42
43
 
43
44
  ```vue
44
45
  <script setup>
45
- import { useStreamline } from '@iankibetsh/vue-streamline';
46
+ import { useStreamline } from "@iankibetsh/vue-streamline";
46
47
 
47
- const { service, loading, props, getActionUrl } = useStreamline('users', 1);
48
- </script>
49
- ```
48
+ // Pass initial arguments (e.g., ID 42)
49
+ // These are PERSISTED for all future actions on this service
50
+ const { service, loading, props } = useStreamline("project", 42);
50
51
 
51
- #### Option B: Standalone `getActionUrl`
52
-
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';
58
-
59
- // Use directly in template or script
60
- const downloadUrl = getActionUrl('reports:download', reportId, 'pdf');
52
+ const update = async () => {
53
+ // Backend receives taskId=42 in constructor automatically
54
+ await service.updateStatus("active");
55
+ };
61
56
  </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
57
  ```
68
58
 
69
- ## API Reference
59
+ ## πŸ“– API Reference
70
60
 
71
61
  ### `useStreamline(stream, ...initialArgs)`
72
62
 
@@ -74,364 +64,80 @@ Primary composable for interfacing with Streamline services.
74
64
 
75
65
  #### Parameters
76
66
 
77
- - **`stream`** (`string`): Name of the target stream or service.
78
- - **`...initialArgs`** (`any`): Optional arguments passed to the stream’s `onMounted` action.
67
+ - **`stream`** (`string`): Name of the target stream class on the backend.
68
+ - **`...initialArgs`** (`any`): Arguments passed to the backend constructor. **These are persisted for every subsequent action call.**
79
69
 
80
70
  #### Returns
81
71
 
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. |
72
+ | Property | Type | Description |
73
+ | --------------- | -------------- | -------------------------------------------------------------- |
74
+ | `service` | Reactive Proxy | Proxy for invoking backend methods. |
75
+ | `loading` | `ref<boolean>` | `true` when a request is in flight. |
76
+ | `props` | Reactive Proxy | Holds public properties fetched from the backend. |
77
+ | `getActionUrl` | `Function` | Generates URLs for actions (downloads, exports). |
78
+ | `confirmAction` | `Function` | Wrapper for triggering a confirmation dialog before an action. |
91
79
 
92
80
  ---
93
81
 
94
- ### `getActionUrl(action, ...args)`
82
+ ## πŸ§™ Advanced Features
95
83
 
96
- Standalone function for generating action URLs without reactive features.
84
+ ### 1. Persistent State
97
85
 
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
86
+ Unlike traditional APIs where you send the ID every time, Streamline remembers:
108
87
 
109
88
  ```javascript
110
- import { getActionUrl } from '@iankibetsh/vue-streamline';
89
+ const { service } = useStreamline("user", 123);
111
90
 
112
- // Simple action URL
113
- const url = getActionUrl('download', fileId);
91
+ // Backend runs: new UserStream(123)->changePassword('secret')
92
+ await service.changePassword("secret");
114
93
 
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');
94
+ // Backend still runs: new UserStream(123)->getLogs()
95
+ await service.getLogs();
120
96
  ```
121
97
 
122
- ---
123
-
124
- ## Features
98
+ ### 2. Method Object Mapping
125
99
 
126
- ### 1. Dynamic Action Calling
100
+ You can pass objects to methods. Streamline will:
127
101
 
128
- Invoke backend actions via the `service` proxy:
102
+ 1. Merge the object into the backend `request()` data.
103
+ 2. Automatically map object keys to specific method parameters if they match by name.
129
104
 
130
105
  ```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);
106
+ // Frontend
107
+ service.updateProfile(123, { bio: 'New bio', age: 30 });
108
+
109
+ // Backend
110
+ public function updateProfile($userId, $bio) {
111
+ // $userId = 123
112
+ // $bio = 'New bio' (auto-mapped from object)
113
+ // request('age') = 30
114
+ }
141
115
  ```
142
116
 
143
- ### 2. Reactive Properties
117
+ ### 3. Confirmation Dialogs
144
118
 
145
- Properties are fetched automatically upon component mount or first access:
119
+ Prompt users before destructive actions:
146
120
 
147
121
  ```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);
122
+ await service.confirm("Delete this user?").delete();
154
123
  ```
155
124
 
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:
125
+ ### 4. Cross-Stream Actions
163
126
 
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>
179
- ```
180
-
181
- ### 4. Local Storage Caching
182
-
183
- Enable persistent caching across sessions:
127
+ Execute actions on different streams using colon notation:
184
128
 
185
129
  ```javascript
186
- app.use(streamline, {
187
- streamlineUrl: 'https://your-api.com/streamline',
188
- enableCache: true
189
- });
130
+ await service["logs:clear"]();
190
131
  ```
191
132
 
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
133
  ### 5. Manual Refresh
199
134
 
200
135
  Force reload of stream properties:
201
136
 
202
137
  ```javascript
203
- const { service } = useStreamline('products', categoryId);
204
-
205
- // Refresh properties
206
138
  await service.refresh(); // or service.reload()
207
139
  ```
208
140
 
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
141
+ ## πŸ“„ License
436
142
 
437
- MIT
143
+ 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.6",
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
@@ -1,11 +1,11 @@
1
1
  <script setup>
2
2
  import useStreamline from './composables/useStreamline.js'
3
- import { onMounted, ref, toRefs } from 'vue'
3
+ import { onMounted, ref, toRefs,computed } from 'vue'
4
4
  import SimpleGetActionUrl from './SimpleGetActionUrl.vue'
5
5
 
6
- const {loading, service:paybillService, getActionUrl,props,confirmAction} = useStreamline('mpesa/paybill',true)
7
-
6
+ const {loading, service:paybillService, getActionUrl,props,confirmAction} = useStreamline('mpesa/paybill',32)
8
7
 
8
+ const existing = computed(()=>props.paybill)
9
9
  const foundPaybill = ref(null)
10
10
 
11
11
  onMounted(()=>{
@@ -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
  })
@@ -41,6 +41,12 @@ const refresh = ()=>{
41
41
  <h1 @click="refresh">Refresh</h1>
42
42
  {{ foundPaybill }}
43
43
  </div>
44
+ <div class="alert alert-info">
45
+ <h2>Existing</h2>
46
+ <p>
47
+ {{ existing }}
48
+ </p>
49
+ </div>
44
50
  <simple-get-action-url/>
45
51
  </template>
46
52
 
@@ -1,5 +1,6 @@
1
1
  import { inject, onMounted, reactive, ref } from 'vue'
2
2
  import { shApis, shRepo } from '@iankibetsh/shframework'
3
+ import Cache from '../utils/cache'
3
4
 
4
5
  const useStreamline = (stream, ...initialArgs) => {
5
6
  let formData = {}
@@ -11,8 +12,8 @@ const useStreamline = (stream, ...initialArgs) => {
11
12
  const cacheKey = `streamline_${stream}_${initialArgs.join('_')}`
12
13
 
13
14
  // Inject headers and API endpoint
14
- const streamlineUrl = inject('streamlineUrl',window.streamlineUrl)
15
- const enableCache = inject('enableCache',window.enableCache)
15
+ const streamlineUrl = inject('streamlineUrl', window.streamlineUrl)
16
+ const enableCache = inject('enableCache', window.enableCache ?? true)
16
17
 
17
18
  const originalProps = reactive({})
18
19
 
@@ -30,7 +31,7 @@ const useStreamline = (stream, ...initialArgs) => {
30
31
  })
31
32
 
32
33
  const fetchServiceProperties = async (force) => {
33
- if(!force){
34
+ if (!force) {
34
35
  if (loading.value || propertiesFetched.value) return
35
36
  }
36
37
 
@@ -42,7 +43,9 @@ const useStreamline = (stream, ...initialArgs) => {
42
43
  params: initialArgs
43
44
  })
44
45
  assignProperties(response.data)
45
- enableCache && localStorage.setItem(cacheKey, JSON.stringify(response.data))
46
+ if (enableCache) {
47
+ await Cache.set(cacheKey, response.data)
48
+ }
46
49
  } catch (error) {
47
50
  console.error(`Error fetching properties for stream ${stream}`, error)
48
51
  throw error
@@ -64,10 +67,10 @@ const useStreamline = (stream, ...initialArgs) => {
64
67
  }
65
68
 
66
69
  return (...args) => {
67
- if(prop === 'refresh' || prop === 'reload'){
70
+ if (prop === 'refresh' || prop === 'reload') {
68
71
  return fetchServiceProperties(true)
69
72
  }
70
- if(prop === 'confirm'){
73
+ if (prop === 'confirm') {
71
74
  return confirmAction(args[0] ?? 'Are you sure?')
72
75
  }
73
76
  let repo = null
@@ -75,10 +78,16 @@ const useStreamline = (stream, ...initialArgs) => {
75
78
  action: prop,
76
79
  stream,
77
80
  ...formData,
78
- params: args
81
+ params: args,
82
+ initialParams: initialArgs
79
83
  }
84
+ args.forEach(arg => {
85
+ if (arg && typeof arg === 'object' && !Array.isArray(arg)) {
86
+ Object.assign(data, arg)
87
+ }
88
+ })
80
89
  if (confirmationMessage.value) {
81
- repo = shRepo.runPlainRequest(streamlineUrl,null, confirmationMessage.value,data)
90
+ repo = shRepo.runPlainRequest(streamlineUrl, null, confirmationMessage.value, data)
82
91
  } else {
83
92
  repo = shApis
84
93
  .doPost(streamlineUrl, data);
@@ -87,13 +96,13 @@ const useStreamline = (stream, ...initialArgs) => {
87
96
  loading.value = true
88
97
  return repo.then((response) => {
89
98
  loading.value = false
90
- if(confirmationMessage.value){
99
+ if (confirmationMessage.value) {
91
100
  confirmationMessage.value = null
92
101
 
93
- if(!response.isConfirmed){
102
+ if (!response.isConfirmed) {
94
103
  return
95
104
  }
96
- if(response.value?.success){
105
+ if (response.value?.success) {
97
106
  return response.value.response
98
107
  } else {
99
108
  // throw error
@@ -102,8 +111,8 @@ const useStreamline = (stream, ...initialArgs) => {
102
111
 
103
112
  }
104
113
 
105
- return response.data
106
- })
114
+ return response.data
115
+ })
107
116
  .catch((error) => {
108
117
  loading.value = false
109
118
  console.error(`Error calling ${prop} on stream ${stream}`, error)
@@ -114,28 +123,15 @@ const useStreamline = (stream, ...initialArgs) => {
114
123
  }
115
124
  const getActionUrl = (action, ...args) => {
116
125
  let newStream = stream
117
- if(action.includes(':')){
126
+ if (action.includes(':')) {
118
127
  [newStream, action] = action.split(':')
119
128
  }
120
129
  const post = {
121
130
  action,
122
- stream:newStream,
131
+ stream: newStream,
123
132
  params: args
124
133
  }
125
134
  return `${streamlineUrl}?${new URLSearchParams(post).toString()}`
126
-
127
- // // Add each arg as a separate param
128
- // args.forEach((arg, index) => {
129
- // let value = arg;
130
- // if (typeof arg === 'object' && !(arg instanceof Date)) {
131
- // value = JSON.stringify(arg);
132
- // } else if (arg instanceof Date) {
133
- // value = arg.toISOString();
134
- // }
135
- // params.append(`params[${index}]`, value);
136
- // })
137
-
138
- // return `${streamlineUrl}?${params.toString()}`
139
135
  }
140
136
 
141
137
  const service = reactive({})
@@ -146,15 +142,16 @@ const useStreamline = (stream, ...initialArgs) => {
146
142
  return new Proxy(service, handler)
147
143
  }
148
144
 
149
- onMounted(() => {
145
+ onMounted(async () => {
146
+ if (enableCache) {
147
+ const cachedData = await Cache.get(cacheKey)
148
+ if (cachedData) {
149
+ assignProperties(cachedData)
150
+ }
151
+ }
150
152
  if (initialArgs.length > 0) {
151
153
  fetchServiceProperties()
152
154
  }
153
- if (!enableCache) return
154
- const cachedData = localStorage.getItem(cacheKey)
155
- if (cachedData) {
156
- assignProperties(JSON.parse(cachedData))
157
- }
158
155
  })
159
156
 
160
157
  return {
package/src/main.js CHANGED
@@ -2,16 +2,20 @@ import { createApp } from 'vue'
2
2
  import './style.css'
3
3
  import App from './App.vue'
4
4
  import streamline from './plugins/streamline.js'
5
+ import { ShFrontend } from '@iankibetsh/shframework'
5
6
 
6
7
  const streamlineHeaders = {
7
8
  Authorization: 'Bearer ' + localStorage.getItem('access_token')
8
9
  }
9
10
  const streamlineUrl = 'http://2022-sharasms.test/api/streamline'
10
11
  const app = createApp(App)
12
+ app.use(ShFrontend, {
13
+ baseApiUrl: 'http://2022-sharasms.test/api'
14
+ })
11
15
  app.use(streamline,{
12
16
  streamlineHeaders,
13
17
  streamlineUrl,
14
- enableCache: false
18
+ enableCache: true
15
19
  })
16
20
 
17
21
  app.mount('#app')
@@ -2,11 +2,11 @@ const streamline = {
2
2
  install(app, options) {
3
3
  app.provide('streamlineUrl', options.streamlineUrl ?? '/api/streamline')
4
4
  // app.provide('streamlineHeaders', options.streamlineHeaders)
5
- app.provide('enableCache', options.enableCache)
5
+ app.provide('enableCache', options.enableCache ?? true)
6
6
 
7
7
  // put them on window
8
8
  window.streamlineUrl = options.streamlineUrl ?? '/api/streamline'
9
- window.enableCache = options.enableCache ?? false
9
+ window.enableCache = options.enableCache ?? true
10
10
  }
11
11
  }
12
12
 
@@ -0,0 +1,113 @@
1
+ const DB_NAME = 'streamline_cache'
2
+ const STORE_NAME = 'properties'
3
+ const DB_VERSION = 1
4
+
5
+ const openDB = () => {
6
+ return new Promise((resolve, reject) => {
7
+ const request = indexedDB.open(DB_NAME, DB_VERSION)
8
+
9
+ request.onupgradeneeded = (event) => {
10
+ const db = event.target.result
11
+ if (!db.objectStoreNames.contains(STORE_NAME)) {
12
+ db.createObjectStore(STORE_NAME)
13
+ }
14
+ }
15
+
16
+ request.onsuccess = (event) => {
17
+ resolve(event.target.result)
18
+ }
19
+
20
+ request.onerror = (event) => {
21
+ reject(event.target.error)
22
+ }
23
+ })
24
+ }
25
+
26
+ const Cache = {
27
+ async get(key) {
28
+ try {
29
+ const db = await openDB()
30
+ return new Promise((resolve, reject) => {
31
+ const transaction = db.transaction(STORE_NAME, 'readonly')
32
+ const store = transaction.objectStore(STORE_NAME)
33
+ const request = store.get(key)
34
+
35
+ request.onsuccess = () => {
36
+ resolve(request.result)
37
+ }
38
+
39
+ request.onerror = () => {
40
+ reject(request.error)
41
+ }
42
+ })
43
+ } catch (error) {
44
+ console.error('Error getting from IndexedDB cache', error)
45
+ return null
46
+ }
47
+ },
48
+
49
+ async set(key, value) {
50
+ try {
51
+ const db = await openDB()
52
+ return new Promise((resolve, reject) => {
53
+ const transaction = db.transaction(STORE_NAME, 'readwrite')
54
+ const store = transaction.objectStore(STORE_NAME)
55
+ const request = store.put(value, key)
56
+
57
+ request.onsuccess = () => {
58
+ resolve()
59
+ }
60
+
61
+ request.onerror = () => {
62
+ reject(request.error)
63
+ }
64
+ })
65
+ } catch (error) {
66
+ console.error('Error setting in IndexedDB cache', error)
67
+ }
68
+ },
69
+
70
+ async delete(key) {
71
+ try {
72
+ const db = await openDB()
73
+ return new Promise((resolve, reject) => {
74
+ const transaction = db.transaction(STORE_NAME, 'readwrite')
75
+ const store = transaction.objectStore(STORE_NAME)
76
+ const request = store.delete(key)
77
+
78
+ request.onsuccess = () => {
79
+ resolve()
80
+ }
81
+
82
+ request.onerror = () => {
83
+ reject(request.error)
84
+ }
85
+ })
86
+ } catch (error) {
87
+ console.error('Error deleting from IndexedDB cache', error)
88
+ }
89
+ },
90
+
91
+ async clear() {
92
+ try {
93
+ const db = await openDB()
94
+ return new Promise((resolve, reject) => {
95
+ const transaction = db.transaction(STORE_NAME, 'readwrite')
96
+ const store = transaction.objectStore(STORE_NAME)
97
+ const request = store.clear()
98
+
99
+ request.onsuccess = () => {
100
+ resolve()
101
+ }
102
+
103
+ request.onerror = () => {
104
+ reject(request.error)
105
+ }
106
+ })
107
+ } catch (error) {
108
+ console.error('Error clearing IndexedDB cache', error)
109
+ }
110
+ }
111
+ }
112
+
113
+ export default Cache