@simplysm/core-common 14.0.4 → 14.0.5

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/docs/errors.md CHANGED
@@ -1,14 +1,14 @@
1
1
  # Errors
2
2
 
3
- A hierarchy of error classes built on `SdError`, which supports tree-structured cause chaining via the ES2024 `cause` property.
3
+ All error classes extend `SdError`, which extends the native `Error`.
4
4
 
5
5
  ## `SdError`
6
6
 
7
- Base error class with cause-chain support. Messages are joined in reverse order with ` => `.
7
+ Tree-structured error class. Supports cause chaining via the ES2024 `cause` property. Messages are joined in reverse order with ` => ` separator.
8
8
 
9
9
  ```typescript
10
10
  class SdError extends Error {
11
- cause?: Error;
11
+ override cause?: Error;
12
12
 
13
13
  constructor(cause: Error, ...messages: string[]);
14
14
  constructor(...messages: string[]);
@@ -16,27 +16,15 @@ class SdError extends Error {
16
16
  ```
17
17
 
18
18
  | Constructor Overload | Description |
19
- |---------------------|-------------|
20
- | `new SdError(cause, ...messages)` | Wraps an existing error. Message becomes `"messages[n] => ... => messages[0] => cause.message"` |
21
- | `new SdError(...messages)` | Creates without cause. Message becomes `"messages[n] => ... => messages[0]"` |
19
+ |----------------------|-------------|
20
+ | `new SdError(cause, ...messages)` | Wrap a cause error. Final message: `"outermost => ... => cause.message"` |
21
+ | `new SdError(...messages)` | Messages only. Final message: `"outermost => ... => innermost"` |
22
22
 
23
- When a cause error is provided, its stack trace is appended under a `---- cause stack ----` separator.
24
-
25
- ```typescript
26
- try {
27
- await fetch(url);
28
- } catch (err) {
29
- throw new SdError(err, "API call failed", "User load failed");
30
- // message: "User load failed => API call failed => <original message>"
31
- }
32
-
33
- throw new SdError("Invalid state", "Cannot process");
34
- // message: "Cannot process => Invalid state"
35
- ```
23
+ The cause chain stack trace is appended to the current stack under a `---- cause stack ----` separator.
36
24
 
37
25
  ## `ArgumentError`
38
26
 
39
- Error for invalid arguments. Extends `SdError`. Formats the argument object as YAML in the message for easy debugging.
27
+ Invalid argument error. Extends `SdError`. Formats the argument object as YAML in the error message for debugging.
40
28
 
41
29
  ```typescript
42
30
  class ArgumentError extends SdError {
@@ -46,21 +34,13 @@ class ArgumentError extends SdError {
46
34
  ```
47
35
 
48
36
  | Constructor Overload | Description |
49
- |---------------------|-------------|
50
- | `new ArgumentError(argObj)` | Default message + YAML-formatted args |
51
- | `new ArgumentError(message, argObj)` | Custom message + YAML-formatted args |
52
-
53
- ```typescript
54
- throw new ArgumentError({ userId: 123, name: null });
55
- // message: "잘못된 인자입니다.\n\nuserId: 123\nname: null"
56
-
57
- throw new ArgumentError("Invalid user", { userId: 123 });
58
- // message: "Invalid user\n\nuserId: 123"
59
- ```
37
+ |----------------------|-------------|
38
+ | `new ArgumentError(argObj)` | Default message + YAML dump of arguments |
39
+ | `new ArgumentError(message, argObj)` | Custom message + YAML dump of arguments |
60
40
 
61
41
  ## `NotImplementedError`
62
42
 
63
- Error for unimplemented features. Extends `SdError`.
43
+ Unimplemented feature error. Extends `SdError`. Use for abstract method stubs or future branches.
64
44
 
65
45
  ```typescript
66
46
  class NotImplementedError extends SdError {
@@ -72,17 +52,9 @@ class NotImplementedError extends SdError {
72
52
  |-----------|------|-------------|
73
53
  | `message` | `string \| undefined` | Optional description of what is not implemented |
74
54
 
75
- ```typescript
76
- throw new NotImplementedError();
77
- // message: "미구현"
78
-
79
- throw new NotImplementedError("Subclass must override");
80
- // message: "미구현: Subclass must override"
81
- ```
82
-
83
55
  ## `TimeoutError`
84
56
 
85
- Error for timeout conditions. Extends `SdError`.
57
+ Timeout exceeded error. Extends `SdError`. Thrown by `wait.until()` when max retry count is exceeded.
86
58
 
87
59
  ```typescript
88
60
  class TimeoutError extends SdError {
@@ -92,13 +64,5 @@ class TimeoutError extends SdError {
92
64
 
93
65
  | Parameter | Type | Description |
94
66
  |-----------|------|-------------|
95
- | `count` | `number \| undefined` | Number of attempts made |
96
- | `message` | `string \| undefined` | Additional description |
97
-
98
- ```typescript
99
- throw new TimeoutError(50);
100
- // message: "대기 시간 초과(50회 시도)"
101
-
102
- throw new TimeoutError(undefined, "API response wait exceeded");
103
- // message: "대기 시간 초과: API response wait exceeded"
104
- ```
67
+ | `count` | `number \| undefined` | Number of attempts made before timeout |
68
+ | `message` | `string \| undefined` | Additional context message |
package/docs/features.md CHANGED
@@ -1,10 +1,10 @@
1
1
  # Features
2
2
 
3
- Async queue and event emitter utilities. All support `using` syntax via `Symbol.dispose`.
4
-
5
3
  ## `DebounceQueue`
6
4
 
7
- Async debounce queue. When called multiple times in rapid succession, only the last enqueued function executes after the delay. Extends `EventEmitter<{ error: SdError }>`.
5
+ Async debounce queue. When multiple calls arrive in quick succession, only the last enqueued function executes after the delay. Extends `EventEmitter<{ error: SdError }>`.
6
+
7
+ If a new request arrives while a previous one is executing, it runs immediately after the current execution completes (no debounce delay).
8
8
 
9
9
  ```typescript
10
10
  class DebounceQueue extends EventEmitter<{ error: SdError }> {
@@ -16,36 +16,27 @@ class DebounceQueue extends EventEmitter<{ error: SdError }> {
16
16
  }
17
17
  ```
18
18
 
19
- ### Constructor
20
-
21
- | Parameter | Type | Default | Description |
22
- |-----------|------|---------|-------------|
23
- | `delay` | `number \| undefined` | `undefined` (next event loop) | Debounce delay in milliseconds |
19
+ | Parameter | Type | Description |
20
+ |-----------|------|-------------|
21
+ | `delay` | `number \| undefined` | Debounce delay in ms. If omitted, executes on next event loop tick. |
24
22
 
25
23
  ### Methods
26
24
 
27
25
  | Method | Description |
28
26
  |--------|-------------|
29
- | `run(fn)` | Enqueue a function. Replaces any previously pending function. After the delay, executes the last enqueued function |
30
- | `dispose()` | Cancels pending timer and clears state |
27
+ | `run(fn)` | Enqueue a function. Replaces any previously pending function. |
28
+ | `dispose()` | Cancel pending work and clean up timers. |
29
+ | `[Symbol.dispose]()` | Supports `using` statement. |
31
30
 
32
- ### Behavior
31
+ ### Error Handling
33
32
 
34
- - If a new `run()` call arrives while a previous function is executing, the new function runs immediately after the current one completes (no debounce delay).
35
- - Errors are emitted as `"error"` events if listeners exist; otherwise logged via consola.
33
+ Errors from the executed function are emitted as `"error"` events. If no listener is registered, errors are logged via `consola`.
36
34
 
37
- ```typescript
38
- const queue = new DebounceQueue(300);
39
- queue.on("error", (err) => console.error(err));
40
-
41
- queue.run(() => console.log("1")); // cancelled
42
- queue.run(() => console.log("2")); // cancelled
43
- queue.run(() => console.log("3")); // executes after 300ms
44
- ```
35
+ ---
45
36
 
46
37
  ## `SerialQueue`
47
38
 
48
- Async serial queue. Functions are executed one at a time in the order they are enqueued. Errors in one function do not prevent subsequent functions from running. Extends `EventEmitter<{ error: SdError }>`.
39
+ Async serial queue. Functions are executed one at a time in FIFO order. Errors in one task do not prevent subsequent tasks from running. Extends `EventEmitter<{ error: SdError }>`.
49
40
 
50
41
  ```typescript
51
42
  class SerialQueue extends EventEmitter<{ error: SdError }> {
@@ -57,72 +48,65 @@ class SerialQueue extends EventEmitter<{ error: SdError }> {
57
48
  }
58
49
  ```
59
50
 
60
- ### Constructor
61
-
62
- | Parameter | Type | Default | Description |
63
- |-----------|------|---------|-------------|
64
- | `gap` | `number` | `0` | Delay between each task execution (ms) |
51
+ | Parameter | Type | Description |
52
+ |-----------|------|-------------|
53
+ | `gap` | `number` | Delay between tasks in ms. Default: `0`. |
65
54
 
66
55
  ### Methods
67
56
 
68
57
  | Method | Description |
69
58
  |--------|-------------|
70
- | `run(fn)` | Add a function to the queue. Starts processing if not already running |
71
- | `dispose()` | Clears the pending queue. The currently running task still completes |
59
+ | `run(fn)` | Add a function to the queue. Starts processing if not already running. |
60
+ | `dispose()` | Clear the pending queue (current task completes). |
61
+ | `[Symbol.dispose]()` | Supports `using` statement. |
72
62
 
73
- ```typescript
74
- const queue = new SerialQueue();
75
- queue.on("error", (err) => console.error(err));
63
+ ### Error Handling
76
64
 
77
- queue.run(async () => { await fetch("/api/1"); }); // runs first
78
- queue.run(async () => { await fetch("/api/2"); }); // runs after 1 completes
79
- queue.run(async () => { await fetch("/api/3"); }); // runs after 2 completes
80
- ```
65
+ Same as `DebounceQueue`: errors are emitted as `"error"` events or logged if no listener.
66
+
67
+ ---
81
68
 
82
69
  ## `EventEmitter<TEvents>`
83
70
 
84
- Type-safe event emitter built on the `EventTarget` API. Works in both browser and Node.js.
71
+ Type-safe event emitter built on the `EventTarget` API. Works in both browser and Node.js. Duplicate listener registration for the same event is silently ignored.
85
72
 
86
73
  ```typescript
87
74
  class EventEmitter<TEvents extends { [K in keyof TEvents]: unknown } = Record<string, unknown>> {
88
- on<K extends keyof TEvents & string>(type: K, listener: (data: TEvents[K]) => void): void;
89
- off<K extends keyof TEvents & string>(type: K, listener: (data: TEvents[K]) => void): void;
90
- emit<K extends keyof TEvents & string>(
91
- type: K,
92
- ...args: TEvents[K] extends void ? [] : [data: TEvents[K]]
75
+ on<TEventName extends keyof TEvents & string>(
76
+ type: TEventName,
77
+ listener: (data: TEvents[TEventName]) => void,
78
+ ): void;
79
+
80
+ off<TEventName extends keyof TEvents & string>(
81
+ type: TEventName,
82
+ listener: (data: TEvents[TEventName]) => void,
83
+ ): void;
84
+
85
+ emit<TEventName extends keyof TEvents & string>(
86
+ type: TEventName,
87
+ ...args: TEvents[TEventName] extends void ? [] : [data: TEvents[TEventName]]
93
88
  ): void;
94
- listenerCount<K extends keyof TEvents & string>(type: K): number;
89
+
90
+ listenerCount<TEventName extends keyof TEvents & string>(type: TEventName): number;
91
+
95
92
  dispose(): void;
96
93
  [Symbol.dispose](): void;
97
94
  }
98
95
  ```
99
96
 
100
- ### Methods
101
-
102
- | Method | Description |
103
- |--------|-------------|
104
- | `on(type, listener)` | Register a listener. Duplicate registration for the same event+listener is ignored |
105
- | `off(type, listener)` | Remove a listener |
106
- | `emit(type, data?)` | Dispatch an event. For `void` event types, data argument is omitted |
107
- | `listenerCount(type)` | Returns the number of registered listeners for an event type |
108
- | `dispose()` | Removes all listeners from all event types |
109
-
110
97
  ### Type Parameter
111
98
 
112
- `TEvents` is a map where keys are event names and values are event data types. Use `void` for events with no data.
99
+ | Parameter | Description |
100
+ |-----------|-------------|
101
+ | `TEvents` | Object type mapping event names to their data types. Use `void` for events with no data. |
113
102
 
114
- ```typescript
115
- interface MyEvents {
116
- data: string;
117
- error: Error;
118
- done: void;
119
- }
120
-
121
- class MyService extends EventEmitter<MyEvents> {}
103
+ ### Methods
122
104
 
123
- const svc = new MyService();
124
- svc.on("data", (data) => {}); // data: string
125
- svc.on("done", () => {}); // no argument
126
- svc.emit("data", "hello");
127
- svc.emit("done"); // no argument needed
128
- ```
105
+ | Method | Description |
106
+ |--------|-------------|
107
+ | `on(type, listener)` | Register a listener. Duplicate registration for same event is ignored. |
108
+ | `off(type, listener)` | Remove a listener. |
109
+ | `emit(type, ...args)` | Dispatch an event. For `void` event types, no data argument is needed. |
110
+ | `listenerCount(type)` | Return the number of listeners for an event type. |
111
+ | `dispose()` | Remove all listeners from all events. |
112
+ | `[Symbol.dispose]()` | Supports `using` statement. |
@@ -0,0 +1,41 @@
1
+ # Map Extensions
2
+
3
+ Prototype extensions added to the global `Map` type. Available after importing `@simplysm/core-common`.
4
+
5
+ ## `Map.prototype.getOrCreate`
6
+
7
+ Get the value for a key. If the key does not exist, create a new value (using the provided value or factory function), store it, and return it.
8
+
9
+ ```typescript
10
+ interface Map<K, V> {
11
+ getOrCreate(key: K, newValue: V): V;
12
+ getOrCreate(key: K, newValueFn: () => V): V;
13
+ }
14
+ ```
15
+
16
+ | Parameter | Type | Description |
17
+ |-----------|------|-------------|
18
+ | `key` | `K` | The key to look up |
19
+ | `newValue` | `V` | Default value to store if key is absent |
20
+ | `newValueFn` | `() => V` | Factory function called only if key is absent |
21
+
22
+ **Returns:** `V` -- the existing or newly created value.
23
+
24
+ **Note:** If `V` is a function type (e.g., `Map<string, () => void>`), passing a function directly as the second argument will be treated as a factory. Wrap it in another function: `map.getOrCreate("key", () => myFn)`.
25
+
26
+ ---
27
+
28
+ ## `Map.prototype.update`
29
+
30
+ Update the value for a key using a transform function. The function receives the current value (or `undefined` if the key does not exist) and its return value is stored.
31
+
32
+ ```typescript
33
+ interface Map<K, V> {
34
+ update(key: K, updateFn: (v: V | undefined) => V): void;
35
+ }
36
+ ```
37
+
38
+ | Parameter | Type | Description |
39
+ |-----------|------|-------------|
40
+ | `key` | `K` | The key to update |
41
+ | `updateFn` | `(v: V \| undefined) => V` | Transform function. Receives current value or `undefined`. |
@@ -0,0 +1,38 @@
1
+ # Set Extensions
2
+
3
+ Prototype extensions added to the global `Set` type. Available after importing `@simplysm/core-common`.
4
+
5
+ ## `Set.prototype.adds`
6
+
7
+ Add multiple values at once.
8
+
9
+ ```typescript
10
+ interface Set<T> {
11
+ adds(...values: T[]): this;
12
+ }
13
+ ```
14
+
15
+ | Parameter | Type | Description |
16
+ |-----------|------|-------------|
17
+ | `values` | `T[]` | Values to add |
18
+
19
+ **Returns:** `this` (for method chaining)
20
+
21
+ ---
22
+
23
+ ## `Set.prototype.toggle`
24
+
25
+ Toggle a value in the set. If the value exists, remove it; if absent, add it. Optionally force add or delete.
26
+
27
+ ```typescript
28
+ interface Set<T> {
29
+ toggle(value: T, addOrDel?: "add" | "del"): this;
30
+ }
31
+ ```
32
+
33
+ | Parameter | Type | Description |
34
+ |-----------|------|-------------|
35
+ | `value` | `T` | The value to toggle |
36
+ | `addOrDel` | `"add" \| "del" \| undefined` | Force `"add"` to always add, `"del"` to always remove. Omit for automatic toggle. |
37
+
38
+ **Returns:** `this` (for method chaining)
@@ -0,0 +1,87 @@
1
+ # Template Strings and Zip
2
+
3
+ ## Template String Tag Functions
4
+
5
+ All template tag functions perform the same operation: combine the template literals and normalize indentation. They exist as separate functions to enable IDE syntax highlighting for different languages.
6
+
7
+ Each function:
8
+ 1. Joins the template strings with interpolated values
9
+ 2. Removes leading and trailing blank lines
10
+ 3. Calculates the minimum indentation across all non-empty lines
11
+ 4. Removes that minimum indentation from every line
12
+
13
+ ```typescript
14
+ function js(strings: TemplateStringsArray, ...values: unknown[]): string;
15
+ function ts(strings: TemplateStringsArray, ...values: unknown[]): string;
16
+ function html(strings: TemplateStringsArray, ...values: unknown[]): string;
17
+ function tsql(strings: TemplateStringsArray, ...values: unknown[]): string;
18
+ function mysql(strings: TemplateStringsArray, ...values: unknown[]): string;
19
+ function pgsql(strings: TemplateStringsArray, ...values: unknown[]): string;
20
+ ```
21
+
22
+ | Function | Purpose |
23
+ |----------|---------|
24
+ | `js` | JavaScript code highlighting |
25
+ | `ts` | TypeScript code highlighting |
26
+ | `html` | HTML markup highlighting |
27
+ | `tsql` | MSSQL T-SQL highlighting |
28
+ | `mysql` | MySQL SQL highlighting |
29
+ | `pgsql` | PostgreSQL SQL highlighting |
30
+
31
+ ---
32
+
33
+ ## `ZipArchiveProgress`
34
+
35
+ Progress callback data for ZIP extraction.
36
+
37
+ ```typescript
38
+ interface ZipArchiveProgress {
39
+ fileName: string;
40
+ totalSize: number;
41
+ extractedSize: number;
42
+ }
43
+ ```
44
+
45
+ | Field | Type | Description |
46
+ |-------|------|-------------|
47
+ | `fileName` | `string` | Name of the file currently being extracted |
48
+ | `totalSize` | `number` | Total uncompressed size of all files in bytes |
49
+ | `extractedSize` | `number` | Cumulative bytes extracted so far |
50
+
51
+ ---
52
+
53
+ ## `ZipArchive`
54
+
55
+ ZIP archive processing class. Supports reading, writing, compressing, and extracting ZIP files. Uses internal caching to avoid duplicate decompression. Built on `@zip.js/zip.js`.
56
+
57
+ ```typescript
58
+ class ZipArchive {
59
+ constructor(data?: Blob | Bytes);
60
+
61
+ get(fileName: string): Promise<Bytes | undefined>;
62
+ exists(fileName: string): Promise<boolean>;
63
+ write(fileName: string, bytes: Bytes): void;
64
+ extractAll(progressCallback?: (progress: ZipArchiveProgress) => void): Promise<Map<string, Bytes | undefined>>;
65
+ compress(): Promise<Bytes>;
66
+ close(): Promise<void>;
67
+ [Symbol.asyncDispose](): Promise<void>;
68
+ }
69
+ ```
70
+
71
+ ### Constructor
72
+
73
+ | Parameter | Type | Description |
74
+ |-----------|------|-------------|
75
+ | `data` | `Blob \| Bytes \| undefined` | Existing ZIP data to read. Omit to create a new empty archive. |
76
+
77
+ ### Methods
78
+
79
+ | Method | Returns | Description |
80
+ |--------|---------|-------------|
81
+ | `get(fileName)` | `Promise<Bytes \| undefined>` | Extract a single file by name. Returns `undefined` if not found. Caches the result. |
82
+ | `exists(fileName)` | `Promise<boolean>` | Check if a file exists in the archive. |
83
+ | `write(fileName, bytes)` | `void` | Write a file to the cache (for later compression). |
84
+ | `extractAll(progressCallback?)` | `Promise<Map<string, Bytes \| undefined>>` | Extract all files. Returns a Map of filename to bytes. Optionally reports progress. |
85
+ | `compress()` | `Promise<Bytes>` | Compress all cached files into a new ZIP. Calls `extractAll()` internally first. |
86
+ | `close()` | `Promise<void>` | Close the reader and clear the cache. |
87
+ | `[Symbol.asyncDispose]()` | `Promise<void>` | Supports `await using` statement. Calls `close()`. |
@@ -1,18 +1,20 @@
1
1
  # Type Utilities
2
2
 
3
- TypeScript type aliases and interfaces exported from `common.types.ts`.
3
+ Exported from `common.types.ts`.
4
4
 
5
5
  ## `Bytes`
6
6
 
7
- Type alias for `Uint8Array`. Used throughout the framework instead of `Buffer`.
7
+ Alias for `Uint8Array`. Used in place of Node.js `Buffer` throughout the framework.
8
8
 
9
9
  ```typescript
10
10
  type Bytes = Uint8Array;
11
11
  ```
12
12
 
13
+ ---
14
+
13
15
  ## `PrimitiveTypeMap`
14
16
 
15
- Maps primitive type string keys to their corresponding TypeScript types. Shared with `orm-common`.
17
+ Maps primitive type string keys to their TypeScript types. Shared with `orm-common`.
16
18
 
17
19
  ```typescript
18
20
  type PrimitiveTypeMap = {
@@ -27,65 +29,47 @@ type PrimitiveTypeMap = {
27
29
  };
28
30
  ```
29
31
 
32
+ ---
33
+
30
34
  ## `PrimitiveTypeStr`
31
35
 
32
- Union of all keys in `PrimitiveTypeMap`.
36
+ Union of all primitive type name strings.
33
37
 
34
38
  ```typescript
35
39
  type PrimitiveTypeStr = keyof PrimitiveTypeMap;
36
40
  // "string" | "number" | "boolean" | "DateTime" | "DateOnly" | "Time" | "Uuid" | "Bytes"
37
41
  ```
38
42
 
43
+ ---
44
+
39
45
  ## `PrimitiveType`
40
46
 
41
- Union of all primitive type values (including `undefined`).
47
+ Union of all primitive type values, plus `undefined`.
42
48
 
43
49
  ```typescript
44
50
  type PrimitiveType = PrimitiveTypeMap[PrimitiveTypeStr] | undefined;
45
- // string | number | boolean | DateTime | DateOnly | Time | Uuid | Uint8Array | undefined
46
51
  ```
47
52
 
48
- ## `DeepPartial<TObject>`
53
+ ---
54
+
55
+ ## `DeepPartial<T>`
49
56
 
50
- Recursively makes all properties optional. Primitive types (`PrimitiveType`) are preserved as-is; only object/array types are recursively made partial.
57
+ Recursively make all properties optional. Primitive types (string, number, boolean, DateTime, DateOnly, Time, Uuid, Bytes) are left as-is; only object/array types are recursively made partial.
51
58
 
52
59
  ```typescript
53
60
  type DeepPartial<TObject> = Partial<{
54
- [K in keyof TObject]: TObject[K] extends PrimitiveType
55
- ? TObject[K]
56
- : DeepPartial<TObject[K]>;
61
+ [K in keyof TObject]: TObject[K] extends PrimitiveType ? TObject[K] : DeepPartial<TObject[K]>;
57
62
  }>;
58
63
  ```
59
64
 
60
- ```typescript
61
- interface User {
62
- name: string;
63
- profile: {
64
- age: number;
65
- address: { city: string };
66
- };
67
- }
68
-
69
- const partial: DeepPartial<User> = {
70
- profile: { address: {} },
71
- };
72
- ```
65
+ ---
73
66
 
74
- ## `Type<TInstance>`
67
+ ## `Type<T>`
75
68
 
76
- Constructor type interface. Represents a class constructor that creates instances of `TInstance`. Used for dependency injection, factory patterns, and `instanceof` checks.
69
+ Constructor type interface. Represents a class constructor that produces instances of type `T`. Used for dependency injection, factory patterns, and `instanceof` checks.
77
70
 
78
71
  ```typescript
79
72
  interface Type<TInstance> extends Function {
80
73
  new (...args: unknown[]): TInstance;
81
74
  }
82
75
  ```
83
-
84
- ```typescript
85
- function create<T>(ctor: Type<T>): T {
86
- return new ctor();
87
- }
88
-
89
- class MyClass { name = "test"; }
90
- const instance = create(MyClass); // MyClass instance
91
- ```