asphodelos 0.0.1

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 - present, Asphodelos Contributors
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
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,593 @@
1
+ # Asphodelos
2
+
3
+ ![img](https://raw.githubusercontent.com/nakita628/asphodelos/refs/heads/main/assets/icon/asphodelos.png)
4
+
5
+ **[Asphodelos](https://www.npmjs.com/package/asphodelos)** generates type-safe [Elysia](https://elysiajs.com/) code from [OpenAPI](https://www.openapis.org/) / [TypeSpec](https://typespec.io/) specifications.
6
+
7
+ - OpenAPI schemas to [TypeBox](https://github.com/sinclairzx81/typebox) schemas (via Elysia's `t`)
8
+ - Elysia routes with per-route validation
9
+ - App entry point + per-resource modules (controller / service / model)
10
+ - TypeBox component bundles (schemas, responses, parameters, …)
11
+ - Eden Treaty wrappers and a self-contained `App` type
12
+ - Client library hooks (SWR, TanStack Query, Preact Query, Solid Query, Vue Query, Svelte Query, Angular Query)
13
+ - `bun:test` tests and a mock server
14
+
15
+ Asphodelos targets the [Bun](https://bun.sh/) runtime.
16
+
17
+ ## Quick Start
18
+
19
+ ### Installation
20
+
21
+ ```bash
22
+ bun add -D asphodelos
23
+ ```
24
+
25
+ ### CLI
26
+
27
+ ```bash
28
+ bunx asphodelos path/to/input.{yaml,json,tsp} -o path/to/output.ts
29
+ ```
30
+
31
+ ### Configuration File
32
+
33
+ Create `asphodelos.config.ts`:
34
+
35
+ ```ts
36
+ import { defineConfig } from 'asphodelos'
37
+
38
+ export default defineConfig({
39
+ input: 'openapi.yaml',
40
+ output: 'src/index.ts', // default
41
+ })
42
+ ```
43
+
44
+ ```bash
45
+ bunx asphodelos
46
+ ```
47
+
48
+ ### CLI Reference
49
+
50
+ `asphodelos --help`:
51
+
52
+ ```text
53
+ DESCRIPTION
54
+ Generate Elysia code from OpenAPI or TypeSpec
55
+
56
+ USAGE
57
+ asphodelos [flags] [<input>]
58
+
59
+ ARGUMENTS
60
+ input input.{yaml,json,tsp} OpenAPI (.yaml, .json) or TypeSpec (.tsp) document to generate from (optional)
61
+
62
+ FLAGS
63
+ --output, -o output.ts TypeScript file the generated app is written to
64
+ --config, -c file Config file to run (default: ./asphodelos.config.ts)
65
+ --watch, -w Rerun the config on every change to its documents or itself
66
+
67
+ GLOBAL FLAGS
68
+ --help, -h Show help information
69
+ --version, -v Show version information
70
+ --wizard Start wizard mode for a command
71
+ --completions <bash|zsh|fish|sh> Print shell completion script (choices: bash, zsh, fish, sh)
72
+ --log-level <all|trace|debug|info|warn|warning|error|fatal|none> Sets the minimum log level (choices: all, trace, debug, info, warn, warning, error, fatal, none)
73
+
74
+ EXAMPLES
75
+ # Generate a single app from one document
76
+ asphodelos openapi.yaml -o src/index.ts
77
+
78
+ # Run every generator declared in ./asphodelos.config.ts
79
+ asphodelos
80
+
81
+ # Run a config file from another location
82
+ asphodelos --config config/api.config.ts
83
+
84
+ # Rerun on every change to the input documents or the config
85
+ asphodelos --watch
86
+ ```
87
+
88
+ With an `<input>` the CLI generates one app and ignores any config file. With no `<input>` it
89
+ runs the config file.
90
+
91
+ ### Watch Mode
92
+
93
+ ```bash
94
+ bunx asphodelos --watch
95
+ ```
96
+
97
+ Reruns the config on every change to the input documents or to the config itself, and keeps
98
+ watching when a run fails. It cannot be combined with `<input>` / `--output`.
99
+
100
+ ### Example
101
+
102
+ input:
103
+
104
+ ```yaml
105
+ openapi: 3.1.0
106
+ info:
107
+ title: Asphodelos API
108
+ version: '1.0.0'
109
+ paths:
110
+ /elysia:
111
+ get:
112
+ summary: Welcome
113
+ operationId: welcome
114
+ responses:
115
+ '200':
116
+ description: OK
117
+ content:
118
+ application/json:
119
+ schema:
120
+ type: object
121
+ required: [message]
122
+ properties:
123
+ message:
124
+ type: string
125
+ ```
126
+
127
+ output:
128
+
129
+ ```text
130
+ src/
131
+ ├── index.ts
132
+ └── modules/
133
+ └── elysia/
134
+ ├── index.ts // controller
135
+ ├── service.ts // abstract class for business logic
136
+ └── model.ts // TypeBox models
137
+ ```
138
+
139
+ ```ts
140
+ // src/index.ts
141
+ import { Elysia } from 'elysia'
142
+ import { elysia } from './modules/elysia'
143
+
144
+ export const app = new Elysia().use(elysia)
145
+
146
+ if (import.meta.main) {
147
+ app.listen(3000)
148
+ console.log(`🦊 Elysia is running at ${app.server?.hostname}:${app.server?.port}`)
149
+ }
150
+ ```
151
+
152
+ ```ts
153
+ // src/modules/elysia/index.ts
154
+ import { Elysia } from 'elysia'
155
+ import { ElysiaModel } from './model'
156
+
157
+ export const elysia = new Elysia().get('/elysia', () => {}, {
158
+ response: { 200: ElysiaModel.welcomeResponse200 },
159
+ detail: { tags: [], summary: 'Welcome', operationId: 'welcome' },
160
+ })
161
+ ```
162
+
163
+ ```ts
164
+ // src/modules/elysia/model.ts
165
+ import { t, type UnwrapSchema } from 'elysia'
166
+
167
+ export const ElysiaModel = { welcomeResponse200: t.Object({ message: t.String() }) } as const
168
+
169
+ export type ElysiaModel = { [k in keyof typeof ElysiaModel]: UnwrapSchema<(typeof ElysiaModel)[k]> }
170
+ ```
171
+
172
+ Handlers are empty stubs: TypeScript flags each one whose response is non-void until you
173
+ implement it. Importing `app` never starts a server.
174
+
175
+ ```bash
176
+ bun add elysia
177
+ bun run src/index.ts
178
+ ```
179
+
180
+ ## Vite Plugin
181
+
182
+ Watches your OpenAPI spec and `asphodelos.config.ts` for changes, then auto-regenerates code on save.
183
+
184
+ Requires `asphodelos.config.ts` in your project root.
185
+
186
+ ```ts
187
+ // vite.config.ts
188
+ import { asphodelosVite } from 'asphodelos/vite-plugin'
189
+ import { defineConfig } from 'vite'
190
+
191
+ export default defineConfig({
192
+ plugins: [asphodelosVite()],
193
+ })
194
+ ```
195
+
196
+ - **What it watches**: `asphodelos.config.ts`, and every `.yaml` / `.json` / `.tsp` in the
197
+ directory of `input` — a `$ref` or a TypeSpec import can reach a sibling file.
198
+ - **When it regenerates**: a config save always regenerates. A document save regenerates only when
199
+ the documents' contents changed, or a generated file has gone missing; a save with nothing new in
200
+ it is skipped.
201
+ - **When the browser reloads**: only when a generated file actually changed.
202
+ - **Cleanup**: an output that the config or the document no longer produces is removed. The app
203
+ entry, `modules/` and the generated tests are never removed, because they hold your code.
204
+ - A config that fails to load is reported and the previous one stays in effect; the next save
205
+ retries, so a typo never needs a restart. Every run is queued, so two never overlap.
206
+
207
+ ## Eden Treaty Integration
208
+
209
+ ### Type-Only Distribution
210
+
211
+ Emit a self-contained `export type App` so [Eden](https://elysiajs.com/eden/treaty/overview.html)
212
+ clients can call `treaty<App>(...)` without importing the runtime app.
213
+
214
+ ```ts
215
+ export default defineConfig({
216
+ input: 'openapi.yaml',
217
+ types: { output: 'src/types.ts' },
218
+ })
219
+ ```
220
+
221
+ ### Wrapper Functions
222
+
223
+ Generate one wrapper per operation over a Treaty client you supply.
224
+
225
+ ```ts
226
+ export default defineConfig({
227
+ input: 'openapi.yaml',
228
+ eden: {
229
+ output: 'src/eden.ts',
230
+ import: './lib', // module exporting `client` = treaty<App>(...)
231
+ client: 'client',
232
+ docs: true, // JSDoc above each wrapper
233
+ },
234
+ })
235
+ ```
236
+
237
+ ## Client Library Integrations
238
+
239
+ Supported: SWR, TanStack Query, Preact Query, Solid Query, Vue Query, Svelte Query, Angular Query.
240
+
241
+ ```ts
242
+ export default defineConfig({
243
+ input: 'openapi.yaml',
244
+ 'tanstack-query': {
245
+ output: './src/tanstack-query',
246
+ import: '../lib',
247
+ split: true,
248
+ client: 'client',
249
+ },
250
+ })
251
+ ```
252
+
253
+ TanStack-family mutations also get a `<operation>MutationOptions()` factory, and SWR queries a
254
+ `useImmutable<Operation>` hook. The generated hooks are compiled and run against the real
255
+ libraries in [`test/`](test/README.md).
256
+
257
+ ### Infinite Query (`x-pagination`)
258
+
259
+ Set `x-pagination: true` on a GET operation to generate infinite query hooks.
260
+
261
+ ```yaml
262
+ paths:
263
+ /items:
264
+ get:
265
+ x-pagination: true
266
+ ```
267
+
268
+ The paging rules go in a `pagination` argument:
269
+
270
+ ```ts
271
+ const items = useListItemsInfinite(undefined, {
272
+ initialPageParam: 0,
273
+ getNextPageParam: (lastPage) => lastPage.nextPage,
274
+ buildInit: (pageParam) => ({ query: { page: String(pageParam) } }),
275
+ })
276
+ ```
277
+
278
+ Vue Query takes only `buildInit` there, with `initialPageParam` / `getNextPageParam` in the third
279
+ argument. SWR takes `buildInit(pageIndex, previousPage)` and stops when it returns `null`.
280
+
281
+ ## Test & Mock Generation
282
+
283
+ ### Test Generation
284
+
285
+ Generates `bun:test` tests that call `app.handle(...)` on the real app: a success-status test per
286
+ operation, plus `401` / `404` tests when the spec declares them. They start red against the empty
287
+ handlers; re-running keeps your hand-written tests.
288
+
289
+ ```ts
290
+ export default defineConfig({
291
+ input: 'openapi.yaml',
292
+ test: {
293
+ output: 'src/app.test.ts', // or `split: true` for modules/<resource>/index.test.ts
294
+ pathAlias: '@/', // optional: import the app through a tsconfig alias
295
+ },
296
+ })
297
+ ```
298
+
299
+ ### Mock Server Generation
300
+
301
+ Generates a standalone Elysia server that answers every operation with a
302
+ [`@faker-js/faker`](https://fakerjs.dev/) mock of its success response. Secured operations that
303
+ declare a `401` answer it when the credential is missing, and path parameters answer a declared
304
+ `404` for the same sentinel values the generated tests send.
305
+
306
+ ```ts
307
+ export default defineConfig({
308
+ input: 'openapi.yaml',
309
+ mock: {
310
+ output: 'src/mock.ts',
311
+ delay: { min: 50, max: 200 },
312
+ locale: 'ja',
313
+ },
314
+ })
315
+ ```
316
+
317
+ Like [Prism](https://stoplight.io/open-source/prism), a request can pick any response or named
318
+ example the document declares with the `Prefer` header (or the `__code` / `__example` query):
319
+
320
+ ```bash
321
+ curl -H 'Prefer: code=404' http://localhost:3000/orders/1 # the 404 response
322
+ curl -H 'Prefer: example=pending' http://localhost:3000/orders/1 # a named example
323
+ curl -H 'Prefer: code=404, example=gone' http://localhost:3000/orders/1
324
+ curl 'http://localhost:3000/orders/1?__code=503'
325
+ ```
326
+
327
+ A code falls back to its `4XX` range, then `default`. A code or example the operation does not
328
+ declare answers `500` with an `application/problem+json` body saying what is missing.
329
+
330
+ ## Full Config Reference
331
+
332
+ With `split: true`, `output` is a directory (one file per entry + `index.ts` barrel); otherwise it
333
+ is a single `.ts` file. `components.output` and the per-type components are mutually exclusive.
334
+
335
+ A split directory belongs to the generator: every run empties its `.ts` files before refilling it,
336
+ so an entry that leaves the document does not leave an orphaned file behind. Subdirectories, other
337
+ files and the single-file outputs of other generators are left alone.
338
+
339
+ ```ts
340
+ import { defineConfig } from 'asphodelos'
341
+
342
+ export default defineConfig({
343
+ input: 'openapi.yaml',
344
+
345
+ output: 'src/index.ts', // app entry; its directory holds modules/ and components/
346
+ prefix: '/api/v3', // new Elysia({ prefix })
347
+ port: '3000',
348
+ integration: false, // true: no .listen(), a host framework owns the server
349
+ pathAlias: false, // true: `@/` imports between generated files
350
+ readonly: false, // wrap top-level schemas in t.Readonly(...)
351
+ // format: {}, // oxfmt FormatConfig
352
+
353
+ // `exportTypes` adds `Static<typeof XSchema>` aliases.
354
+ components: {
355
+ // output: 'src/components.ts', // single-file mode
356
+
357
+ schemas: {
358
+ output: 'src/components/schemas',
359
+ split: true,
360
+ import: '../schemas',
361
+ exportTypes: true,
362
+ },
363
+ responses: {
364
+ output: 'src/components/responses',
365
+ split: true,
366
+ import: '../responses',
367
+ exportTypes: true,
368
+ },
369
+ parameters: {
370
+ output: 'src/components/parameters',
371
+ split: true,
372
+ import: '../parameters',
373
+ exportTypes: true,
374
+ },
375
+ requestBodies: {
376
+ output: 'src/components/requestBodies',
377
+ split: true,
378
+ import: '../requestBodies',
379
+ exportTypes: true,
380
+ },
381
+ headers: {
382
+ output: 'src/components/headers',
383
+ split: true,
384
+ import: '../headers',
385
+ exportTypes: true,
386
+ },
387
+ mediaTypes: {
388
+ output: 'src/components/mediaTypes',
389
+ split: true,
390
+ import: '../mediaTypes',
391
+ exportTypes: true,
392
+ },
393
+ examples: {
394
+ output: 'src/components/examples',
395
+ split: true,
396
+ import: '../examples',
397
+ },
398
+ securitySchemes: {
399
+ output: 'src/components/securitySchemes',
400
+ split: true,
401
+ import: '../securitySchemes',
402
+ },
403
+ links: {
404
+ output: 'src/components/links',
405
+ split: true,
406
+ import: '../links',
407
+ },
408
+ callbacks: {
409
+ output: 'src/components/callbacks',
410
+ split: true,
411
+ import: '../callbacks',
412
+ },
413
+ pathItems: {
414
+ output: 'src/components/pathItems',
415
+ split: true,
416
+ import: '../pathItems',
417
+ },
418
+ },
419
+
420
+ types: {
421
+ output: 'src/types.ts',
422
+ },
423
+
424
+ eden: {
425
+ output: 'src/eden.ts',
426
+ import: './lib',
427
+ client: 'client',
428
+ docs: false,
429
+ },
430
+
431
+ test: {
432
+ split: true, // false: a single file at `output`
433
+ pathAlias: '@/',
434
+ },
435
+
436
+ mock: {
437
+ output: 'src/mock.ts',
438
+ useExamples: true, // true: response examples | 'all': also schema/property examples | false
439
+ locale: 'en', // @faker-js/faker/locale/<locale>
440
+ // seed: 42, // optional: same body per route on every request (snapshot-friendly)
441
+ delay: false, // ms, { min, max }, or false
442
+ arrayMin: 1, // array length when the schema sets no minItems / maxItems
443
+ arrayMax: 5,
444
+ },
445
+
446
+ swr: {
447
+ output: 'src/swr',
448
+ import: '../lib',
449
+ split: true,
450
+ client: 'client',
451
+ },
452
+ 'tanstack-query': {
453
+ output: 'src/tanstack-query',
454
+ import: '../lib',
455
+ split: true,
456
+ client: 'client',
457
+ },
458
+ 'preact-query': {
459
+ output: 'src/preact-query',
460
+ import: '../lib',
461
+ split: true,
462
+ client: 'client',
463
+ },
464
+ 'solid-query': {
465
+ output: 'src/solid-query',
466
+ import: '../lib',
467
+ split: true,
468
+ client: 'client',
469
+ },
470
+ 'vue-query': {
471
+ output: 'src/vue-query',
472
+ import: '../lib',
473
+ split: true,
474
+ client: 'client',
475
+ },
476
+ 'svelte-query': {
477
+ output: 'src/svelte-query',
478
+ import: '../lib',
479
+ split: true,
480
+ client: 'client',
481
+ },
482
+ 'angular-query': {
483
+ output: 'src/angular-query',
484
+ import: '../lib',
485
+ split: true,
486
+ client: 'client',
487
+ },
488
+ })
489
+ ```
490
+
491
+ ## Vendor Extensions (x-\*)
492
+
493
+ ### Custom Validation Error Messages
494
+
495
+ Attach a custom error message with `x-<jsonSchemaKeyword>-message`, one extension per keyword.
496
+
497
+ ```yaml
498
+ name:
499
+ type: string
500
+ minLength: 3
501
+ maxLength: 20
502
+ x-error-message: 'name must be a string'
503
+ x-minLength-message: 'name must be at least 3 characters'
504
+ x-maxLength-message: 'name must be at most 20 characters'
505
+ ```
506
+
507
+ | Group | Extensions |
508
+ | ----------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
509
+ | Common | `x-error-message`, `x-required-message`†, `x-const-message`, `x-enum-message` |
510
+ | Numeric | `x-minimum-message`, `x-maximum-message`, `x-exclusiveMinimum-message`, `x-exclusiveMaximum-message`, `x-multipleOf-message` |
511
+ | String | `x-minLength-message`, `x-maxLength-message`, `x-pattern-message`, `x-length-message` |
512
+ | Array | `x-minItems-message`, `x-maxItems-message`, `x-uniqueItems-message`, `x-contains-message`, `x-minContains-message`, `x-maxContains-message`, `x-prefixItems-message`, `x-items-message` |
513
+ | Object | `x-minProperties-message`, `x-maxProperties-message`, `x-additionalProperties-message`†, `x-propertyNames-message`, `x-patternProperties-message`†, `x-dependentRequired-message`, `x-dependentSchemas-message`, `x-properties-message`†, `x-unevaluatedProperties-message`†, `x-unevaluatedItems-message`† |
514
+ | Combinators | `x-allOf-message`‡, `x-anyOf-message`, `x-oneOf-message`, `x-not-message`, `x-implication-message` |
515
+ | Conditional | `x-if-message`†, `x-then-message`†, `x-else-message`† |
516
+
517
+ - † Kept on the schema, but Elysia 1.4 cannot route a 422 to it.
518
+ - ‡ Best-effort: a sibling error is usually reported first.
519
+
520
+ ### Behavior Extensions
521
+
522
+ | Extension | Effect |
523
+ | -------------------------------------------- | ------------------------------------------------------------------ |
524
+ | `x-trim` | `t.Transform` that trims the string |
525
+ | `x-toLowerCase` / `x-lowercase` | `t.Transform` that lower-cases the string |
526
+ | `x-toUpperCase` / `x-uppercase` | `t.Transform` that upper-cases the string |
527
+ | `x-normalize` | `t.Transform` with `normalize('NFC' \| 'NFD' \| 'NFKC' \| 'NFKD')` |
528
+ | `x-startsWith` / `x-endsWith` / `x-includes` | Folded into `pattern` |
529
+ | `x-emailRegex` | Replaces `pattern` on `format: email` |
530
+ | `x-readonly` | `t.Readonly(...)` |
531
+ | `x-brand` | `t.Unsafe<T & { readonly __brand: 'Name' }>(...)` on primitives |
532
+ | `x-transform` | Replaces the schema with a raw `t.Transform(...)` expression |
533
+
534
+ String transforms run **after** validation, so write `minLength` / `pattern` against the value as
535
+ it arrives, not as it looks after trimming or case-folding.
536
+
537
+ ```yaml
538
+ UpdatedAt:
539
+ type: string
540
+ format: date-time
541
+ x-transform: >-
542
+ t.Transform(t.String()).Decode((v) => new Date(v)).Encode((v) => v.toISOString())
543
+ ```
544
+
545
+ > **⚠️ Security:** `x-transform` is emitted verbatim and runs when the generated module is
546
+ > imported. Only generate from specs you author or fully trust.
547
+
548
+ `Decode` runs on requests and `Encode` on responses (only when the response declares a schema).
549
+ Keep query / path / header transforms on a `string` base. Eden types the decoded value, but the
550
+ wire carries the encoded one.
551
+
552
+ `x-uuidVersion`, `x-urlHostname`, `x-urlProtocol`, `x-urlNormalize`, `x-isoPrecision`,
553
+ `x-isoOffset`, `x-isoLocal`, `x-macDelimiter`, `x-jwtAlg`, `x-hashAlg` and `x-hashEnc` are
554
+ round-tripped through OpenAPI but emit no code.
555
+
556
+ ### Coercion Formats
557
+
558
+ | `format` | Generates |
559
+ | ---------------- | ------------------- |
560
+ | `numeric` | `t.Numeric()` |
561
+ | `boolean-string` | `t.BooleanString()` |
562
+
563
+ Elysia already coerces query / path / header / cookie values and JSON bodies, so these only make
564
+ the intent explicit in a body. Do not add them to parameters.
565
+
566
+ ### Unsupported Extensions
567
+
568
+ `x-refine`, `x-superRefine`, `x-prefault` (use `default`), `x-decode` / `x-encode` (use
569
+ `x-transform`), `x-cookie-secrets`, `x-unionEnum`, `x-composite` and `x-form` are not supported.
570
+
571
+ ## Contributing
572
+
573
+ We welcome feedback and contributions!
574
+
575
+ - Open an issue at [GitHub Issues](https://github.com/nakita628/asphodelos/issues)
576
+ - Submit a pull request with your improvements
577
+
578
+ Lint and tests run from the repository root:
579
+
580
+ ```bash
581
+ bun run check # format check, lint, type check, tests, then the client suite
582
+ bun run fix # autofix formatting, oxlint, markdownlint and textlint
583
+ bun run lint # oxlint, markdownlint, textlint, cspell, secretlint, actionlint
584
+ bun run test # unit tests
585
+ bun run test:clients # build, then compile and run the generated hooks under test/
586
+ bun run test:pack # build, pack, and install the tarball with npm into an empty project
587
+ ```
588
+
589
+ Coding agents: start with [AGENTS.md](AGENTS.md).
590
+
591
+ ## License
592
+
593
+ Distributed under the MIT License. See [LICENSE](https://github.com/nakita628/asphodelos?tab=MIT-1-ov-file) for more information.
package/dist/cli.d.mts ADDED
@@ -0,0 +1 @@
1
+ export { };