@hkdigital/lib-core 0.4.24 → 0.4.25
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/dist/logging/internal/adapters/pino.d.ts +7 -3
- package/dist/logging/internal/adapters/pino.js +200 -67
- package/dist/logging/internal/transports/pretty-transport.d.ts +17 -0
- package/dist/logging/internal/transports/pretty-transport.js +104 -0
- package/dist/logging/internal/transports/test-transport.d.ts +19 -0
- package/dist/logging/internal/transports/test-transport.js +79 -0
- package/dist/network/states/NetworkLoader.svelte.d.ts +6 -0
- package/dist/network/states/NetworkLoader.svelte.js +15 -2
- package/dist/services/service-base/ServiceBase.d.ts +12 -8
- package/dist/services/service-base/ServiceBase.js +8 -6
- package/dist/state/machines/finite-state-machine/FiniteStateMachine.svelte.d.ts +3 -7
- package/dist/state/machines/finite-state-machine/FiniteStateMachine.svelte.js +57 -27
- package/dist/state/machines/finite-state-machine/README.md +17 -15
- package/dist/state/machines/finite-state-machine/constants.d.ts +13 -0
- package/dist/state/machines/finite-state-machine/constants.js +15 -0
- package/dist/state/machines/finite-state-machine/index.d.ts +1 -0
- package/dist/state/machines/finite-state-machine/index.js +1 -0
- package/dist/state/machines/finite-state-machine/typedef.js +5 -5
- package/dist/state/machines/loading-state-machine/LoadingStateMachine.svelte.d.ts +12 -0
- package/dist/state/machines/loading-state-machine/LoadingStateMachine.svelte.js +27 -2
- package/dist/state/machines/loading-state-machine/README.md +66 -18
- package/dist/state/machines/loading-state-machine/constants.d.ts +2 -0
- package/dist/state/machines/loading-state-machine/constants.js +2 -0
- package/package.json +1 -1
- package/dist/logging/internal/adapters/pino.js__ +0 -260
|
@@ -8,7 +8,7 @@ A specialized finite state machine designed for managing loading operations with
|
|
|
8
8
|
|
|
9
9
|
## States
|
|
10
10
|
|
|
11
|
-
The machine defines
|
|
11
|
+
The machine defines seven primary states:
|
|
12
12
|
|
|
13
13
|
- **`INITIAL`** - Starting state, ready to begin loading
|
|
14
14
|
- **`LOADING`** - Currently loading data or resources
|
|
@@ -16,6 +16,7 @@ The machine defines six primary states:
|
|
|
16
16
|
- **`UNLOADING`** - Cleaning up or releasing resources
|
|
17
17
|
- **`CANCELLED`** - Loading operation was cancelled
|
|
18
18
|
- **`ERROR`** - An error occurred during loading
|
|
19
|
+
- **`TIMEOUT`** - Loading operation exceeded time limit
|
|
19
20
|
|
|
20
21
|
## Events
|
|
21
22
|
|
|
@@ -26,6 +27,7 @@ Available events to trigger state transitions:
|
|
|
26
27
|
- **`UNLOAD`** - Begin cleanup/unloading
|
|
27
28
|
- **`CANCEL`** - Cancel the loading operation
|
|
28
29
|
- **`ERROR`** - Signal an error occurred
|
|
30
|
+
- **`TIMEOUT`** - Signal operation has timed out
|
|
29
31
|
- **`INITIAL`** - Return to initial state
|
|
30
32
|
|
|
31
33
|
## Basic Usage
|
|
@@ -65,35 +67,36 @@ console.log(loader.current); // STATE_LOADED
|
|
|
65
67
|
|
|
66
68
|
```
|
|
67
69
|
INITIAL → LOADING
|
|
68
|
-
LOADING → LOADED | CANCELLED | ERROR
|
|
70
|
+
LOADING → LOADED | CANCELLED | ERROR | TIMEOUT
|
|
69
71
|
LOADED → LOADING | UNLOADING
|
|
70
72
|
UNLOADING → INITIAL | ERROR
|
|
71
73
|
CANCELLED → LOADING | UNLOADING
|
|
72
74
|
ERROR → LOADING | UNLOADING
|
|
75
|
+
TIMEOUT → LOADING | UNLOADING
|
|
73
76
|
```
|
|
74
77
|
|
|
75
78
|
### Transition Diagram
|
|
76
79
|
|
|
77
80
|
```
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
81
|
+
┌─────────┐
|
|
82
|
+
│ INITIAL │
|
|
83
|
+
└────┬────┘
|
|
84
|
+
│ LOAD
|
|
85
|
+
▼
|
|
83
86
|
┌─────────┐ CANCEL ┌───────────┐
|
|
84
87
|
│ LOADING │──────────────→│ CANCELLED │
|
|
85
88
|
└────┬────┘ └─────┬─────┘
|
|
86
89
|
│ │
|
|
87
|
-
LOADED│ ERROR
|
|
88
|
-
│ │
|
|
89
|
-
▼ ▼
|
|
90
|
-
┌────────┐ ┌───────┐
|
|
91
|
-
│ LOADED │ │ ERROR │
|
|
92
|
-
└────┬───┘ └───┬───┘
|
|
93
|
-
│ │
|
|
94
|
-
UNLOAD│ LOAD │ UNLOAD
|
|
95
|
-
▼ ▼
|
|
96
|
-
|
|
90
|
+
LOADED│ ERROR TIMEOUT LOAD │ UNLOAD
|
|
91
|
+
│ │ │ │ │
|
|
92
|
+
▼ ▼ ▼ ▼ ▼
|
|
93
|
+
┌────────┐ ┌───────┐ ┌─────────┐ ┌──────────┐
|
|
94
|
+
│ LOADED │ │ ERROR │ │ TIMEOUT │ │UNLOADING │
|
|
95
|
+
└────┬───┘ └───┬───┘ └────┬────┘ └────┬─────┘
|
|
96
|
+
│ │ │ │
|
|
97
|
+
UNLOAD│ LOAD │ UNLOAD LOAD│ UNLOAD INITIAL│ ERROR
|
|
98
|
+
▼ ▼ ▼ ▼
|
|
99
|
+
┌──────────┐◄──────────────────────────┘
|
|
97
100
|
│UNLOADING │
|
|
98
101
|
└──────────┘
|
|
99
102
|
```
|
|
@@ -119,6 +122,10 @@ loader.onenter = (state) => {
|
|
|
119
122
|
console.log('Loading failed');
|
|
120
123
|
showError();
|
|
121
124
|
break;
|
|
125
|
+
case STATE_TIMEOUT:
|
|
126
|
+
console.log('Loading timed out');
|
|
127
|
+
showRetryOption();
|
|
128
|
+
break;
|
|
122
129
|
}
|
|
123
130
|
};
|
|
124
131
|
```
|
|
@@ -167,6 +174,42 @@ if (loader.current === 'error') {
|
|
|
167
174
|
}
|
|
168
175
|
```
|
|
169
176
|
|
|
177
|
+
## Timeout Handling
|
|
178
|
+
|
|
179
|
+
The machine supports timeout functionality for managing loading operations that exceed expected duration:
|
|
180
|
+
|
|
181
|
+
```javascript
|
|
182
|
+
const loader = new LoadingStateMachine();
|
|
183
|
+
|
|
184
|
+
// External timeout management
|
|
185
|
+
const timeoutId = setTimeout(() => {
|
|
186
|
+
if (loader.current === STATE_LOADING) {
|
|
187
|
+
loader.doTimeout(); // Transitions to STATE_TIMEOUT
|
|
188
|
+
}
|
|
189
|
+
}, 10000); // 10 second timeout
|
|
190
|
+
|
|
191
|
+
loader.onenter = (state) => {
|
|
192
|
+
switch (state) {
|
|
193
|
+
case STATE_TIMEOUT:
|
|
194
|
+
console.log('Loading timed out');
|
|
195
|
+
showRetryButton();
|
|
196
|
+
break;
|
|
197
|
+
case STATE_LOADED:
|
|
198
|
+
clearTimeout(timeoutId); // Cancel timeout on success
|
|
199
|
+
break;
|
|
200
|
+
}
|
|
201
|
+
};
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
### doTimeout Method
|
|
205
|
+
|
|
206
|
+
Use `doTimeout()` to manually trigger a timeout transition:
|
|
207
|
+
|
|
208
|
+
```javascript
|
|
209
|
+
// Only works when in STATE_LOADING
|
|
210
|
+
loader.doTimeout(); // Sends TIMEOUT signal
|
|
211
|
+
```
|
|
212
|
+
|
|
170
213
|
## Integration with Svelte Reactivity
|
|
171
214
|
|
|
172
215
|
LoadingStateMachine inherits the `onenter` callback and Svelte reactivity integration patterns from [FiniteStateMachine](../finite-state-machine/README.md#integration-with-svelte-reactivity).
|
|
@@ -235,6 +278,9 @@ export default class MultiSourceLoader {
|
|
|
235
278
|
case STATE_ERROR:
|
|
236
279
|
this.#handleLoadError();
|
|
237
280
|
break;
|
|
281
|
+
case STATE_TIMEOUT:
|
|
282
|
+
this.#handleTimeout();
|
|
283
|
+
break;
|
|
238
284
|
}
|
|
239
285
|
this.state = state;
|
|
240
286
|
};
|
|
@@ -532,6 +578,7 @@ import {
|
|
|
532
578
|
STATE_UNLOADING,
|
|
533
579
|
STATE_CANCELLED,
|
|
534
580
|
STATE_ERROR,
|
|
581
|
+
STATE_TIMEOUT,
|
|
535
582
|
|
|
536
583
|
// Events
|
|
537
584
|
LOAD,
|
|
@@ -539,6 +586,7 @@ import {
|
|
|
539
586
|
UNLOAD,
|
|
540
587
|
CANCEL,
|
|
541
588
|
ERROR,
|
|
542
|
-
INITIAL
|
|
589
|
+
INITIAL,
|
|
590
|
+
TIMEOUT
|
|
543
591
|
} from './constants.js';
|
|
544
592
|
```
|
|
@@ -4,9 +4,11 @@ export const STATE_UNLOADING: "unloading";
|
|
|
4
4
|
export const STATE_LOADED: "loaded";
|
|
5
5
|
export const STATE_CANCELLED: "cancelled";
|
|
6
6
|
export const STATE_ERROR: "error";
|
|
7
|
+
export const STATE_TIMEOUT: "timeout";
|
|
7
8
|
export const INITIAL: "initial";
|
|
8
9
|
export const LOAD: "load";
|
|
9
10
|
export const CANCEL: "cancel";
|
|
10
11
|
export const ERROR: "error";
|
|
11
12
|
export const LOADED: "loaded";
|
|
12
13
|
export const UNLOAD: "unload";
|
|
14
|
+
export const TIMEOUT: "timeout";
|
|
@@ -5,6 +5,7 @@ export const STATE_LOADED = 'loaded';
|
|
|
5
5
|
|
|
6
6
|
export const STATE_CANCELLED = 'cancelled';
|
|
7
7
|
export const STATE_ERROR = 'error';
|
|
8
|
+
export const STATE_TIMEOUT = 'timeout';
|
|
8
9
|
|
|
9
10
|
// > Signals
|
|
10
11
|
|
|
@@ -14,3 +15,4 @@ export const CANCEL = 'cancel';
|
|
|
14
15
|
export const ERROR = 'error';
|
|
15
16
|
export const LOADED = 'loaded';
|
|
16
17
|
export const UNLOAD = 'unload';
|
|
18
|
+
export const TIMEOUT = 'timeout';
|
package/package.json
CHANGED
|
@@ -1,260 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Pino adapter for server-side logging
|
|
3
|
-
*/
|
|
4
|
-
import pino from 'pino';
|
|
5
|
-
import { dev } from '$app/environment';
|
|
6
|
-
|
|
7
|
-
import {
|
|
8
|
-
detectErrorMeta,
|
|
9
|
-
findRelevantFrameIndex,
|
|
10
|
-
formatErrorDisplay
|
|
11
|
-
} from './formatting.js';
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Pino adapter that bridges Logger events to pino
|
|
15
|
-
*/
|
|
16
|
-
export class PinoAdapter {
|
|
17
|
-
#projectRoot = null;
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Create a new PinoAdapter
|
|
21
|
-
*
|
|
22
|
-
* @param {Object} [options] - Pino configuration options
|
|
23
|
-
*/
|
|
24
|
-
constructor(options = {}) {
|
|
25
|
-
// Determine project root once for stack trace cleaning
|
|
26
|
-
this.#projectRoot = import.meta.env.VITE_PROJECT_ROOT || process.cwd();
|
|
27
|
-
const baseOptions = {
|
|
28
|
-
serializers: {
|
|
29
|
-
errors: (err) => {
|
|
30
|
-
|
|
31
|
-
/** @type {import('./typedef').ErrorSummary[]} */
|
|
32
|
-
const chain = [];
|
|
33
|
-
let loggedAt = null;
|
|
34
|
-
|
|
35
|
-
let current = err;
|
|
36
|
-
let isFirst = true;
|
|
37
|
-
|
|
38
|
-
while (current && current instanceof Error) {
|
|
39
|
-
// Check if this is the first error and it's a LoggerError - extract logging context
|
|
40
|
-
if (isFirst && current.name === 'LoggerError') {
|
|
41
|
-
if (current.stack) {
|
|
42
|
-
const cleanedStackString = this.#cleanStackTrace(current.stack);
|
|
43
|
-
const cleanedStackArray = cleanedStackString
|
|
44
|
-
.split('\n')
|
|
45
|
-
.map((line) => line.trim())
|
|
46
|
-
.filter(
|
|
47
|
-
(line) =>
|
|
48
|
-
line && line !== current.name + ': ' + current.message
|
|
49
|
-
);
|
|
50
|
-
|
|
51
|
-
// For LoggerError, we know it's a logger.error call, so find the relevant frame
|
|
52
|
-
const loggerErrorIndex = cleanedStackArray.findIndex(frame =>
|
|
53
|
-
(frame.includes('Logger.error') && frame.includes('logger/Logger.js')) ||
|
|
54
|
-
(frame.includes('error@') && frame.includes('logger/Logger.js'))
|
|
55
|
-
);
|
|
56
|
-
|
|
57
|
-
if (loggerErrorIndex >= 0 && loggerErrorIndex + 1 < cleanedStackArray.length) {
|
|
58
|
-
const relevantFrame = cleanedStackArray[loggerErrorIndex + 1];
|
|
59
|
-
|
|
60
|
-
// Extract function name from the relevant frame
|
|
61
|
-
// const functionName = parseFunctionName(relevantFrame);
|
|
62
|
-
|
|
63
|
-
// const errorType = functionName ? `logger.error in ${functionName}` : 'logger.error';
|
|
64
|
-
loggedAt = relevantFrame.slice(3); // remove "at "
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
// Skip the LoggerError and move to the actual error
|
|
69
|
-
current = current.cause;
|
|
70
|
-
isFirst = false;
|
|
71
|
-
continue;
|
|
72
|
-
}
|
|
73
|
-
/** @type {import('./typedef').ErrorSummary} */
|
|
74
|
-
const serialized = {
|
|
75
|
-
name: current.name,
|
|
76
|
-
message: current.message
|
|
77
|
-
};
|
|
78
|
-
|
|
79
|
-
// Add error metadata for structured logging and terminal display
|
|
80
|
-
if (current.stack) {
|
|
81
|
-
// Convert cleaned stack string to array format expected by formatting functions
|
|
82
|
-
const cleanedStackString = this.#cleanStackTrace(current.stack);
|
|
83
|
-
const cleanedStackArray = cleanedStackString
|
|
84
|
-
.split('\n')
|
|
85
|
-
.map((line) => line.trim())
|
|
86
|
-
.filter(
|
|
87
|
-
(line) =>
|
|
88
|
-
line && line !== current.name + ': ' + current.message
|
|
89
|
-
);
|
|
90
|
-
|
|
91
|
-
const errorMeta = detectErrorMeta(current, cleanedStackArray);
|
|
92
|
-
const relevantFrameIndex = findRelevantFrameIndex(
|
|
93
|
-
current,
|
|
94
|
-
cleanedStackArray
|
|
95
|
-
);
|
|
96
|
-
|
|
97
|
-
serialized.meta = errorMeta;
|
|
98
|
-
serialized.errorType = formatErrorDisplay(errorMeta);
|
|
99
|
-
|
|
100
|
-
// Include stack frames for terminal display
|
|
101
|
-
serialized.stackFrames = cleanedStackArray
|
|
102
|
-
.slice(0, 9)
|
|
103
|
-
.map((frame, index) => {
|
|
104
|
-
const marker = index === relevantFrameIndex ? '→' : ' ';
|
|
105
|
-
|
|
106
|
-
return `${marker} ${frame}`;
|
|
107
|
-
});
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
// Include HttpError-specific properties
|
|
111
|
-
const httpError = /** @type {import('$lib/network/errors.js').HttpError} */ (current);
|
|
112
|
-
if (httpError.status !== undefined) {
|
|
113
|
-
serialized.status = httpError.status;
|
|
114
|
-
}
|
|
115
|
-
if (httpError.details !== undefined) {
|
|
116
|
-
serialized.details = httpError.details;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
chain.push(serialized);
|
|
120
|
-
current = current.cause;
|
|
121
|
-
isFirst = false;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
return loggedAt ? { chain, loggedAt } : chain;
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
};
|
|
128
|
-
|
|
129
|
-
// Add error handling for missing pino-pretty in dev
|
|
130
|
-
if ( dev) {
|
|
131
|
-
const devOptions = {
|
|
132
|
-
level: 'debug',
|
|
133
|
-
transport: {
|
|
134
|
-
target: 'pino-pretty',
|
|
135
|
-
options: {
|
|
136
|
-
colorize: true,
|
|
137
|
-
ignore: 'hostname,pid'
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
};
|
|
141
|
-
|
|
142
|
-
try {
|
|
143
|
-
this.pino = pino({ ...baseOptions, ...devOptions, ...options });
|
|
144
|
-
} catch (error) {
|
|
145
|
-
if (
|
|
146
|
-
error.message.includes('Cannot find module') &&
|
|
147
|
-
error.message.includes('pino-pretty')
|
|
148
|
-
) {
|
|
149
|
-
const errorMessage = `
|
|
150
|
-
╭─────────────────────────────────────────────────────────────╮
|
|
151
|
-
│ Missing Dependency │
|
|
152
|
-
├─────────────────────────────────────────────────────────────┤
|
|
153
|
-
│ 'pino-pretty' is required for development logging │
|
|
154
|
-
│ Install it with: pnpm add -D pino-pretty │
|
|
155
|
-
╰─────────────────────────────────────────────────────────────╯`;
|
|
156
|
-
console.error(errorMessage);
|
|
157
|
-
throw new Error('pino-pretty is required for development mode');
|
|
158
|
-
}
|
|
159
|
-
throw error;
|
|
160
|
-
}
|
|
161
|
-
} else {
|
|
162
|
-
this.pino = pino({ ...baseOptions, ...options });
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
/**
|
|
167
|
-
* Clean stack trace by removing project root path and simplifying node_modules
|
|
168
|
-
*
|
|
169
|
-
* @param {string} stack - Original stack trace
|
|
170
|
-
* @returns {string} Cleaned stack trace
|
|
171
|
-
*/
|
|
172
|
-
#cleanStackTrace(stack) {
|
|
173
|
-
if (!stack || !this.#projectRoot) {
|
|
174
|
-
return stack;
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
let cleaned = stack;
|
|
178
|
-
|
|
179
|
-
// Escape special regex characters in the project root path
|
|
180
|
-
const escapedRoot = this.#projectRoot.replace(
|
|
181
|
-
/[.*+?^${}()|[\]\\]/g,
|
|
182
|
-
'\\$&'
|
|
183
|
-
);
|
|
184
|
-
|
|
185
|
-
// Replace project root path with relative path, handling file:// protocol
|
|
186
|
-
// Match both regular paths and file:// URLs
|
|
187
|
-
const rootRegex = new RegExp(
|
|
188
|
-
`(\\s+at\\s+.*\\()(file://)?${escapedRoot}[\\/\\\\]`,
|
|
189
|
-
'g'
|
|
190
|
-
);
|
|
191
|
-
cleaned = cleaned.replace(rootRegex, '$1');
|
|
192
|
-
|
|
193
|
-
// Simplify pnpm paths: node_modules/.pnpm/package@version_deps/node_modules/package
|
|
194
|
-
// becomes: node_modules/package
|
|
195
|
-
const pnpmRegex =
|
|
196
|
-
/node_modules\/\.pnpm\/([^@\/]+)@[^\/]+\/node_modules\/\1/g;
|
|
197
|
-
cleaned = cleaned.replace(pnpmRegex, 'node_modules/$1');
|
|
198
|
-
|
|
199
|
-
// Also handle cases where the package name might be different in the final path
|
|
200
|
-
const pnpmRegex2 = /node_modules\/\.pnpm\/[^\/]+\/node_modules\/([^\/]+)/g;
|
|
201
|
-
cleaned = cleaned.replace(pnpmRegex2, 'node_modules/$1');
|
|
202
|
-
|
|
203
|
-
return cleaned;
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
/**
|
|
207
|
-
* Handle log events from Logger
|
|
208
|
-
*
|
|
209
|
-
* @param {Object} logEvent - Log event from Logger
|
|
210
|
-
*/
|
|
211
|
-
handleLog(logEvent) {
|
|
212
|
-
const { level, message, details, source, timestamp } = logEvent;
|
|
213
|
-
|
|
214
|
-
const logData = {
|
|
215
|
-
source,
|
|
216
|
-
timestamp
|
|
217
|
-
};
|
|
218
|
-
|
|
219
|
-
// Check if details contains an error and promote it to err property for pino serializer
|
|
220
|
-
if (details) {
|
|
221
|
-
if (details instanceof Error) {
|
|
222
|
-
// details is directly an error
|
|
223
|
-
logData.err = details;
|
|
224
|
-
} else if (details.error instanceof Error) {
|
|
225
|
-
// details has an error property
|
|
226
|
-
logData.err = details.error;
|
|
227
|
-
// Include other details except the error
|
|
228
|
-
// eslint-disable-next-line no-unused-vars
|
|
229
|
-
const { error, ...otherDetails } = details;
|
|
230
|
-
if (Object.keys(otherDetails).length > 0) {
|
|
231
|
-
logData.details = otherDetails;
|
|
232
|
-
}
|
|
233
|
-
} else {
|
|
234
|
-
// No error found in details, include all details
|
|
235
|
-
logData.details = details;
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
// Check if we have loggedAt info from the serializer
|
|
240
|
-
if (logData.err && typeof logData.err === 'object' && logData.err.loggedAt) {
|
|
241
|
-
logData.loggedAt = logData.err.loggedAt;
|
|
242
|
-
logData.err = logData.err.chain;
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
this.pino[level](logData, message);
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
/**
|
|
249
|
-
* Create a child logger with additional context
|
|
250
|
-
*
|
|
251
|
-
* @param {Object} context - Additional context data
|
|
252
|
-
* @returns {PinoAdapter} New adapter instance with context
|
|
253
|
-
*/
|
|
254
|
-
child(context) {
|
|
255
|
-
const childPino = this.pino.child(context);
|
|
256
|
-
const adapter = new PinoAdapter();
|
|
257
|
-
adapter.pino = childPino;
|
|
258
|
-
return adapter;
|
|
259
|
-
}
|
|
260
|
-
}
|