incur 0.1.17 → 0.2.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 +86 -0
- package/SKILL.md +42 -0
- package/dist/Cli.d.ts +23 -2
- package/dist/Cli.d.ts.map +1 -1
- package/dist/Cli.js +222 -22
- package/dist/Cli.js.map +1 -1
- package/dist/Fetch.d.ts +26 -0
- package/dist/Fetch.d.ts.map +1 -0
- package/dist/Fetch.js +150 -0
- package/dist/Fetch.js.map +1 -0
- package/dist/Openapi.d.ts +20 -0
- package/dist/Openapi.d.ts.map +1 -0
- package/dist/Openapi.js +136 -0
- package/dist/Openapi.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/package.json +4 -1
- package/src/Cli.test-d.ts +2 -2
- package/src/Cli.test.ts +205 -0
- package/src/Cli.ts +258 -23
- package/src/Fetch.test.ts +274 -0
- package/src/Fetch.ts +170 -0
- package/src/Openapi.test.ts +320 -0
- package/src/Openapi.ts +196 -0
- package/src/e2e.test.ts +175 -0
- package/src/index.ts +2 -0
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,KAAK,GAAG,MAAM,UAAU,CAAA;AAC/B,OAAO,KAAK,WAAW,MAAM,kBAAkB,CAAA;AAC/C,OAAO,EAAE,OAAO,IAAI,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAEvD,OAAO,KAAK,MAAM,MAAM,aAAa,CAAA;AACrC,OAAO,KAAK,SAAS,MAAM,gBAAgB,CAAA;AAC3C,OAAO,KAAK,IAAI,MAAM,WAAW,CAAA;AACjC,OAAO,KAAK,GAAG,MAAM,UAAU,CAAA;AAC/B,OAAO,KAAK,MAAM,MAAM,aAAa,CAAA;AAErC,OAAO,KAAK,MAAM,MAAM,aAAa,CAAA;AACrC,OAAO,KAAK,KAAK,MAAM,YAAY,CAAA;AACnC,OAAO,KAAK,QAAQ,MAAM,eAAe,CAAA;AACzC,OAAO,KAAK,OAAO,MAAM,cAAc,CAAA;AACvC,OAAO,KAAK,UAAU,MAAM,iBAAiB,CAAA;AAC7C,OAAO,KAAK,OAAO,MAAM,cAAc,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,KAAK,GAAG,MAAM,UAAU,CAAA;AAC/B,OAAO,KAAK,WAAW,MAAM,kBAAkB,CAAA;AAC/C,OAAO,EAAE,OAAO,IAAI,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAEvD,OAAO,KAAK,MAAM,MAAM,aAAa,CAAA;AACrC,OAAO,KAAK,SAAS,MAAM,gBAAgB,CAAA;AAC3C,OAAO,KAAK,IAAI,MAAM,WAAW,CAAA;AACjC,OAAO,KAAK,GAAG,MAAM,UAAU,CAAA;AAC/B,OAAO,KAAK,KAAK,MAAM,YAAY,CAAA;AACnC,OAAO,KAAK,OAAO,MAAM,cAAc,CAAA;AACvC,OAAO,KAAK,MAAM,MAAM,aAAa,CAAA;AAErC,OAAO,KAAK,MAAM,MAAM,aAAa,CAAA;AACrC,OAAO,KAAK,KAAK,MAAM,YAAY,CAAA;AACnC,OAAO,KAAK,QAAQ,MAAM,eAAe,CAAA;AACzC,OAAO,KAAK,OAAO,MAAM,cAAc,CAAA;AACvC,OAAO,KAAK,UAAU,MAAM,iBAAiB,CAAA;AAC7C,OAAO,KAAK,OAAO,MAAM,cAAc,CAAA"}
|
package/package.json
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"devDependencies": {
|
|
3
3
|
"@changesets/cli": "latest",
|
|
4
|
+
"@hono/zod-openapi": "^1.2.2",
|
|
4
5
|
"@types/node": "latest",
|
|
5
6
|
"@vitest/coverage-v8": "^4.0.18",
|
|
6
7
|
"bun": "^1.3.10",
|
|
8
|
+
"hono": "^4.12.5",
|
|
7
9
|
"oxfmt": "^0.35.0",
|
|
8
10
|
"oxlint": "^1.50.0",
|
|
9
11
|
"tsx": "^4.21.0",
|
|
@@ -14,7 +16,7 @@
|
|
|
14
16
|
"[!start-pkg]": "",
|
|
15
17
|
"name": "incur",
|
|
16
18
|
"type": "module",
|
|
17
|
-
"version": "0.
|
|
19
|
+
"version": "0.2.0",
|
|
18
20
|
"license": "MIT",
|
|
19
21
|
"repository": {
|
|
20
22
|
"type": "git",
|
|
@@ -28,6 +30,7 @@
|
|
|
28
30
|
],
|
|
29
31
|
"dependencies": {
|
|
30
32
|
"@modelcontextprotocol/sdk": "^1.27.1",
|
|
33
|
+
"@readme/openapi-parser": "^6.0.0",
|
|
31
34
|
"@toon-format/toon": "^2.1.0",
|
|
32
35
|
"yaml": "^2.8.2",
|
|
33
36
|
"zod": "^4.3.6"
|
package/src/Cli.test-d.ts
CHANGED
|
@@ -47,9 +47,9 @@ test('output constrains run return type', () => {
|
|
|
47
47
|
},
|
|
48
48
|
})
|
|
49
49
|
|
|
50
|
+
// @ts-expect-error — return doesn't match output schema
|
|
50
51
|
cli.command('greet', {
|
|
51
52
|
output: z.object({ message: z.string() }),
|
|
52
|
-
// @ts-expect-error — return doesn't match output schema
|
|
53
53
|
run() {
|
|
54
54
|
return { wrong: 123 }
|
|
55
55
|
},
|
|
@@ -64,9 +64,9 @@ test('alias keys are constrained to option keys', () => {
|
|
|
64
64
|
run: () => ({}),
|
|
65
65
|
})
|
|
66
66
|
|
|
67
|
+
// @ts-expect-error — 'foo' is not an option key
|
|
67
68
|
cli.command('list', {
|
|
68
69
|
options: z.object({ state: z.string() }),
|
|
69
|
-
// @ts-expect-error — 'foo' is not an option key
|
|
70
70
|
alias: { foo: 'f' },
|
|
71
71
|
run: () => ({}),
|
|
72
72
|
})
|
package/src/Cli.test.ts
CHANGED
|
@@ -2435,3 +2435,208 @@ test('--llms includes hint in skill output', async () => {
|
|
|
2435
2435
|
const { output } = await serve(cli, ['--llms'])
|
|
2436
2436
|
expect(output).toContain('Always confirm before deploying to production')
|
|
2437
2437
|
})
|
|
2438
|
+
|
|
2439
|
+
describe('fetch', async () => {
|
|
2440
|
+
const { app } = await import('../test/fixtures/hono-api.js')
|
|
2441
|
+
|
|
2442
|
+
test('command with fetch: GET /users', async () => {
|
|
2443
|
+
const cli = Cli.create('test', { description: 'test' }).command('api', {
|
|
2444
|
+
description: 'Hono API',
|
|
2445
|
+
fetch: app.fetch,
|
|
2446
|
+
})
|
|
2447
|
+
const { output } = await serve(cli, ['api', 'users'])
|
|
2448
|
+
expect(output).toMatchInlineSnapshot(`
|
|
2449
|
+
"users[1]{id,name}:
|
|
2450
|
+
1,Alice
|
|
2451
|
+
limit: 10
|
|
2452
|
+
"
|
|
2453
|
+
`)
|
|
2454
|
+
})
|
|
2455
|
+
|
|
2456
|
+
test('GET with query params', async () => {
|
|
2457
|
+
const cli = Cli.create('test', { description: 'test' }).command('api', {
|
|
2458
|
+
fetch: app.fetch,
|
|
2459
|
+
})
|
|
2460
|
+
const { output } = await serve(cli, ['api', 'users', '--limit', '5'])
|
|
2461
|
+
expect(output).toMatchInlineSnapshot(`
|
|
2462
|
+
"users[1]{id,name}:
|
|
2463
|
+
1,Alice
|
|
2464
|
+
limit: 5
|
|
2465
|
+
"
|
|
2466
|
+
`)
|
|
2467
|
+
})
|
|
2468
|
+
|
|
2469
|
+
test('GET /users/:id via path segments', async () => {
|
|
2470
|
+
const cli = Cli.create('test', { description: 'test' }).command('api', {
|
|
2471
|
+
fetch: app.fetch,
|
|
2472
|
+
})
|
|
2473
|
+
const { output } = await serve(cli, ['api', 'users', '42'])
|
|
2474
|
+
expect(output).toMatchInlineSnapshot(`
|
|
2475
|
+
"id: 42
|
|
2476
|
+
name: Alice
|
|
2477
|
+
"
|
|
2478
|
+
`)
|
|
2479
|
+
})
|
|
2480
|
+
|
|
2481
|
+
test('POST with -X and -d', async () => {
|
|
2482
|
+
const cli = Cli.create('test', { description: 'test' }).command('api', {
|
|
2483
|
+
fetch: app.fetch,
|
|
2484
|
+
})
|
|
2485
|
+
const { output } = await serve(cli, [
|
|
2486
|
+
'api',
|
|
2487
|
+
'users',
|
|
2488
|
+
'-X',
|
|
2489
|
+
'POST',
|
|
2490
|
+
'-d',
|
|
2491
|
+
'{"name":"Bob"}',
|
|
2492
|
+
])
|
|
2493
|
+
expect(output).toMatchInlineSnapshot(`
|
|
2494
|
+
"created: true
|
|
2495
|
+
name: Bob
|
|
2496
|
+
"
|
|
2497
|
+
`)
|
|
2498
|
+
})
|
|
2499
|
+
|
|
2500
|
+
test('implicit POST with --body', async () => {
|
|
2501
|
+
const cli = Cli.create('test', { description: 'test' }).command('api', {
|
|
2502
|
+
fetch: app.fetch,
|
|
2503
|
+
})
|
|
2504
|
+
const { output } = await serve(cli, [
|
|
2505
|
+
'api',
|
|
2506
|
+
'users',
|
|
2507
|
+
'--body',
|
|
2508
|
+
'{"name":"Eve"}',
|
|
2509
|
+
])
|
|
2510
|
+
expect(output).toMatchInlineSnapshot(`
|
|
2511
|
+
"created: true
|
|
2512
|
+
name: Eve
|
|
2513
|
+
"
|
|
2514
|
+
`)
|
|
2515
|
+
})
|
|
2516
|
+
|
|
2517
|
+
test('DELETE with --method', async () => {
|
|
2518
|
+
const cli = Cli.create('test', { description: 'test' }).command('api', {
|
|
2519
|
+
fetch: app.fetch,
|
|
2520
|
+
})
|
|
2521
|
+
const { output } = await serve(cli, [
|
|
2522
|
+
'api',
|
|
2523
|
+
'users',
|
|
2524
|
+
'1',
|
|
2525
|
+
'--method',
|
|
2526
|
+
'DELETE',
|
|
2527
|
+
])
|
|
2528
|
+
expect(output).toMatchInlineSnapshot(`
|
|
2529
|
+
"deleted: true
|
|
2530
|
+
id: 1
|
|
2531
|
+
"
|
|
2532
|
+
`)
|
|
2533
|
+
})
|
|
2534
|
+
|
|
2535
|
+
test('error response → exit code 1', async () => {
|
|
2536
|
+
const cli = Cli.create('test', { description: 'test' }).command('api', {
|
|
2537
|
+
fetch: app.fetch,
|
|
2538
|
+
})
|
|
2539
|
+
const { exitCode, output } = await serve(cli, ['api', 'error'])
|
|
2540
|
+
expect(exitCode).toBe(1)
|
|
2541
|
+
expect(output).toContain('HTTP_404')
|
|
2542
|
+
})
|
|
2543
|
+
|
|
2544
|
+
test('--format json', async () => {
|
|
2545
|
+
const cli = Cli.create('test', { description: 'test' }).command('api', {
|
|
2546
|
+
fetch: app.fetch,
|
|
2547
|
+
})
|
|
2548
|
+
const { output } = await serve(cli, ['api', 'health', '--format', 'json'])
|
|
2549
|
+
expect(JSON.parse(output)).toEqual({ ok: true })
|
|
2550
|
+
})
|
|
2551
|
+
|
|
2552
|
+
test('--verbose includes request/response meta', async () => {
|
|
2553
|
+
const cli = Cli.create('test', { description: 'test' }).command('api', {
|
|
2554
|
+
fetch: app.fetch,
|
|
2555
|
+
})
|
|
2556
|
+
const { output } = await serve(cli, ['api', 'health', '--verbose', '--format', 'json'])
|
|
2557
|
+
const parsed = JSON.parse(output)
|
|
2558
|
+
expect(parsed.ok).toBe(true)
|
|
2559
|
+
expect(parsed.data).toEqual({ ok: true })
|
|
2560
|
+
expect(parsed.meta.command).toBe('api')
|
|
2561
|
+
})
|
|
2562
|
+
|
|
2563
|
+
test('native + fetch commands coexist', async () => {
|
|
2564
|
+
const cli = Cli.create('test', { description: 'test' })
|
|
2565
|
+
.command('api', { fetch: app.fetch })
|
|
2566
|
+
.command('ping', { run: () => ({ pong: true }) })
|
|
2567
|
+
const { output: fetchOut } = await serve(cli, ['api', 'health'])
|
|
2568
|
+
expect(fetchOut).toContain('ok: true')
|
|
2569
|
+
const { output: nativeOut } = await serve(cli, ['ping'])
|
|
2570
|
+
expect(nativeOut).toContain('pong: true')
|
|
2571
|
+
})
|
|
2572
|
+
|
|
2573
|
+
test('root-level fetch', async () => {
|
|
2574
|
+
const cli = Cli.create('api', { description: 'API', fetch: app.fetch })
|
|
2575
|
+
const { output } = await serve(cli, ['users'])
|
|
2576
|
+
expect(output).toMatchInlineSnapshot(`
|
|
2577
|
+
"users[1]{id,name}:
|
|
2578
|
+
1,Alice
|
|
2579
|
+
limit: 10
|
|
2580
|
+
"
|
|
2581
|
+
`)
|
|
2582
|
+
})
|
|
2583
|
+
|
|
2584
|
+
test('root-level fetch with no args → root path', async () => {
|
|
2585
|
+
const cli = Cli.create('api', { description: 'API', fetch: app.fetch })
|
|
2586
|
+
// Hono returns 404 for / since we don't have a root route
|
|
2587
|
+
const { exitCode } = await serve(cli, [])
|
|
2588
|
+
expect(exitCode).toBe(1)
|
|
2589
|
+
})
|
|
2590
|
+
|
|
2591
|
+
test('--help on fetch command', async () => {
|
|
2592
|
+
const cli = Cli.create('test', { description: 'test' }).command('api', {
|
|
2593
|
+
description: 'Proxy to Hono API',
|
|
2594
|
+
fetch: app.fetch,
|
|
2595
|
+
})
|
|
2596
|
+
const { output } = await serve(cli, ['api', '--help'])
|
|
2597
|
+
expect(output).toContain('Proxy to Hono API')
|
|
2598
|
+
expect(output).toContain('--method')
|
|
2599
|
+
expect(output).toContain('--header')
|
|
2600
|
+
expect(output).toContain('--body')
|
|
2601
|
+
})
|
|
2602
|
+
|
|
2603
|
+
test('text response', async () => {
|
|
2604
|
+
const cli = Cli.create('test', { description: 'test' }).command('api', {
|
|
2605
|
+
fetch: app.fetch,
|
|
2606
|
+
})
|
|
2607
|
+
const { output } = await serve(cli, ['api', 'text'])
|
|
2608
|
+
expect(output).toContain('hello world')
|
|
2609
|
+
})
|
|
2610
|
+
|
|
2611
|
+
test('middleware runs before fetch handler', async () => {
|
|
2612
|
+
let middlewareRan = false
|
|
2613
|
+
const cli = Cli.create('test', { description: 'test' })
|
|
2614
|
+
.use(async (_c, next) => {
|
|
2615
|
+
middlewareRan = true
|
|
2616
|
+
await next()
|
|
2617
|
+
})
|
|
2618
|
+
.command('api', { fetch: app.fetch })
|
|
2619
|
+
await serve(cli, ['api', 'health'])
|
|
2620
|
+
expect(middlewareRan).toBe(true)
|
|
2621
|
+
})
|
|
2622
|
+
|
|
2623
|
+
test('fetch command appears in --llms', async () => {
|
|
2624
|
+
const cli = Cli.create('test', { description: 'test' }).command('api', {
|
|
2625
|
+
description: 'Proxy to API',
|
|
2626
|
+
fetch: app.fetch,
|
|
2627
|
+
})
|
|
2628
|
+
const { output } = await serve(cli, ['--llms'])
|
|
2629
|
+
expect(output).toContain('api')
|
|
2630
|
+
expect(output).toContain('Proxy to API')
|
|
2631
|
+
})
|
|
2632
|
+
|
|
2633
|
+
test('fetch command appears in --help root', async () => {
|
|
2634
|
+
const cli = Cli.create('test', { description: 'test' }).command('api', {
|
|
2635
|
+
description: 'Proxy to API',
|
|
2636
|
+
fetch: app.fetch,
|
|
2637
|
+
})
|
|
2638
|
+
const { output } = await serve(cli, ['--help'])
|
|
2639
|
+
expect(output).toContain('api')
|
|
2640
|
+
expect(output).toContain('Proxy to API')
|
|
2641
|
+
})
|
|
2642
|
+
})
|