specra-cli 0.3.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.
Files changed (172) hide show
  1. package/LICENSE.MD +33 -0
  2. package/README.md +246 -0
  3. package/dist/api-client-VHQARPDT.js +15 -0
  4. package/dist/api-client-VHQARPDT.js.map +1 -0
  5. package/dist/chunk-5765WX4D.js +192 -0
  6. package/dist/chunk-5765WX4D.js.map +1 -0
  7. package/dist/chunk-72RDEJR2.js +94 -0
  8. package/dist/chunk-72RDEJR2.js.map +1 -0
  9. package/dist/chunk-SQ2MMFUZ.js +102 -0
  10. package/dist/chunk-SQ2MMFUZ.js.map +1 -0
  11. package/dist/cli.d.ts +2 -0
  12. package/dist/cli.js +242 -0
  13. package/dist/cli.js.map +1 -0
  14. package/dist/deploy-V4JO2D6B.js +179 -0
  15. package/dist/deploy-V4JO2D6B.js.map +1 -0
  16. package/dist/doctor-ICALAJ4N.js +309 -0
  17. package/dist/doctor-ICALAJ4N.js.map +1 -0
  18. package/dist/login-UG3WU7DY.js +92 -0
  19. package/dist/login-UG3WU7DY.js.map +1 -0
  20. package/dist/logout-WJKHJZT6.js +24 -0
  21. package/dist/logout-WJKHJZT6.js.map +1 -0
  22. package/dist/logs-BLUJPWNO.js +77 -0
  23. package/dist/logs-BLUJPWNO.js.map +1 -0
  24. package/dist/projects-LJ57GK3D.js +49 -0
  25. package/dist/projects-LJ57GK3D.js.map +1 -0
  26. package/package.json +50 -0
  27. package/templates/book-docs/.env.sample +1 -0
  28. package/templates/book-docs/docs/v1.0.0/concepts.mdx +89 -0
  29. package/templates/book-docs/docs/v1.0.0/content/_category_.json +7 -0
  30. package/templates/book-docs/docs/v1.0.0/content/formatting.mdx +128 -0
  31. package/templates/book-docs/docs/v1.0.0/content/reusable-content.mdx +116 -0
  32. package/templates/book-docs/docs/v1.0.0/content/structure.mdx +92 -0
  33. package/templates/book-docs/docs/v1.0.0/customization/_category_.json +7 -0
  34. package/templates/book-docs/docs/v1.0.0/customization/branding.mdx +115 -0
  35. package/templates/book-docs/docs/v1.0.0/customization/themes.mdx +81 -0
  36. package/templates/book-docs/docs/v1.0.0/introduction.mdx +38 -0
  37. package/templates/book-docs/docs/v1.0.0/quickstart.mdx +112 -0
  38. package/templates/book-docs/docs/v2.0.0/concepts.mdx +89 -0
  39. package/templates/book-docs/docs/v2.0.0/content/_category_.json +7 -0
  40. package/templates/book-docs/docs/v2.0.0/content/formatting.mdx +128 -0
  41. package/templates/book-docs/docs/v2.0.0/content/reusable-content.mdx +116 -0
  42. package/templates/book-docs/docs/v2.0.0/content/structure.mdx +92 -0
  43. package/templates/book-docs/docs/v2.0.0/customization/_category_.json +7 -0
  44. package/templates/book-docs/docs/v2.0.0/customization/branding.mdx +115 -0
  45. package/templates/book-docs/docs/v2.0.0/customization/themes.mdx +81 -0
  46. package/templates/book-docs/docs/v2.0.0/introduction.mdx +39 -0
  47. package/templates/book-docs/docs/v2.0.0/quickstart.mdx +112 -0
  48. package/templates/book-docs/gitignore +7 -0
  49. package/templates/book-docs/package.json +28 -0
  50. package/templates/book-docs/postcss.config.mjs +8 -0
  51. package/templates/book-docs/public/api-specs/openapi-example.json +259 -0
  52. package/templates/book-docs/public/api-specs/postman-example.json +205 -0
  53. package/templates/book-docs/public/api-specs/test-api.json +256 -0
  54. package/templates/book-docs/public/api-specs/users-api.json +264 -0
  55. package/templates/book-docs/specra.config.json +77 -0
  56. package/templates/book-docs/src/app.css +86 -0
  57. package/templates/book-docs/src/app.html +17 -0
  58. package/templates/book-docs/src/params/product.ts +7 -0
  59. package/templates/book-docs/src/routes/+layout.server.ts +14 -0
  60. package/templates/book-docs/src/routes/+layout.svelte +21 -0
  61. package/templates/book-docs/src/routes/+page.server.ts +9 -0
  62. package/templates/book-docs/src/routes/docs/[product=product]/[version]/+layout.server.ts +40 -0
  63. package/templates/book-docs/src/routes/docs/[product=product]/[version]/+page.server.ts +24 -0
  64. package/templates/book-docs/src/routes/docs/[product=product]/[version]/[...slug]/+page.server.ts +131 -0
  65. package/templates/book-docs/src/routes/docs/[product=product]/[version]/[...slug]/+page.svelte +180 -0
  66. package/templates/book-docs/src/routes/docs/[version]/+layout.server.ts +42 -0
  67. package/templates/book-docs/src/routes/docs/[version]/+page.server.ts +27 -0
  68. package/templates/book-docs/src/routes/docs/[version]/[...slug]/+page.server.ts +106 -0
  69. package/templates/book-docs/src/routes/docs/[version]/[...slug]/+page.svelte +172 -0
  70. package/templates/book-docs/static/favicon.svg +4 -0
  71. package/templates/book-docs/svelte.config.js +13 -0
  72. package/templates/book-docs/tsconfig.json +12 -0
  73. package/templates/book-docs/vite.config.ts +6 -0
  74. package/templates/jbrains-docs/.env.sample +1 -0
  75. package/templates/jbrains-docs/docs/v1.0.0/advanced/_category_.json +8 -0
  76. package/templates/jbrains-docs/docs/v1.0.0/advanced/async.mdx +95 -0
  77. package/templates/jbrains-docs/docs/v1.0.0/advanced/generics.mdx +126 -0
  78. package/templates/jbrains-docs/docs/v1.0.0/basics/_category_.json +8 -0
  79. package/templates/jbrains-docs/docs/v1.0.0/basics/control-flow.mdx +106 -0
  80. package/templates/jbrains-docs/docs/v1.0.0/basics/syntax.mdx +129 -0
  81. package/templates/jbrains-docs/docs/v1.0.0/basics/types.mdx +135 -0
  82. package/templates/jbrains-docs/docs/v1.0.0/getting-started.mdx +111 -0
  83. package/templates/jbrains-docs/docs/v1.0.0/home.mdx +37 -0
  84. package/templates/jbrains-docs/docs/v1.0.0/tools/_category_.json +8 -0
  85. package/templates/jbrains-docs/docs/v1.0.0/tools/build-tools.mdx +165 -0
  86. package/templates/jbrains-docs/docs/v1.0.0/tools/testing.mdx +112 -0
  87. package/templates/jbrains-docs/docs/v2.0.0/advanced/_category_.json +8 -0
  88. package/templates/jbrains-docs/docs/v2.0.0/advanced/async.mdx +95 -0
  89. package/templates/jbrains-docs/docs/v2.0.0/advanced/generics.mdx +126 -0
  90. package/templates/jbrains-docs/docs/v2.0.0/basics/_category_.json +8 -0
  91. package/templates/jbrains-docs/docs/v2.0.0/basics/control-flow.mdx +106 -0
  92. package/templates/jbrains-docs/docs/v2.0.0/basics/syntax.mdx +129 -0
  93. package/templates/jbrains-docs/docs/v2.0.0/basics/types.mdx +135 -0
  94. package/templates/jbrains-docs/docs/v2.0.0/getting-started.mdx +111 -0
  95. package/templates/jbrains-docs/docs/v2.0.0/home.mdx +37 -0
  96. package/templates/jbrains-docs/docs/v2.0.0/tools/_category_.json +8 -0
  97. package/templates/jbrains-docs/docs/v2.0.0/tools/build-tools.mdx +165 -0
  98. package/templates/jbrains-docs/docs/v2.0.0/tools/testing.mdx +112 -0
  99. package/templates/jbrains-docs/gitignore +7 -0
  100. package/templates/jbrains-docs/package.json +28 -0
  101. package/templates/jbrains-docs/postcss.config.mjs +8 -0
  102. package/templates/jbrains-docs/public/api-specs/openapi-example.json +259 -0
  103. package/templates/jbrains-docs/public/api-specs/postman-example.json +205 -0
  104. package/templates/jbrains-docs/public/api-specs/test-api.json +256 -0
  105. package/templates/jbrains-docs/public/api-specs/users-api.json +264 -0
  106. package/templates/jbrains-docs/specra.config.json +80 -0
  107. package/templates/jbrains-docs/src/app.css +86 -0
  108. package/templates/jbrains-docs/src/app.html +17 -0
  109. package/templates/jbrains-docs/src/params/product.ts +7 -0
  110. package/templates/jbrains-docs/src/routes/+layout.server.ts +14 -0
  111. package/templates/jbrains-docs/src/routes/+layout.svelte +21 -0
  112. package/templates/jbrains-docs/src/routes/+page.server.ts +9 -0
  113. package/templates/jbrains-docs/src/routes/docs/[product=product]/[version]/+layout.server.ts +40 -0
  114. package/templates/jbrains-docs/src/routes/docs/[product=product]/[version]/+page.server.ts +24 -0
  115. package/templates/jbrains-docs/src/routes/docs/[product=product]/[version]/[...slug]/+page.server.ts +131 -0
  116. package/templates/jbrains-docs/src/routes/docs/[product=product]/[version]/[...slug]/+page.svelte +180 -0
  117. package/templates/jbrains-docs/src/routes/docs/[version]/+layout.server.ts +42 -0
  118. package/templates/jbrains-docs/src/routes/docs/[version]/+page.server.ts +27 -0
  119. package/templates/jbrains-docs/src/routes/docs/[version]/[...slug]/+page.server.ts +106 -0
  120. package/templates/jbrains-docs/src/routes/docs/[version]/[...slug]/+page.svelte +172 -0
  121. package/templates/jbrains-docs/static/favicon.svg +4 -0
  122. package/templates/jbrains-docs/svelte.config.js +13 -0
  123. package/templates/jbrains-docs/tsconfig.json +12 -0
  124. package/templates/jbrains-docs/vite.config.ts +6 -0
  125. package/templates/minimal/.env.sample +1 -0
  126. package/templates/minimal/docs/v1.0.0/about.mdx +57 -0
  127. package/templates/minimal/docs/v1.0.0/components/_category_.json +8 -0
  128. package/templates/minimal/docs/v1.0.0/components/callout.mdx +83 -0
  129. package/templates/minimal/docs/v1.0.0/components/code-block.mdx +103 -0
  130. package/templates/minimal/docs/v1.0.0/components/index.mdx +8 -0
  131. package/templates/minimal/docs/v1.0.0/components/tabs.mdx +92 -0
  132. package/templates/minimal/docs/v1.0.0/configuration.mdx +322 -0
  133. package/templates/minimal/docs/v1.0.0/features.mdx +197 -0
  134. package/templates/minimal/docs/v1.0.0/getting-started.mdx +183 -0
  135. package/templates/minimal/docs/v2.0.0/about.mdx +57 -0
  136. package/templates/minimal/docs/v2.0.0/components/_category_.json +8 -0
  137. package/templates/minimal/docs/v2.0.0/components/callout.mdx +83 -0
  138. package/templates/minimal/docs/v2.0.0/components/code-block.mdx +103 -0
  139. package/templates/minimal/docs/v2.0.0/components/index.mdx +8 -0
  140. package/templates/minimal/docs/v2.0.0/components/tabs.mdx +92 -0
  141. package/templates/minimal/docs/v2.0.0/configuration.mdx +322 -0
  142. package/templates/minimal/docs/v2.0.0/features.mdx +197 -0
  143. package/templates/minimal/docs/v2.0.0/getting-started.mdx +183 -0
  144. package/templates/minimal/gitignore +7 -0
  145. package/templates/minimal/package.json +29 -0
  146. package/templates/minimal/postcss.config.mjs +8 -0
  147. package/templates/minimal/specra.config.json +91 -0
  148. package/templates/minimal/src/app.css +86 -0
  149. package/templates/minimal/src/app.html +17 -0
  150. package/templates/minimal/src/hooks.server.ts +8 -0
  151. package/templates/minimal/src/params/product.ts +7 -0
  152. package/templates/minimal/src/routes/+error.svelte +10 -0
  153. package/templates/minimal/src/routes/+layout.server.ts +14 -0
  154. package/templates/minimal/src/routes/+layout.svelte +21 -0
  155. package/templates/minimal/src/routes/+page.server.ts +9 -0
  156. package/templates/minimal/src/routes/+page.svelte +149 -0
  157. package/templates/minimal/src/routes/docs/[product=product]/[version]/+layout.server.ts +40 -0
  158. package/templates/minimal/src/routes/docs/[product=product]/[version]/+page.server.ts +24 -0
  159. package/templates/minimal/src/routes/docs/[product=product]/[version]/[...slug]/+page.server.ts +131 -0
  160. package/templates/minimal/src/routes/docs/[product=product]/[version]/[...slug]/+page.svelte +180 -0
  161. package/templates/minimal/src/routes/docs/[version]/+layout.server.ts +42 -0
  162. package/templates/minimal/src/routes/docs/[version]/+page.server.ts +27 -0
  163. package/templates/minimal/src/routes/docs/[version]/[...slug]/+page.server.ts +106 -0
  164. package/templates/minimal/src/routes/docs/[version]/[...slug]/+page.svelte +172 -0
  165. package/templates/minimal/static/api-specs/openapi-example.json +259 -0
  166. package/templates/minimal/static/api-specs/postman-example.json +205 -0
  167. package/templates/minimal/static/api-specs/test-api.json +256 -0
  168. package/templates/minimal/static/api-specs/users-api.json +264 -0
  169. package/templates/minimal/static/favicon.svg +4 -0
  170. package/templates/minimal/svelte.config.js +13 -0
  171. package/templates/minimal/tsconfig.json +12 -0
  172. package/templates/minimal/vite.config.ts +6 -0
package/LICENSE.MD ADDED
@@ -0,0 +1,33 @@
1
+ MIT License with Branding Requirement
2
+
3
+ Copyright (c) 2025-2026 dalmasonto, arthur-kamau
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ 1. The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ 2. BRANDING / WATERMARK REQUIREMENT: All documentation sites generated using
16
+ this Software or the associated Specra SDK MUST display the "Powered by
17
+ Specra" watermark in a visible location (footer or equivalent) on every
18
+ public-facing page.
19
+
20
+ The watermark MUST NOT be removed, hidden, obscured, or made invisible
21
+ unless the user holds an active paid subscription (Starter tier or above)
22
+ on the Specra platform (https://specra-docs.com).
23
+
24
+ Removal, hiding, or circumvention of the watermark without a valid paid
25
+ subscription constitutes a COPYRIGHT VIOLATION and a breach of this license.
26
+
27
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
28
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
29
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
30
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
31
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
32
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
33
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,246 @@
1
+ # create-specra
2
+
3
+ The fastest way to create a new Specra documentation site. Scaffold a complete documentation project with a single command.
4
+
5
+ ## What is Specra?
6
+
7
+ Specra is a modern documentation library for SvelteKit that provides:
8
+ - Multi-version documentation support
9
+ - API reference generation
10
+ - Full-text search
11
+ - MDX-powered content
12
+ - Beautiful UI components
13
+
14
+ The official Specra site ([specra-docs](https://specra-docs.com)) also offers a SaaS platform with paid tiers (Starter, Pro, Enterprise) including authentication, Stripe/M-Pesa billing, and a user dashboard. The CLI scaffolds free, self-hosted documentation sites — no billing features are included in generated projects.
15
+
16
+ ## Usage
17
+
18
+ ### With npx (recommended)
19
+
20
+ ```bash
21
+ npx create-specra my-docs
22
+ ```
23
+
24
+ ### With npm
25
+
26
+ ```bash
27
+ npm create specra my-docs
28
+ ```
29
+
30
+ ### With yarn
31
+
32
+ ```bash
33
+ yarn create specra my-docs
34
+ ```
35
+
36
+ ### With pnpm
37
+
38
+ ```bash
39
+ pnpm create specra my-docs
40
+ ```
41
+
42
+ ## Options
43
+
44
+ ```bash
45
+ npx create-specra [project-directory] [options]
46
+ ```
47
+
48
+ ### Arguments
49
+
50
+ - `[project-directory]` - The directory to create the project in (optional, will prompt if not provided)
51
+
52
+ ### Options
53
+
54
+ - `--template <template>` - Template to use: `minimal`, `book-docs`, `jbrains-docs` (will prompt if not provided)
55
+ - `--use-npm` - Use npm as the package manager
56
+ - `--use-pnpm` - Use pnpm as the package manager
57
+ - `--use-yarn` - Use yarn as the package manager
58
+ - `--skip-install` - Skip package installation
59
+
60
+ ## Templates
61
+
62
+ ### Minimal (Default)
63
+
64
+ Minimal setup to get started quickly:
65
+ - Basic documentation structure
66
+ - Essential configuration
67
+ - Clean starting point
68
+ - Ready to customize
69
+
70
+ ### Book Docs
71
+
72
+ Knowledge base style inspired by popular docs platforms:
73
+ - Dark theme by default
74
+ - Categorized sidebar with section headers (Content, Customization)
75
+ - Site-wide banner
76
+ - Flush sidebar layout (attached to screen edge)
77
+ - Version badge in sidebar
78
+
79
+ ### JBrains Docs
80
+
81
+ Reference documentation style with tabbed navigation:
82
+ - Light theme by default
83
+ - Tab groups for organizing content (Language, Multiplatform)
84
+ - Collapsible tree-style sidebar
85
+ - No table of contents (right panel)
86
+ - Flush sidebar layout
87
+ - Version badge in sidebar
88
+
89
+ ## Examples
90
+
91
+ Create a new project with interactive prompts:
92
+
93
+ ```bash
94
+ npx create-specra
95
+ ```
96
+
97
+ Create a project with the minimal template using npm:
98
+
99
+ ```bash
100
+ npx create-specra my-docs --template minimal --use-npm
101
+ ```
102
+
103
+ Create a project with the book-docs template:
104
+
105
+ ```bash
106
+ npx create-specra my-docs --template book-docs
107
+ ```
108
+
109
+ Create a project and skip installation:
110
+
111
+ ```bash
112
+ npx create-specra my-docs --skip-install
113
+ ```
114
+
115
+ ## What's Created
116
+
117
+ The CLI creates a new SvelteKit project with Specra pre-configured:
118
+
119
+ ```
120
+ my-docs/
121
+ ├── src/
122
+ │ ├── routes/
123
+ │ │ ├── +layout.svelte
124
+ │ │ ├── +page.svelte
125
+ │ │ └── docs/
126
+ │ │ └── [version]/[...slug]/
127
+ │ │ ├── +page.svelte
128
+ │ │ └── +page.ts
129
+ │ └── app.html
130
+ ├── docs/
131
+ │ └── v1.0.0/
132
+ │ └── index.mdx
133
+ ├── static/
134
+ ├── specra.config.json
135
+ ├── svelte.config.js
136
+ ├── vite.config.ts
137
+ └── package.json
138
+ ```
139
+
140
+ ## After Creation
141
+
142
+ Once your project is created, you can:
143
+
144
+ 1. Start the development server:
145
+ ```bash
146
+ cd my-docs
147
+ npm run dev
148
+ ```
149
+
150
+ 2. Open [http://localhost:5173](http://localhost:5173) in your browser
151
+
152
+ 3. Edit your documentation in the `docs/` directory
153
+
154
+ 4. Customize your site in `specra.config.json`
155
+
156
+ ## Deployment
157
+
158
+ Specra projects are standard SvelteKit apps, so you can deploy anywhere SvelteKit runs.
159
+
160
+ ### Static Hosting (Vercel, Netlify, Cloudflare Pages)
161
+
162
+ Install the appropriate SvelteKit adapter:
163
+
164
+ ```bash
165
+ # Vercel
166
+ npm install -D @sveltejs/adapter-vercel
167
+
168
+ # Netlify
169
+ npm install -D @sveltejs/adapter-netlify
170
+
171
+ # Cloudflare Pages
172
+ npm install -D @sveltejs/adapter-cloudflare
173
+ ```
174
+
175
+ Update `svelte.config.js` to use the adapter:
176
+
177
+ ```js
178
+ import adapter from '@sveltejs/adapter-vercel'; // or adapter-netlify, etc.
179
+
180
+ export default {
181
+ kit: {
182
+ adapter: adapter()
183
+ }
184
+ };
185
+ ```
186
+
187
+ Then push to your Git provider — the platform handles the rest.
188
+
189
+ ### Node Server (VPS, Docker, Railway)
190
+
191
+ Use `@sveltejs/adapter-node` (included by default):
192
+
193
+ ```bash
194
+ npm run build
195
+ node build
196
+ ```
197
+
198
+ The server listens on port 3000 by default. Configure with environment variables:
199
+
200
+ ```bash
201
+ PORT=8080 HOST=0.0.0.0 node build
202
+ ```
203
+
204
+ ### Static Site Generation
205
+
206
+ For fully static docs with no server needed:
207
+
208
+ ```bash
209
+ npm install -D @sveltejs/adapter-static
210
+ ```
211
+
212
+ Update `svelte.config.js`:
213
+
214
+ ```js
215
+ import adapter from '@sveltejs/adapter-static';
216
+
217
+ export default {
218
+ kit: {
219
+ adapter: adapter({
220
+ fallback: '404.html'
221
+ })
222
+ }
223
+ };
224
+ ```
225
+
226
+ ```bash
227
+ npm run build
228
+ ```
229
+
230
+ Upload the `build/` directory to any static host (GitHub Pages, S3, etc.).
231
+
232
+ ## Learn More
233
+
234
+ - [Specra on npm](https://www.npmjs.com/package/specra)
235
+ - [SvelteKit Documentation](https://svelte.dev/docs/kit)
236
+ - [MDX Documentation](https://mdxjs.com)
237
+
238
+ ## License
239
+
240
+ MIT with Branding Requirement — see [LICENSE.MD](LICENSE.MD).
241
+
242
+ All documentation sites generated with create-specra display a "Powered by Specra" watermark by default. Removing the watermark requires an active paid subscription (Starter tier or above) at [specra-docs.com](https://specra-docs.com). Unauthorized removal is a copyright violation.
243
+
244
+ ## Authors
245
+
246
+ dalmasonto, arthur-kamau
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ ApiError,
4
+ apiRequest,
5
+ apiUpload,
6
+ formatError
7
+ } from "./chunk-72RDEJR2.js";
8
+ import "./chunk-5765WX4D.js";
9
+ export {
10
+ ApiError,
11
+ apiRequest,
12
+ apiUpload,
13
+ formatError
14
+ };
15
+ //# sourceMappingURL=api-client-VHQARPDT.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,192 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/config.ts
4
+ import { readFileSync, writeFileSync, mkdirSync, existsSync, unlinkSync } from "fs";
5
+ import { join, resolve } from "path";
6
+ import { homedir } from "os";
7
+ var GLOBAL_CONFIG_DIR = join(homedir(), ".specra");
8
+ var GLOBAL_CONFIG_FILE = join(GLOBAL_CONFIG_DIR, "config.json");
9
+ var DEFAULT_TOKEN_ENV = "SPECRA_TOKEN";
10
+ var DEFAULT_GLOBAL_CONFIG = {
11
+ apiUrl: "https://specra-docs.com"
12
+ };
13
+ function getEnvFilePath(dir) {
14
+ return join(resolve(dir || "."), ".env");
15
+ }
16
+ function readEnvFile(dir) {
17
+ const envPath = getEnvFilePath(dir);
18
+ try {
19
+ const content = readFileSync(envPath, "utf-8");
20
+ const env = {};
21
+ for (const line of content.split("\n")) {
22
+ const trimmed = line.trim();
23
+ if (!trimmed || trimmed.startsWith("#")) continue;
24
+ const eqIdx = trimmed.indexOf("=");
25
+ if (eqIdx === -1) continue;
26
+ const key = trimmed.slice(0, eqIdx).trim();
27
+ const value = trimmed.slice(eqIdx + 1).trim().replace(/^["']|["']$/g, "");
28
+ env[key] = value;
29
+ }
30
+ return env;
31
+ } catch {
32
+ return {};
33
+ }
34
+ }
35
+ function writeEnvVar(key, value, dir) {
36
+ const envPath = getEnvFilePath(dir);
37
+ let content = "";
38
+ try {
39
+ content = readFileSync(envPath, "utf-8");
40
+ } catch {
41
+ }
42
+ const lines = content.split("\n");
43
+ let found = false;
44
+ for (let i = 0; i < lines.length; i++) {
45
+ if (lines[i].trim().startsWith(`${key}=`)) {
46
+ lines[i] = `${key}=${value}`;
47
+ found = true;
48
+ break;
49
+ }
50
+ }
51
+ if (!found) {
52
+ if (lines.length > 0 && lines[lines.length - 1] !== "") {
53
+ lines.push("");
54
+ }
55
+ lines.push(`${key}=${value}`);
56
+ }
57
+ writeFileSync(envPath, lines.join("\n") + "\n");
58
+ }
59
+ function removeEnvVar(key, dir) {
60
+ const envPath = getEnvFilePath(dir);
61
+ try {
62
+ const content = readFileSync(envPath, "utf-8");
63
+ const lines = content.split("\n").filter((line) => !line.trim().startsWith(`${key}=`));
64
+ writeFileSync(envPath, lines.join("\n"));
65
+ } catch {
66
+ }
67
+ }
68
+ function ensureGitignore(dir) {
69
+ const gitignorePath = join(resolve(dir || "."), ".gitignore");
70
+ try {
71
+ let content = "";
72
+ try {
73
+ content = readFileSync(gitignorePath, "utf-8");
74
+ } catch {
75
+ }
76
+ if (!content.split("\n").some((line) => line.trim() === ".env")) {
77
+ const newline = content && !content.endsWith("\n") ? "\n" : "";
78
+ writeFileSync(gitignorePath, content + newline + ".env\n");
79
+ }
80
+ } catch {
81
+ }
82
+ }
83
+ function getGlobalConfig() {
84
+ try {
85
+ const raw = readFileSync(GLOBAL_CONFIG_FILE, "utf-8");
86
+ return { ...DEFAULT_GLOBAL_CONFIG, ...JSON.parse(raw) };
87
+ } catch {
88
+ return DEFAULT_GLOBAL_CONFIG;
89
+ }
90
+ }
91
+ function saveGlobalConfig(config) {
92
+ mkdirSync(GLOBAL_CONFIG_DIR, { recursive: true });
93
+ const current = getGlobalConfig();
94
+ const merged = { ...current, ...config };
95
+ writeFileSync(GLOBAL_CONFIG_FILE, JSON.stringify(merged, null, 2));
96
+ }
97
+ function clearGlobalToken() {
98
+ const config = getGlobalConfig();
99
+ delete config.token;
100
+ mkdirSync(GLOBAL_CONFIG_DIR, { recursive: true });
101
+ writeFileSync(GLOBAL_CONFIG_FILE, JSON.stringify(config, null, 2));
102
+ }
103
+ function getLocalConfigPath(dir) {
104
+ return join(resolve(dir || "."), "specra.config.json");
105
+ }
106
+ function readLocalConfig(dir) {
107
+ const configPath = getLocalConfigPath(dir);
108
+ try {
109
+ return JSON.parse(readFileSync(configPath, "utf-8"));
110
+ } catch {
111
+ return null;
112
+ }
113
+ }
114
+ function getLocalToken(dir) {
115
+ const config = readLocalConfig(dir);
116
+ if (!config?.auth) return void 0;
117
+ if (config.auth.tokenEnv) {
118
+ const envName = config.auth.tokenEnv;
119
+ if (process.env[envName]) return process.env[envName];
120
+ const envVars = readEnvFile(dir);
121
+ return envVars[envName];
122
+ }
123
+ if (config.auth.source === "global") {
124
+ return getGlobalConfig().token;
125
+ }
126
+ return config.auth.token;
127
+ }
128
+ function saveLocalToken(token, dir) {
129
+ const configPath = getLocalConfigPath(dir);
130
+ if (!existsSync(configPath)) {
131
+ throw new Error(`specra.config.json not found in ${resolve(dir || ".")}. Are you in a Specra project?`);
132
+ }
133
+ writeEnvVar(DEFAULT_TOKEN_ENV, token, dir);
134
+ const config = JSON.parse(readFileSync(configPath, "utf-8"));
135
+ config.auth = { tokenEnv: DEFAULT_TOKEN_ENV };
136
+ writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
137
+ ensureGitignore(dir);
138
+ }
139
+ function clearLocalToken(dir) {
140
+ removeEnvVar(DEFAULT_TOKEN_ENV, dir);
141
+ const configPath = getLocalConfigPath(dir);
142
+ if (!existsSync(configPath)) return;
143
+ try {
144
+ const config = JSON.parse(readFileSync(configPath, "utf-8"));
145
+ if (config.auth) {
146
+ delete config.auth;
147
+ }
148
+ writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
149
+ } catch {
150
+ }
151
+ }
152
+ function getToken(dir) {
153
+ return getLocalToken(dir) || getGlobalConfig().token;
154
+ }
155
+ function getConfig() {
156
+ return getGlobalConfig();
157
+ }
158
+ function isAuthenticated(dir) {
159
+ return !!getToken(dir);
160
+ }
161
+ function saveToken(token, options) {
162
+ if (options?.global) {
163
+ saveGlobalConfig({ token });
164
+ const configPath = getLocalConfigPath(options?.dir);
165
+ if (existsSync(configPath)) {
166
+ try {
167
+ const config = JSON.parse(readFileSync(configPath, "utf-8"));
168
+ config.auth = { source: "global" };
169
+ writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
170
+ } catch {
171
+ }
172
+ }
173
+ } else {
174
+ saveLocalToken(token, options?.dir);
175
+ }
176
+ }
177
+ function clearToken(options) {
178
+ if (options?.global) {
179
+ clearGlobalToken();
180
+ } else {
181
+ clearLocalToken(options?.dir);
182
+ }
183
+ }
184
+
185
+ export {
186
+ getToken,
187
+ getConfig,
188
+ isAuthenticated,
189
+ saveToken,
190
+ clearToken
191
+ };
192
+ //# sourceMappingURL=chunk-5765WX4D.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/config.ts"],"sourcesContent":["import { readFileSync, writeFileSync, mkdirSync, existsSync, unlinkSync } from 'fs'\nimport { join, resolve } from 'path'\nimport { homedir } from 'os'\n\n// Global config: ~/.specra/config.json\nconst GLOBAL_CONFIG_DIR = join(homedir(), '.specra')\nconst GLOBAL_CONFIG_FILE = join(GLOBAL_CONFIG_DIR, 'config.json')\n\nconst DEFAULT_TOKEN_ENV = 'SPECRA_TOKEN'\n\ninterface GlobalConfig {\n apiUrl: string\n token?: string\n defaultProject?: string\n}\n\nconst DEFAULT_GLOBAL_CONFIG: GlobalConfig = {\n apiUrl: 'https://specra-docs.com',\n}\n\n// --------------- .env file helpers ---------------\n\nfunction getEnvFilePath(dir?: string): string {\n return join(resolve(dir || '.'), '.env')\n}\n\nfunction readEnvFile(dir?: string): Record<string, string> {\n const envPath = getEnvFilePath(dir)\n try {\n const content = readFileSync(envPath, 'utf-8')\n const env: Record<string, string> = {}\n for (const line of content.split('\\n')) {\n const trimmed = line.trim()\n if (!trimmed || trimmed.startsWith('#')) continue\n const eqIdx = trimmed.indexOf('=')\n if (eqIdx === -1) continue\n const key = trimmed.slice(0, eqIdx).trim()\n const value = trimmed.slice(eqIdx + 1).trim().replace(/^[\"']|[\"']$/g, '')\n env[key] = value\n }\n return env\n } catch {\n return {}\n }\n}\n\nfunction writeEnvVar(key: string, value: string, dir?: string) {\n const envPath = getEnvFilePath(dir)\n let content = ''\n try {\n content = readFileSync(envPath, 'utf-8')\n } catch {\n // file doesn't exist yet\n }\n\n const lines = content.split('\\n')\n let found = false\n for (let i = 0; i < lines.length; i++) {\n if (lines[i].trim().startsWith(`${key}=`)) {\n lines[i] = `${key}=${value}`\n found = true\n break\n }\n }\n\n if (!found) {\n // Ensure trailing newline before appending\n if (lines.length > 0 && lines[lines.length - 1] !== '') {\n lines.push('')\n }\n lines.push(`${key}=${value}`)\n }\n\n writeFileSync(envPath, lines.join('\\n') + '\\n')\n}\n\nfunction removeEnvVar(key: string, dir?: string) {\n const envPath = getEnvFilePath(dir)\n try {\n const content = readFileSync(envPath, 'utf-8')\n const lines = content.split('\\n').filter(line => !line.trim().startsWith(`${key}=`))\n writeFileSync(envPath, lines.join('\\n'))\n } catch {\n // file doesn't exist, nothing to remove\n }\n}\n\nfunction ensureGitignore(dir?: string) {\n const gitignorePath = join(resolve(dir || '.'), '.gitignore')\n try {\n let content = ''\n try {\n content = readFileSync(gitignorePath, 'utf-8')\n } catch {\n // no .gitignore yet\n }\n if (!content.split('\\n').some(line => line.trim() === '.env')) {\n const newline = content && !content.endsWith('\\n') ? '\\n' : ''\n writeFileSync(gitignorePath, content + newline + '.env\\n')\n }\n } catch {\n // ignore\n }\n}\n\n// --------------- Global config ---------------\n\nexport function getGlobalConfig(): GlobalConfig {\n try {\n const raw = readFileSync(GLOBAL_CONFIG_FILE, 'utf-8')\n return { ...DEFAULT_GLOBAL_CONFIG, ...JSON.parse(raw) }\n } catch {\n return DEFAULT_GLOBAL_CONFIG\n }\n}\n\nexport function saveGlobalConfig(config: Partial<GlobalConfig>) {\n mkdirSync(GLOBAL_CONFIG_DIR, { recursive: true })\n const current = getGlobalConfig()\n const merged = { ...current, ...config }\n writeFileSync(GLOBAL_CONFIG_FILE, JSON.stringify(merged, null, 2))\n}\n\nexport function clearGlobalToken() {\n const config = getGlobalConfig()\n delete config.token\n mkdirSync(GLOBAL_CONFIG_DIR, { recursive: true })\n writeFileSync(GLOBAL_CONFIG_FILE, JSON.stringify(config, null, 2))\n}\n\n// --------------- Local config (specra.config.json) ---------------\n\nfunction getLocalConfigPath(dir?: string): string {\n return join(resolve(dir || '.'), 'specra.config.json')\n}\n\nfunction readLocalConfig(dir?: string): Record<string, any> | null {\n const configPath = getLocalConfigPath(dir)\n try {\n return JSON.parse(readFileSync(configPath, 'utf-8'))\n } catch {\n return null\n }\n}\n\nexport function getLocalToken(dir?: string): string | undefined {\n const config = readLocalConfig(dir)\n if (!config?.auth) return undefined\n\n // Env var reference: auth.tokenEnv → read from process.env, then .env file\n if (config.auth.tokenEnv) {\n const envName = config.auth.tokenEnv\n if (process.env[envName]) return process.env[envName]\n const envVars = readEnvFile(dir)\n return envVars[envName]\n }\n\n // Global reference: auth.source === \"global\" → read from ~/.specra/config.json\n if (config.auth.source === 'global') {\n return getGlobalConfig().token\n }\n\n // Legacy: direct auth.token (backwards compat for existing projects)\n return config.auth.token\n}\n\nexport function saveLocalToken(token: string, dir?: string) {\n const configPath = getLocalConfigPath(dir)\n if (!existsSync(configPath)) {\n throw new Error(`specra.config.json not found in ${resolve(dir || '.')}. Are you in a Specra project?`)\n }\n\n // Save the actual token to .env\n writeEnvVar(DEFAULT_TOKEN_ENV, token, dir)\n\n // Point specra.config.json at the env var (never store raw token)\n const config = JSON.parse(readFileSync(configPath, 'utf-8'))\n config.auth = { tokenEnv: DEFAULT_TOKEN_ENV }\n writeFileSync(configPath, JSON.stringify(config, null, 2) + '\\n')\n\n // Make sure .env is gitignored\n ensureGitignore(dir)\n}\n\nexport function clearLocalToken(dir?: string) {\n // Remove token from .env\n removeEnvVar(DEFAULT_TOKEN_ENV, dir)\n\n // Clean up auth section from specra.config.json\n const configPath = getLocalConfigPath(dir)\n if (!existsSync(configPath)) return\n try {\n const config = JSON.parse(readFileSync(configPath, 'utf-8'))\n if (config.auth) {\n delete config.auth\n }\n writeFileSync(configPath, JSON.stringify(config, null, 2) + '\\n')\n } catch {\n // ignore parse errors\n }\n}\n\n// --------------- Unified accessors ---------------\n\n/** Get token: checks local specra.config.json first, then global ~/.specra/config.json */\nexport function getToken(dir?: string): string | undefined {\n return getLocalToken(dir) || getGlobalConfig().token\n}\n\n/** Get the API URL from global config */\nexport function getConfig(): GlobalConfig {\n return getGlobalConfig()\n}\n\nexport function isAuthenticated(dir?: string): boolean {\n return !!getToken(dir)\n}\n\n/** Save token to local .env (default) or global ~/.specra/config.json */\nexport function saveToken(token: string, options?: { global?: boolean; dir?: string }) {\n if (options?.global) {\n saveGlobalConfig({ token })\n // If specra.config.json exists locally, point it at global\n const configPath = getLocalConfigPath(options?.dir)\n if (existsSync(configPath)) {\n try {\n const config = JSON.parse(readFileSync(configPath, 'utf-8'))\n config.auth = { source: 'global' }\n writeFileSync(configPath, JSON.stringify(config, null, 2) + '\\n')\n } catch {\n // ignore\n }\n }\n } else {\n saveLocalToken(token, options?.dir)\n }\n}\n\n/** Clear token from local .env (default) or global config */\nexport function clearToken(options?: { global?: boolean; dir?: string }) {\n if (options?.global) {\n clearGlobalToken()\n } else {\n clearLocalToken(options?.dir)\n }\n}\n\n// Legacy exports for backwards compat\nexport function saveConfig(config: Partial<GlobalConfig>) {\n saveGlobalConfig(config)\n}\n\nexport function clearConfig() {\n if (existsSync(GLOBAL_CONFIG_FILE)) {\n unlinkSync(GLOBAL_CONFIG_FILE)\n }\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,94 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ getConfig,
4
+ getToken
5
+ } from "./chunk-5765WX4D.js";
6
+
7
+ // src/api-client.ts
8
+ var ApiError = class extends Error {
9
+ constructor(status, message) {
10
+ super(message);
11
+ this.status = status;
12
+ this.name = "ApiError";
13
+ }
14
+ };
15
+ var STATUS_HINTS = {
16
+ 401: "Try running `specra login` to re-authenticate.",
17
+ 403: "You may not have permission for this action. Check your plan or project access.",
18
+ 502: "The server is temporarily unavailable. Try again in a moment.",
19
+ 503: "The server is temporarily unavailable. Try again in a moment."
20
+ };
21
+ function formatError(context, err) {
22
+ const prefix = context ? `${context}: ` : "";
23
+ if (err instanceof ApiError) {
24
+ const hint = STATUS_HINTS[err.status] || "";
25
+ return `${prefix}${err.message} (${err.status})${hint ? `
26
+ ${hint}` : ""}`;
27
+ }
28
+ if (err instanceof Error) {
29
+ return `${prefix}${err.message}`;
30
+ }
31
+ return `${prefix}${String(err)}`;
32
+ }
33
+ async function apiRequest(path, options = {}) {
34
+ const config = getConfig();
35
+ const token = getToken();
36
+ if (!token) {
37
+ throw new Error("Not authenticated. Run `specra login` first.");
38
+ }
39
+ const url = `${config.apiUrl}${path}`;
40
+ const res = await fetch(url, {
41
+ ...options,
42
+ headers: {
43
+ Authorization: `Bearer ${token}`,
44
+ ...options.headers
45
+ }
46
+ });
47
+ if (!res.ok) {
48
+ let message;
49
+ try {
50
+ const data = await res.json();
51
+ message = data.error || res.statusText;
52
+ } catch {
53
+ message = res.statusText;
54
+ }
55
+ throw new ApiError(res.status, message);
56
+ }
57
+ return res.json();
58
+ }
59
+ async function apiUpload(path, body, headers = {}) {
60
+ const config = getConfig();
61
+ const token = getToken();
62
+ if (!token) {
63
+ throw new Error("Not authenticated. Run `specra login` first.");
64
+ }
65
+ const url = `${config.apiUrl}${path}`;
66
+ const res = await fetch(url, {
67
+ method: "POST",
68
+ headers: {
69
+ Authorization: `Bearer ${token}`,
70
+ "Content-Type": "application/octet-stream",
71
+ ...headers
72
+ },
73
+ body
74
+ });
75
+ if (!res.ok) {
76
+ let message;
77
+ try {
78
+ const data = await res.json();
79
+ message = data.error || res.statusText;
80
+ } catch {
81
+ message = res.statusText;
82
+ }
83
+ throw new ApiError(res.status, message);
84
+ }
85
+ return res.json();
86
+ }
87
+
88
+ export {
89
+ ApiError,
90
+ formatError,
91
+ apiRequest,
92
+ apiUpload
93
+ };
94
+ //# sourceMappingURL=chunk-72RDEJR2.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/api-client.ts"],"sourcesContent":["import { getConfig, getToken } from './config.js'\n\nexport class ApiError extends Error {\n constructor(public status: number, message: string) {\n super(message)\n this.name = 'ApiError'\n }\n}\n\nconst STATUS_HINTS: Record<number, string> = {\n 401: 'Try running `specra login` to re-authenticate.',\n 403: 'You may not have permission for this action. Check your plan or project access.',\n 502: 'The server is temporarily unavailable. Try again in a moment.',\n 503: 'The server is temporarily unavailable. Try again in a moment.',\n}\n\n/** Format an error for CLI display (no stack traces). */\nexport function formatError(context: string, err: unknown): string {\n const prefix = context ? `${context}: ` : ''\n if (err instanceof ApiError) {\n const hint = STATUS_HINTS[err.status] || ''\n return `${prefix}${err.message} (${err.status})${hint ? `\\n ${hint}` : ''}`\n }\n if (err instanceof Error) {\n return `${prefix}${err.message}`\n }\n return `${prefix}${String(err)}`\n}\n\nexport async function apiRequest<T = unknown>(\n path: string,\n options: RequestInit = {}\n): Promise<T> {\n const config = getConfig()\n const token = getToken()\n\n if (!token) {\n throw new Error('Not authenticated. Run `specra login` first.')\n }\n\n const url = `${config.apiUrl}${path}`\n const res = await fetch(url, {\n ...options,\n headers: {\n Authorization: `Bearer ${token}`,\n ...options.headers,\n },\n })\n\n if (!res.ok) {\n let message: string\n try {\n const data = (await res.json()) as Record<string, string>\n message = data.error || res.statusText\n } catch {\n message = res.statusText\n }\n throw new ApiError(res.status, message)\n }\n\n return res.json() as Promise<T>\n}\n\nexport async function apiUpload(\n path: string,\n body: Buffer | ReadableStream,\n headers: Record<string, string> = {}\n): Promise<unknown> {\n const config = getConfig()\n const token = getToken()\n\n if (!token) {\n throw new Error('Not authenticated. Run `specra login` first.')\n }\n\n const url = `${config.apiUrl}${path}`\n const res = await fetch(url, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${token}`,\n 'Content-Type': 'application/octet-stream',\n ...headers,\n },\n body,\n })\n\n if (!res.ok) {\n let message: string\n try {\n const data = (await res.json()) as Record<string, string>\n message = data.error || res.statusText\n } catch {\n message = res.statusText\n }\n throw new ApiError(res.status, message)\n }\n\n return res.json()\n}\n"],"mappings":";;;;;;;AAEO,IAAM,WAAN,cAAuB,MAAM;AAAA,EAClC,YAAmB,QAAgB,SAAiB;AAClD,UAAM,OAAO;AADI;AAEjB,SAAK,OAAO;AAAA,EACd;AACF;AAEA,IAAM,eAAuC;AAAA,EAC3C,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AACP;AAGO,SAAS,YAAY,SAAiB,KAAsB;AACjE,QAAM,SAAS,UAAU,GAAG,OAAO,OAAO;AAC1C,MAAI,eAAe,UAAU;AAC3B,UAAM,OAAO,aAAa,IAAI,MAAM,KAAK;AACzC,WAAO,GAAG,MAAM,GAAG,IAAI,OAAO,KAAK,IAAI,MAAM,IAAI,OAAO;AAAA,IAAO,IAAI,KAAK,EAAE;AAAA,EAC5E;AACA,MAAI,eAAe,OAAO;AACxB,WAAO,GAAG,MAAM,GAAG,IAAI,OAAO;AAAA,EAChC;AACA,SAAO,GAAG,MAAM,GAAG,OAAO,GAAG,CAAC;AAChC;AAEA,eAAsB,WACpB,MACA,UAAuB,CAAC,GACZ;AACZ,QAAM,SAAS,UAAU;AACzB,QAAM,QAAQ,SAAS;AAEvB,MAAI,CAAC,OAAO;AACV,UAAM,IAAI,MAAM,8CAA8C;AAAA,EAChE;AAEA,QAAM,MAAM,GAAG,OAAO,MAAM,GAAG,IAAI;AACnC,QAAM,MAAM,MAAM,MAAM,KAAK;AAAA,IAC3B,GAAG;AAAA,IACH,SAAS;AAAA,MACP,eAAe,UAAU,KAAK;AAAA,MAC9B,GAAG,QAAQ;AAAA,IACb;AAAA,EACF,CAAC;AAED,MAAI,CAAC,IAAI,IAAI;AACX,QAAI;AACJ,QAAI;AACF,YAAM,OAAQ,MAAM,IAAI,KAAK;AAC7B,gBAAU,KAAK,SAAS,IAAI;AAAA,IAC9B,QAAQ;AACN,gBAAU,IAAI;AAAA,IAChB;AACA,UAAM,IAAI,SAAS,IAAI,QAAQ,OAAO;AAAA,EACxC;AAEA,SAAO,IAAI,KAAK;AAClB;AAEA,eAAsB,UACpB,MACA,MACA,UAAkC,CAAC,GACjB;AAClB,QAAM,SAAS,UAAU;AACzB,QAAM,QAAQ,SAAS;AAEvB,MAAI,CAAC,OAAO;AACV,UAAM,IAAI,MAAM,8CAA8C;AAAA,EAChE;AAEA,QAAM,MAAM,GAAG,OAAO,MAAM,GAAG,IAAI;AACnC,QAAM,MAAM,MAAM,MAAM,KAAK;AAAA,IAC3B,QAAQ;AAAA,IACR,SAAS;AAAA,MACP,eAAe,UAAU,KAAK;AAAA,MAC9B,gBAAgB;AAAA,MAChB,GAAG;AAAA,IACL;AAAA,IACA;AAAA,EACF,CAAC;AAED,MAAI,CAAC,IAAI,IAAI;AACX,QAAI;AACJ,QAAI;AACF,YAAM,OAAQ,MAAM,IAAI,KAAK;AAC7B,gBAAU,KAAK,SAAS,IAAI;AAAA,IAC9B,QAAQ;AACN,gBAAU,IAAI;AAAA,IAChB;AACA,UAAM,IAAI,SAAS,IAAI,QAAQ,OAAO;AAAA,EACxC;AAEA,SAAO,IAAI,KAAK;AAClB;","names":[]}