@mushi-mushi/cli 0.2.0 → 0.5.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.
- package/README.md +59 -10
- package/dist/chunk-I3FFGUE5.js +6 -0
- package/dist/chunk-ZZNVMBMG.js +256 -0
- package/dist/detect.d.ts +30 -0
- package/dist/detect.js +20 -0
- package/dist/index.js +764 -9
- package/dist/init.d.ts +39 -0
- package/dist/init.js +529 -0
- package/dist/version.d.ts +11 -0
- package/dist/version.js +6 -0
- package/package.json +48 -10
package/README.md
CHANGED
|
@@ -1,25 +1,74 @@
|
|
|
1
1
|
# @mushi-mushi/cli
|
|
2
2
|
|
|
3
|
-
CLI
|
|
3
|
+
CLI for Mushi Mushi — set up the SDK in one command, then triage reports and monitor the pipeline from your terminal.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## One-command setup
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npx @mushi-mushi/cli init
|
|
9
|
+
# equivalently:
|
|
10
|
+
npx mushi-mushi
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
The wizard:
|
|
14
|
+
|
|
15
|
+
1. Detects your framework (Next.js, Nuxt, SvelteKit, Angular, Expo, Capacitor, plain React/Vue/Svelte, or vanilla JS) from `package.json` and config files.
|
|
16
|
+
2. Picks the right SDK package (`@mushi-mushi/react`, `@mushi-mushi/vue`, etc.) plus `@mushi-mushi/web` when the framework SDK is API-only.
|
|
17
|
+
3. Detects your package manager (npm / pnpm / yarn / bun) from your lockfile and installs with that — `shell: false`, with Windows `.cmd` shim resolution.
|
|
18
|
+
4. Writes `MUSHI_PROJECT_ID` and `MUSHI_API_KEY` (with the right framework prefix — `NEXT_PUBLIC_`, `NUXT_PUBLIC_`, `VITE_`) to `.env.local` (or `.env`).
|
|
19
|
+
5. Warns you if `.env.local` isn't in `.gitignore` (covers `.env*.local`, `*.local`, etc.).
|
|
20
|
+
6. Prints the framework-specific provider snippet to copy-paste.
|
|
21
|
+
7. Offers to **send a real test report** so you see your first classified bug in the console immediately. Opt out via `--skip-test-report`.
|
|
22
|
+
|
|
23
|
+
It never silently overwrites existing env vars or modifies application code. Pasted credentials are sanitized (stripped of quotes / CR / LF / NUL) and validated against `^proj_[A-Za-z0-9_-]{10,}$` / `^mushi_[A-Za-z0-9_-]{10,}$` before anything is written to disk.
|
|
24
|
+
|
|
25
|
+
### Flags
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
mushi init --framework next # skip framework detection
|
|
29
|
+
mushi init --project-id proj_xxx --api-key mushi_xxx # skip credential prompts
|
|
30
|
+
mushi init --skip-install # print the install command instead
|
|
31
|
+
mushi init --skip-test-report # don't offer to send a test report
|
|
32
|
+
mushi init --cwd apps/web # run in a sub-package of a monorepo
|
|
33
|
+
mushi init --endpoint https://mushi.your-company.com # self-hosted Mushi API
|
|
34
|
+
mushi init -y # accept the detected framework
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Non-interactive use (CI): pass `--yes --project-id proj_xxx --api-key mushi_xxx` or the wizard exits with a clear error instead of hanging on a prompt.
|
|
38
|
+
|
|
39
|
+
Stale-version hint: the wizard checks the npm registry (2s timeout) and prints a one-line upgrade nudge if a newer stable is published. Opt out with `MUSHI_NO_UPDATE_CHECK=1`.
|
|
40
|
+
|
|
41
|
+
Monorepo awareness: if you run the wizard at a workspace root with no framework dep, it scans `apps/*`, `packages/*`, `examples/*` and tells you which sub-package you probably meant (`mushi init --cwd apps/web`).
|
|
42
|
+
|
|
43
|
+
## Install globally
|
|
6
44
|
|
|
7
45
|
```bash
|
|
8
46
|
npm install -g @mushi-mushi/cli
|
|
47
|
+
mushi --help
|
|
48
|
+
mushi --version
|
|
9
49
|
```
|
|
10
50
|
|
|
11
|
-
##
|
|
51
|
+
## Other commands
|
|
12
52
|
|
|
13
53
|
```bash
|
|
14
|
-
mushi login
|
|
15
|
-
mushi status
|
|
16
|
-
mushi reports list
|
|
17
|
-
mushi reports show <id>
|
|
18
|
-
mushi reports triage <id> --
|
|
19
|
-
mushi deploy check
|
|
20
|
-
mushi
|
|
54
|
+
mushi login --api-key mushi_xxx # store credentials in ~/.mushirc (mode 0o600)
|
|
55
|
+
mushi status # project overview
|
|
56
|
+
mushi reports list # recent reports
|
|
57
|
+
mushi reports show <id> # one report
|
|
58
|
+
mushi reports triage <id> --status acknowledged --severity high
|
|
59
|
+
mushi deploy check # edge-function health probe
|
|
60
|
+
mushi index <path> # walk a local repo and feed RAG
|
|
61
|
+
mushi test # submit a test report end-to-end
|
|
62
|
+
mushi config endpoint https://... # set API endpoint (https:// required outside localhost)
|
|
21
63
|
```
|
|
22
64
|
|
|
65
|
+
## Security notes
|
|
66
|
+
|
|
67
|
+
- `~/.mushirc` is written with mode `0o600` on Unix. Legacy configs with looser permissions are tightened on load.
|
|
68
|
+
- `--endpoint` values are parsed through `new URL()` and required to use `https://` except for `localhost` / `127.0.0.1` / `*.local`.
|
|
69
|
+
- The `--api-key` flag leaks into `ps -ef` — prefer the interactive prompt on shared machines.
|
|
70
|
+
- Full stack traces on error: `DEBUG=mushi mushi init`.
|
|
71
|
+
|
|
23
72
|
## License
|
|
24
73
|
|
|
25
74
|
MIT
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
// src/detect.ts
|
|
2
|
+
import { readFileSync, existsSync } from "fs";
|
|
3
|
+
import { join } from "path";
|
|
4
|
+
var FRAMEWORK_IDS = [
|
|
5
|
+
"next",
|
|
6
|
+
"react",
|
|
7
|
+
"vue",
|
|
8
|
+
"nuxt",
|
|
9
|
+
"svelte",
|
|
10
|
+
"sveltekit",
|
|
11
|
+
"angular",
|
|
12
|
+
"expo",
|
|
13
|
+
"react-native",
|
|
14
|
+
"capacitor",
|
|
15
|
+
"vanilla"
|
|
16
|
+
];
|
|
17
|
+
function isFrameworkId(value) {
|
|
18
|
+
return typeof value === "string" && FRAMEWORK_IDS.includes(value);
|
|
19
|
+
}
|
|
20
|
+
var FRAMEWORKS = {
|
|
21
|
+
next: {
|
|
22
|
+
id: "next",
|
|
23
|
+
label: "Next.js",
|
|
24
|
+
packageName: "@mushi-mushi/react",
|
|
25
|
+
needsWebPackage: false,
|
|
26
|
+
snippet: (apiKey, projectId) => `// app/providers.tsx (or pages/_app.tsx for /pages router)
|
|
27
|
+
'use client'
|
|
28
|
+
import { MushiProvider } from '@mushi-mushi/react'
|
|
29
|
+
|
|
30
|
+
export function Providers({ children }: { children: React.ReactNode }) {
|
|
31
|
+
return (
|
|
32
|
+
<MushiProvider config={{
|
|
33
|
+
projectId: '${projectId}',
|
|
34
|
+
apiKey: '${apiKey}',
|
|
35
|
+
}}>
|
|
36
|
+
{children}
|
|
37
|
+
</MushiProvider>
|
|
38
|
+
)
|
|
39
|
+
}`
|
|
40
|
+
},
|
|
41
|
+
react: {
|
|
42
|
+
id: "react",
|
|
43
|
+
label: "React",
|
|
44
|
+
packageName: "@mushi-mushi/react",
|
|
45
|
+
needsWebPackage: false,
|
|
46
|
+
snippet: (apiKey, projectId) => `// src/main.tsx
|
|
47
|
+
import { MushiProvider } from '@mushi-mushi/react'
|
|
48
|
+
|
|
49
|
+
createRoot(document.getElementById('root')!).render(
|
|
50
|
+
<MushiProvider config={{
|
|
51
|
+
projectId: '${projectId}',
|
|
52
|
+
apiKey: '${apiKey}',
|
|
53
|
+
}}>
|
|
54
|
+
<App />
|
|
55
|
+
</MushiProvider>
|
|
56
|
+
)`
|
|
57
|
+
},
|
|
58
|
+
vue: {
|
|
59
|
+
id: "vue",
|
|
60
|
+
label: "Vue 3",
|
|
61
|
+
packageName: "@mushi-mushi/vue",
|
|
62
|
+
needsWebPackage: true,
|
|
63
|
+
snippet: (apiKey, projectId) => `// src/main.ts
|
|
64
|
+
import { MushiPlugin } from '@mushi-mushi/vue'
|
|
65
|
+
import { Mushi } from '@mushi-mushi/web'
|
|
66
|
+
|
|
67
|
+
app.use(MushiPlugin, { projectId: '${projectId}', apiKey: '${apiKey}' })
|
|
68
|
+
Mushi.init({ projectId: '${projectId}', apiKey: '${apiKey}' })`
|
|
69
|
+
},
|
|
70
|
+
nuxt: {
|
|
71
|
+
id: "nuxt",
|
|
72
|
+
label: "Nuxt",
|
|
73
|
+
packageName: "@mushi-mushi/vue",
|
|
74
|
+
needsWebPackage: true,
|
|
75
|
+
snippet: (apiKey, projectId) => `// plugins/mushi.client.ts
|
|
76
|
+
import { MushiPlugin } from '@mushi-mushi/vue'
|
|
77
|
+
import { Mushi } from '@mushi-mushi/web'
|
|
78
|
+
|
|
79
|
+
export default defineNuxtPlugin((nuxtApp) => {
|
|
80
|
+
nuxtApp.vueApp.use(MushiPlugin, {
|
|
81
|
+
projectId: '${projectId}',
|
|
82
|
+
apiKey: '${apiKey}',
|
|
83
|
+
})
|
|
84
|
+
Mushi.init({ projectId: '${projectId}', apiKey: '${apiKey}' })
|
|
85
|
+
})`
|
|
86
|
+
},
|
|
87
|
+
svelte: {
|
|
88
|
+
id: "svelte",
|
|
89
|
+
label: "Svelte",
|
|
90
|
+
packageName: "@mushi-mushi/svelte",
|
|
91
|
+
needsWebPackage: true,
|
|
92
|
+
snippet: (apiKey, projectId) => `// src/main.ts (or +layout.svelte for SvelteKit)
|
|
93
|
+
import { initMushi } from '@mushi-mushi/svelte'
|
|
94
|
+
import { Mushi } from '@mushi-mushi/web'
|
|
95
|
+
|
|
96
|
+
initMushi({ projectId: '${projectId}', apiKey: '${apiKey}' })
|
|
97
|
+
Mushi.init({ projectId: '${projectId}', apiKey: '${apiKey}' })`
|
|
98
|
+
},
|
|
99
|
+
sveltekit: {
|
|
100
|
+
id: "sveltekit",
|
|
101
|
+
label: "SvelteKit",
|
|
102
|
+
packageName: "@mushi-mushi/svelte",
|
|
103
|
+
needsWebPackage: true,
|
|
104
|
+
snippet: (apiKey, projectId) => `// src/routes/+layout.svelte
|
|
105
|
+
<script>
|
|
106
|
+
import { onMount } from 'svelte'
|
|
107
|
+
import { initMushi } from '@mushi-mushi/svelte'
|
|
108
|
+
|
|
109
|
+
onMount(async () => {
|
|
110
|
+
const { Mushi } = await import('@mushi-mushi/web')
|
|
111
|
+
initMushi({ projectId: '${projectId}', apiKey: '${apiKey}' })
|
|
112
|
+
Mushi.init({ projectId: '${projectId}', apiKey: '${apiKey}' })
|
|
113
|
+
})
|
|
114
|
+
</script>`
|
|
115
|
+
},
|
|
116
|
+
angular: {
|
|
117
|
+
id: "angular",
|
|
118
|
+
label: "Angular",
|
|
119
|
+
packageName: "@mushi-mushi/angular",
|
|
120
|
+
needsWebPackage: true,
|
|
121
|
+
snippet: (apiKey, projectId) => `// src/main.ts
|
|
122
|
+
import { provideMushi } from '@mushi-mushi/angular'
|
|
123
|
+
import { Mushi } from '@mushi-mushi/web'
|
|
124
|
+
|
|
125
|
+
bootstrapApplication(AppComponent, {
|
|
126
|
+
providers: [
|
|
127
|
+
provideMushi({ projectId: '${projectId}', apiKey: '${apiKey}' }),
|
|
128
|
+
],
|
|
129
|
+
})
|
|
130
|
+
Mushi.init({ projectId: '${projectId}', apiKey: '${apiKey}' })`
|
|
131
|
+
},
|
|
132
|
+
expo: {
|
|
133
|
+
id: "expo",
|
|
134
|
+
label: "Expo",
|
|
135
|
+
packageName: "@mushi-mushi/react-native",
|
|
136
|
+
needsWebPackage: false,
|
|
137
|
+
snippet: (apiKey, projectId) => `// App.tsx
|
|
138
|
+
import { MushiProvider } from '@mushi-mushi/react-native'
|
|
139
|
+
|
|
140
|
+
export default function App() {
|
|
141
|
+
return (
|
|
142
|
+
<MushiProvider projectId="${projectId}" apiKey="${apiKey}">
|
|
143
|
+
<YourApp />
|
|
144
|
+
</MushiProvider>
|
|
145
|
+
)
|
|
146
|
+
}`
|
|
147
|
+
},
|
|
148
|
+
"react-native": {
|
|
149
|
+
id: "react-native",
|
|
150
|
+
label: "React Native",
|
|
151
|
+
packageName: "@mushi-mushi/react-native",
|
|
152
|
+
needsWebPackage: false,
|
|
153
|
+
snippet: (apiKey, projectId) => `// App.tsx
|
|
154
|
+
import { MushiProvider } from '@mushi-mushi/react-native'
|
|
155
|
+
|
|
156
|
+
export default function App() {
|
|
157
|
+
return (
|
|
158
|
+
<MushiProvider projectId="${projectId}" apiKey="${apiKey}">
|
|
159
|
+
<YourApp />
|
|
160
|
+
</MushiProvider>
|
|
161
|
+
)
|
|
162
|
+
}`
|
|
163
|
+
},
|
|
164
|
+
capacitor: {
|
|
165
|
+
id: "capacitor",
|
|
166
|
+
label: "Capacitor (Ionic)",
|
|
167
|
+
packageName: "@mushi-mushi/capacitor",
|
|
168
|
+
needsWebPackage: false,
|
|
169
|
+
snippet: (apiKey, projectId) => `// src/main.ts
|
|
170
|
+
import { Mushi } from '@mushi-mushi/capacitor'
|
|
171
|
+
|
|
172
|
+
Mushi.init({ projectId: '${projectId}', apiKey: '${apiKey}' })`
|
|
173
|
+
},
|
|
174
|
+
vanilla: {
|
|
175
|
+
id: "vanilla",
|
|
176
|
+
label: "Vanilla JS / unknown",
|
|
177
|
+
packageName: "@mushi-mushi/web",
|
|
178
|
+
needsWebPackage: false,
|
|
179
|
+
snippet: (apiKey, projectId) => `// Anywhere in your client bundle
|
|
180
|
+
import { Mushi } from '@mushi-mushi/web'
|
|
181
|
+
|
|
182
|
+
Mushi.init({ projectId: '${projectId}', apiKey: '${apiKey}' })`
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
function readPackageJson(cwd) {
|
|
186
|
+
const path = join(cwd, "package.json");
|
|
187
|
+
if (!existsSync(path)) return null;
|
|
188
|
+
try {
|
|
189
|
+
return JSON.parse(readFileSync(path, "utf-8"));
|
|
190
|
+
} catch {
|
|
191
|
+
return null;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
function detectFramework(cwd, pkg) {
|
|
195
|
+
const deps = collectDeps(pkg);
|
|
196
|
+
if (deps.has("next")) return FRAMEWORKS.next;
|
|
197
|
+
if (deps.has("nuxt")) return FRAMEWORKS.nuxt;
|
|
198
|
+
if (deps.has("@sveltejs/kit")) return FRAMEWORKS.sveltekit;
|
|
199
|
+
if (deps.has("@angular/core")) return FRAMEWORKS.angular;
|
|
200
|
+
if (deps.has("expo")) return FRAMEWORKS.expo;
|
|
201
|
+
if (deps.has("react-native")) return FRAMEWORKS["react-native"];
|
|
202
|
+
if (deps.has("@capacitor/core")) return FRAMEWORKS.capacitor;
|
|
203
|
+
if (deps.has("svelte")) return FRAMEWORKS.svelte;
|
|
204
|
+
if (deps.has("vue")) return FRAMEWORKS.vue;
|
|
205
|
+
if (deps.has("react")) return FRAMEWORKS.react;
|
|
206
|
+
if (existsSync(join(cwd, "next.config.js")) || existsSync(join(cwd, "next.config.ts"))) {
|
|
207
|
+
return FRAMEWORKS.next;
|
|
208
|
+
}
|
|
209
|
+
if (existsSync(join(cwd, "nuxt.config.ts")) || existsSync(join(cwd, "nuxt.config.js"))) {
|
|
210
|
+
return FRAMEWORKS.nuxt;
|
|
211
|
+
}
|
|
212
|
+
if (existsSync(join(cwd, "svelte.config.js"))) return FRAMEWORKS.svelte;
|
|
213
|
+
if (existsSync(join(cwd, "angular.json"))) return FRAMEWORKS.angular;
|
|
214
|
+
return FRAMEWORKS.vanilla;
|
|
215
|
+
}
|
|
216
|
+
function detectPackageManager(cwd) {
|
|
217
|
+
if (existsSync(join(cwd, "bun.lockb")) || existsSync(join(cwd, "bun.lock"))) return "bun";
|
|
218
|
+
if (existsSync(join(cwd, "pnpm-lock.yaml"))) return "pnpm";
|
|
219
|
+
if (existsSync(join(cwd, "yarn.lock"))) return "yarn";
|
|
220
|
+
if (existsSync(join(cwd, "package-lock.json"))) return "npm";
|
|
221
|
+
const userAgent = process.env.npm_config_user_agent ?? "";
|
|
222
|
+
if (userAgent.startsWith("bun")) return "bun";
|
|
223
|
+
if (userAgent.startsWith("pnpm")) return "pnpm";
|
|
224
|
+
if (userAgent.startsWith("yarn")) return "yarn";
|
|
225
|
+
return "npm";
|
|
226
|
+
}
|
|
227
|
+
function installCommand(pm, packages) {
|
|
228
|
+
const verb = pm === "npm" ? "install" : "add";
|
|
229
|
+
return `${pm} ${verb} ${packages.join(" ")}`;
|
|
230
|
+
}
|
|
231
|
+
function envVarsToWrite(apiKey, projectId, framework) {
|
|
232
|
+
const prefix = framework.id === "next" ? "NEXT_PUBLIC_" : framework.id === "nuxt" ? "NUXT_PUBLIC_" : "VITE_";
|
|
233
|
+
return [
|
|
234
|
+
`${prefix}MUSHI_PROJECT_ID=${projectId}`,
|
|
235
|
+
`${prefix}MUSHI_API_KEY=${apiKey}`
|
|
236
|
+
].join("\n");
|
|
237
|
+
}
|
|
238
|
+
function collectDeps(pkg) {
|
|
239
|
+
if (!pkg) return /* @__PURE__ */ new Set();
|
|
240
|
+
return /* @__PURE__ */ new Set([
|
|
241
|
+
...Object.keys(pkg.dependencies ?? {}),
|
|
242
|
+
...Object.keys(pkg.devDependencies ?? {}),
|
|
243
|
+
...Object.keys(pkg.peerDependencies ?? {})
|
|
244
|
+
]);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
export {
|
|
248
|
+
FRAMEWORK_IDS,
|
|
249
|
+
isFrameworkId,
|
|
250
|
+
FRAMEWORKS,
|
|
251
|
+
readPackageJson,
|
|
252
|
+
detectFramework,
|
|
253
|
+
detectPackageManager,
|
|
254
|
+
installCommand,
|
|
255
|
+
envVarsToWrite
|
|
256
|
+
};
|
package/dist/detect.d.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FILE: packages/cli/src/detect.ts
|
|
3
|
+
* PURPOSE: Pure detection helpers for framework, package manager, and project state.
|
|
4
|
+
* Kept side-effect-free so the wizard remains unit-testable.
|
|
5
|
+
*/
|
|
6
|
+
type FrameworkId = 'next' | 'react' | 'vue' | 'nuxt' | 'svelte' | 'sveltekit' | 'angular' | 'expo' | 'react-native' | 'capacitor' | 'vanilla';
|
|
7
|
+
interface Framework {
|
|
8
|
+
id: FrameworkId;
|
|
9
|
+
label: string;
|
|
10
|
+
packageName: string;
|
|
11
|
+
needsWebPackage: boolean;
|
|
12
|
+
snippet: (apiKey: string, projectId: string) => string;
|
|
13
|
+
}
|
|
14
|
+
interface PackageJson {
|
|
15
|
+
name?: string;
|
|
16
|
+
dependencies?: Record<string, string>;
|
|
17
|
+
devDependencies?: Record<string, string>;
|
|
18
|
+
peerDependencies?: Record<string, string>;
|
|
19
|
+
}
|
|
20
|
+
type PackageManager = 'npm' | 'pnpm' | 'yarn' | 'bun';
|
|
21
|
+
declare const FRAMEWORK_IDS: ReadonlyArray<FrameworkId>;
|
|
22
|
+
declare function isFrameworkId(value: unknown): value is FrameworkId;
|
|
23
|
+
declare const FRAMEWORKS: Record<FrameworkId, Framework>;
|
|
24
|
+
declare function readPackageJson(cwd: string): PackageJson | null;
|
|
25
|
+
declare function detectFramework(cwd: string, pkg: PackageJson | null): Framework;
|
|
26
|
+
declare function detectPackageManager(cwd: string): PackageManager;
|
|
27
|
+
declare function installCommand(pm: PackageManager, packages: string[]): string;
|
|
28
|
+
declare function envVarsToWrite(apiKey: string, projectId: string, framework: Framework): string;
|
|
29
|
+
|
|
30
|
+
export { FRAMEWORKS, FRAMEWORK_IDS, type Framework, type FrameworkId, type PackageJson, type PackageManager, detectFramework, detectPackageManager, envVarsToWrite, installCommand, isFrameworkId, readPackageJson };
|
package/dist/detect.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import {
|
|
2
|
+
FRAMEWORKS,
|
|
3
|
+
FRAMEWORK_IDS,
|
|
4
|
+
detectFramework,
|
|
5
|
+
detectPackageManager,
|
|
6
|
+
envVarsToWrite,
|
|
7
|
+
installCommand,
|
|
8
|
+
isFrameworkId,
|
|
9
|
+
readPackageJson
|
|
10
|
+
} from "./chunk-ZZNVMBMG.js";
|
|
11
|
+
export {
|
|
12
|
+
FRAMEWORKS,
|
|
13
|
+
FRAMEWORK_IDS,
|
|
14
|
+
detectFramework,
|
|
15
|
+
detectPackageManager,
|
|
16
|
+
envVarsToWrite,
|
|
17
|
+
installCommand,
|
|
18
|
+
isFrameworkId,
|
|
19
|
+
readPackageJson
|
|
20
|
+
};
|