@lovrozagar/honey 0.1.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 +51 -0
- package/docs/api-ref/go.md +399 -0
- package/docs/api-ref/python.md +399 -0
- package/docs/api-ref/rust.md +399 -0
- package/docs/api-ref/typescript.md +399 -0
- package/docs/invalidate-spec.md +303 -0
- package/docs/sdk.md +722 -0
- package/package.json +120 -0
- package/src/accepts.ts +78 -0
- package/src/body-limit.ts +100 -0
- package/src/build/index.ts +174 -0
- package/src/cli-go/auth.go +15 -0
- package/src/cli-go/config.go +165 -0
- package/src/cli-go/errors.go +57 -0
- package/src/cli-go/multipart.go +55 -0
- package/src/cli-go/output.go +109 -0
- package/src/cli-go/root.go +30 -0
- package/src/cli-go/stream.go +88 -0
- package/src/cli-go/version.go +5 -0
- package/src/cli.ts +204 -0
- package/src/client/error.ts +27 -0
- package/src/client/http.ts +502 -0
- package/src/client/index.ts +127 -0
- package/src/client/sdk.ts +265 -0
- package/src/client/sse.ts +102 -0
- package/src/client/types.ts +211 -0
- package/src/client/ws.ts +115 -0
- package/src/client-go/errors.go +207 -0
- package/src/client-go/invalidation.go +373 -0
- package/src/client-go/realtime.go +317 -0
- package/src/client-go/result.go +20 -0
- package/src/client-go/runtime.go +413 -0
- package/src/client-go/sse.go +149 -0
- package/src/client-go/transport.go +452 -0
- package/src/client-go/ws.go +104 -0
- package/src/client-mcp/README.md +10 -0
- package/src/client-mcp/server.ts +94 -0
- package/src/client-mcp/types.ts +19 -0
- package/src/client-python/_errors.py +115 -0
- package/src/client-python/_invalidation.py +578 -0
- package/src/client-python/_realtime.py +156 -0
- package/src/client-python/_runtime.py +469 -0
- package/src/client-python/_sse.py +99 -0
- package/src/client-python/_transport.py +335 -0
- package/src/client-python/_ws.py +131 -0
- package/src/client-python/py.typed +0 -0
- package/src/client-rust/errors.rs +200 -0
- package/src/client-rust/invalidation.rs +441 -0
- package/src/client-rust/invalidation_sync.rs +306 -0
- package/src/client-rust/realtime.rs +695 -0
- package/src/client-rust/result.rs +22 -0
- package/src/client-rust/runtime.rs +547 -0
- package/src/client-rust/runtime_sync.rs +452 -0
- package/src/client-rust/sse.rs +167 -0
- package/src/client-rust/ws.rs +143 -0
- package/src/cloudflare/realtime.ts +236 -0
- package/src/codegen-go-cli.ts +1180 -0
- package/src/codegen-go.ts +1010 -0
- package/src/codegen-hash.ts +8 -0
- package/src/codegen-ir.ts +517 -0
- package/src/codegen-loaders.ts +53 -0
- package/src/codegen-mcp.ts +364 -0
- package/src/codegen-python.ts +1028 -0
- package/src/codegen-rust.ts +1652 -0
- package/src/codegen-sanitize.ts +67 -0
- package/src/codegen-schema-naming.ts +146 -0
- package/src/codegen.ts +5015 -0
- package/src/context.ts +210 -0
- package/src/cookie-sign.ts +58 -0
- package/src/cookie.ts +89 -0
- package/src/cors.ts +96 -0
- package/src/crypto.ts +19 -0
- package/src/csrf.ts +54 -0
- package/src/curl-logger.ts +235 -0
- package/src/detect-runtime.ts +19 -0
- package/src/error.ts +52 -0
- package/src/errors.ts +87 -0
- package/src/etag.ts +74 -0
- package/src/feature-load.ts +23 -0
- package/src/go-type-emitter.ts +404 -0
- package/src/honey-response.ts +354 -0
- package/src/i18n-slot.ts +20 -0
- package/src/i18n.ts +234 -0
- package/src/index.ts +3843 -0
- package/src/init.ts +149 -0
- package/src/input.ts +8 -0
- package/src/ip-restrict.ts +192 -0
- package/src/logger.ts +149 -0
- package/src/middleware.ts +199 -0
- package/src/node-request.ts +240 -0
- package/src/node.ts +212 -0
- package/src/openapi/register.ts +19 -0
- package/src/openapi/scalar.ts +33 -0
- package/src/openapi/spec-factory.ts +46 -0
- package/src/openapi/spec.ts +31 -0
- package/src/openapi/swagger.ts +34 -0
- package/src/plugin.ts +771 -0
- package/src/powered-by.ts +17 -0
- package/src/pretty-json.ts +39 -0
- package/src/proxy.ts +155 -0
- package/src/python-type-emitter.ts +144 -0
- package/src/realtime/buffer.ts +101 -0
- package/src/realtime/bus.ts +93 -0
- package/src/realtime/longpoll.ts +148 -0
- package/src/realtime/protocol.ts +128 -0
- package/src/realtime/route.ts +104 -0
- package/src/realtime/runtime.ts +430 -0
- package/src/request-id.ts +23 -0
- package/src/request-to-curl.ts +34 -0
- package/src/response.ts +414 -0
- package/src/rust-type-emitter.ts +412 -0
- package/src/secure-headers.ts +69 -0
- package/src/serve-register.ts +8 -0
- package/src/serve-slot.ts +20 -0
- package/src/serve.ts +141 -0
- package/src/server-timing.ts +66 -0
- package/src/static.ts +60 -0
- package/src/telemetry/otel.ts +139 -0
- package/src/testing.ts +114 -0
- package/src/timeout.ts +36 -0
- package/src/tree.ts +464 -0
- package/src/ts-type-emitter.ts +106 -0
- package/src/type-emitter.ts +524 -0
- package/src/type-extractor.ts +749 -0
- package/src/types.ts +638 -0
- package/src/validation.ts +300 -0
- package/src/ws/bun.ts +89 -0
- package/src/ws/cloudflare.ts +133 -0
- package/src/ws/deno.ts +109 -0
- package/src/ws/node.ts +161 -0
- package/src/yaml.ts +80 -0
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
// Code generated by honey. DO NOT EDIT.
|
|
2
|
+
package cli
|
|
3
|
+
|
|
4
|
+
import (
|
|
5
|
+
"context"
|
|
6
|
+
"errors"
|
|
7
|
+
"net"
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
// ExitFor maps a CLI-level error to a process exit code.
|
|
11
|
+
//
|
|
12
|
+
// Codes:
|
|
13
|
+
//
|
|
14
|
+
// 0 success
|
|
15
|
+
// 1 4xx client error (status < 500)
|
|
16
|
+
// 2 5xx server error (status >= 500)
|
|
17
|
+
// 3 network / transport failure or status == 0
|
|
18
|
+
// 4 bad flags / bad config / missing api key
|
|
19
|
+
// 5 3xx unexpected redirect
|
|
20
|
+
func ExitFor(err error) int {
|
|
21
|
+
if err == nil {
|
|
22
|
+
return 0
|
|
23
|
+
}
|
|
24
|
+
if errors.Is(err, ErrMissingAPIKey) {
|
|
25
|
+
return 4
|
|
26
|
+
}
|
|
27
|
+
if errors.Is(err, context.DeadlineExceeded) || errors.Is(err, context.Canceled) {
|
|
28
|
+
return 3
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
var netErr net.Error
|
|
32
|
+
if errors.As(err, &netErr) {
|
|
33
|
+
return 3
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
var apiErr interface{ StatusCode() int }
|
|
37
|
+
if errors.As(err, &apiErr) {
|
|
38
|
+
return exitForStatus(apiErr.StatusCode())
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return 3
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
func exitForStatus(status int) int {
|
|
45
|
+
switch {
|
|
46
|
+
case status == 0:
|
|
47
|
+
return 3
|
|
48
|
+
case status >= 300 && status < 400:
|
|
49
|
+
return 5
|
|
50
|
+
case status >= 400 && status < 500:
|
|
51
|
+
return 1
|
|
52
|
+
case status >= 500 && status < 600:
|
|
53
|
+
return 2
|
|
54
|
+
default:
|
|
55
|
+
return 1
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
// Code generated by honey. DO NOT EDIT.
|
|
2
|
+
package cli
|
|
3
|
+
|
|
4
|
+
import (
|
|
5
|
+
"bytes"
|
|
6
|
+
"fmt"
|
|
7
|
+
"io"
|
|
8
|
+
"mime/multipart"
|
|
9
|
+
"os"
|
|
10
|
+
"path/filepath"
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
// BuildMultipart assembles a multipart/form-data body.
|
|
14
|
+
//
|
|
15
|
+
// fields are written as plain form fields.
|
|
16
|
+
// files are keyed by form-field-name → absolute-or-relative file path.
|
|
17
|
+
//
|
|
18
|
+
// Returns the body reader, the Content-Type header value (including boundary),
|
|
19
|
+
// and any error.
|
|
20
|
+
func BuildMultipart(fields map[string]string, files map[string]string) (io.Reader, string, error) {
|
|
21
|
+
var buf bytes.Buffer
|
|
22
|
+
writer := multipart.NewWriter(&buf)
|
|
23
|
+
|
|
24
|
+
for name, val := range fields {
|
|
25
|
+
if err := writer.WriteField(name, val); err != nil {
|
|
26
|
+
return nil, "", fmt.Errorf("write field %q: %w", name, err)
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
for name, path := range files {
|
|
31
|
+
if path == "" {
|
|
32
|
+
return nil, "", fmt.Errorf("missing file path for field %q", name)
|
|
33
|
+
}
|
|
34
|
+
file, err := os.Open(path)
|
|
35
|
+
if err != nil {
|
|
36
|
+
return nil, "", fmt.Errorf("open file %s: %w", path, err)
|
|
37
|
+
}
|
|
38
|
+
part, err := writer.CreateFormFile(name, filepath.Base(path))
|
|
39
|
+
if err != nil {
|
|
40
|
+
file.Close()
|
|
41
|
+
return nil, "", fmt.Errorf("create form file %q: %w", name, err)
|
|
42
|
+
}
|
|
43
|
+
if _, err := io.Copy(part, file); err != nil {
|
|
44
|
+
file.Close()
|
|
45
|
+
return nil, "", fmt.Errorf("copy file %s: %w", path, err)
|
|
46
|
+
}
|
|
47
|
+
file.Close()
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if err := writer.Close(); err != nil {
|
|
51
|
+
return nil, "", fmt.Errorf("close multipart writer: %w", err)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return &buf, writer.FormDataContentType(), nil
|
|
55
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
// Code generated by honey. DO NOT EDIT.
|
|
2
|
+
package cli
|
|
3
|
+
|
|
4
|
+
import (
|
|
5
|
+
"encoding/json"
|
|
6
|
+
"fmt"
|
|
7
|
+
"io"
|
|
8
|
+
"sort"
|
|
9
|
+
"strings"
|
|
10
|
+
"text/tabwriter"
|
|
11
|
+
|
|
12
|
+
"sigs.k8s.io/yaml"
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
// Emit writes data to w in the requested mode.
|
|
16
|
+
//
|
|
17
|
+
// Supported modes: json (pretty, 2-space indent), ndjson, yaml, table.
|
|
18
|
+
// Unknown modes return an error that ExitFor maps to exit code 4.
|
|
19
|
+
func Emit(data any, mode string, w io.Writer) error {
|
|
20
|
+
if data == nil {
|
|
21
|
+
return nil
|
|
22
|
+
}
|
|
23
|
+
switch mode {
|
|
24
|
+
case "", "json":
|
|
25
|
+
enc, err := json.MarshalIndent(data, "", " ")
|
|
26
|
+
if err != nil {
|
|
27
|
+
return err
|
|
28
|
+
}
|
|
29
|
+
_, err = fmt.Fprintln(w, string(enc))
|
|
30
|
+
return err
|
|
31
|
+
case "ndjson":
|
|
32
|
+
enc, err := json.Marshal(data)
|
|
33
|
+
if err != nil {
|
|
34
|
+
return err
|
|
35
|
+
}
|
|
36
|
+
_, err = fmt.Fprintln(w, string(enc))
|
|
37
|
+
return err
|
|
38
|
+
case "yaml":
|
|
39
|
+
enc, err := yaml.Marshal(data)
|
|
40
|
+
if err != nil {
|
|
41
|
+
return err
|
|
42
|
+
}
|
|
43
|
+
_, err = w.Write(enc)
|
|
44
|
+
return err
|
|
45
|
+
case "table":
|
|
46
|
+
return printTable(data, w)
|
|
47
|
+
default:
|
|
48
|
+
return fmt.Errorf("unknown output mode %q: valid modes are json, ndjson, yaml, table", mode)
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// printTable renders data as an aligned table when possible, or falls back to raw print.
|
|
53
|
+
func printTable(data any, w io.Writer) error {
|
|
54
|
+
switch v := data.(type) {
|
|
55
|
+
case []any:
|
|
56
|
+
if len(v) == 0 {
|
|
57
|
+
return nil
|
|
58
|
+
}
|
|
59
|
+
rows := make([]map[string]any, 0, len(v))
|
|
60
|
+
for _, row := range v {
|
|
61
|
+
if m, ok := row.(map[string]any); ok {
|
|
62
|
+
rows = append(rows, m)
|
|
63
|
+
} else {
|
|
64
|
+
fmt.Fprintln(w, row)
|
|
65
|
+
return nil
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
keys := collectKeys(rows)
|
|
69
|
+
tw := tabwriter.NewWriter(w, 0, 0, 2, ' ', 0)
|
|
70
|
+
fmt.Fprintln(tw, strings.Join(keys, "\t"))
|
|
71
|
+
for _, row := range rows {
|
|
72
|
+
cols := make([]string, len(keys))
|
|
73
|
+
for i, k := range keys {
|
|
74
|
+
cols[i] = fmt.Sprintf("%v", row[k])
|
|
75
|
+
}
|
|
76
|
+
fmt.Fprintln(tw, strings.Join(cols, "\t"))
|
|
77
|
+
}
|
|
78
|
+
return tw.Flush()
|
|
79
|
+
case map[string]any:
|
|
80
|
+
keys := make([]string, 0, len(v))
|
|
81
|
+
for k := range v {
|
|
82
|
+
keys = append(keys, k)
|
|
83
|
+
}
|
|
84
|
+
sort.Strings(keys)
|
|
85
|
+
tw := tabwriter.NewWriter(w, 0, 0, 2, ' ', 0)
|
|
86
|
+
for _, k := range keys {
|
|
87
|
+
fmt.Fprintf(tw, "%s\t%v\n", k, v[k])
|
|
88
|
+
}
|
|
89
|
+
return tw.Flush()
|
|
90
|
+
default:
|
|
91
|
+
_, err := fmt.Fprintln(w, data)
|
|
92
|
+
return err
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
func collectKeys(rows []map[string]any) []string {
|
|
97
|
+
seen := map[string]bool{}
|
|
98
|
+
keys := []string{}
|
|
99
|
+
for _, row := range rows {
|
|
100
|
+
for k := range row {
|
|
101
|
+
if !seen[k] {
|
|
102
|
+
seen[k] = true
|
|
103
|
+
keys = append(keys, k)
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
sort.Strings(keys)
|
|
108
|
+
return keys
|
|
109
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// Code generated by honey. DO NOT EDIT.
|
|
2
|
+
package cli
|
|
3
|
+
|
|
4
|
+
import (
|
|
5
|
+
"github.com/spf13/cobra"
|
|
6
|
+
)
|
|
7
|
+
|
|
8
|
+
// NewRoot constructs a cobra root command with the standard persistent flags.
|
|
9
|
+
//
|
|
10
|
+
// Flags (all persistent, inherited by subcommands):
|
|
11
|
+
//
|
|
12
|
+
// --api-key API key (overrides env + config file)
|
|
13
|
+
// --base-url Override API base URL
|
|
14
|
+
// --output Output mode: json|ndjson|yaml|table (default json)
|
|
15
|
+
// --verbose Print request metadata to stderr
|
|
16
|
+
// --config Path to config file
|
|
17
|
+
// --timeout Go duration string, e.g. "30s"
|
|
18
|
+
func NewRoot(binaryName, version string) *cobra.Command {
|
|
19
|
+
root := &cobra.Command{
|
|
20
|
+
Use: binaryName,
|
|
21
|
+
Version: version,
|
|
22
|
+
}
|
|
23
|
+
root.PersistentFlags().String("api-key", "", "API key (overrides env + config file)")
|
|
24
|
+
root.PersistentFlags().String("base-url", "", "Override API base URL")
|
|
25
|
+
root.PersistentFlags().String("output", "json", "Output mode: json|ndjson|yaml|table")
|
|
26
|
+
root.PersistentFlags().Bool("verbose", false, "Print request metadata to stderr")
|
|
27
|
+
root.PersistentFlags().String("config", "", "Path to config file")
|
|
28
|
+
root.PersistentFlags().String("timeout", "30s", "Request timeout (Go duration)")
|
|
29
|
+
return root
|
|
30
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
// Code generated by honey. DO NOT EDIT.
|
|
2
|
+
package cli
|
|
3
|
+
|
|
4
|
+
import (
|
|
5
|
+
"encoding/json"
|
|
6
|
+
"fmt"
|
|
7
|
+
"io"
|
|
8
|
+
"iter"
|
|
9
|
+
"os"
|
|
10
|
+
|
|
11
|
+
"sigs.k8s.io/yaml"
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
// SSEEvent is the concrete SSE event type consumed by StreamSSE. The CLI's
|
|
15
|
+
// generated cmd/*.go files wrap the SDK's iterator into a Seq2[SSEEvent, error]
|
|
16
|
+
// before passing it in, so this runtime stays SDK-agnostic. Go generics do not
|
|
17
|
+
// support struct-field access through a type parameter, so this is a plain
|
|
18
|
+
// concrete struct rather than a constrained generic.
|
|
19
|
+
type SSEEvent struct {
|
|
20
|
+
Data string
|
|
21
|
+
Event string
|
|
22
|
+
ID string
|
|
23
|
+
Retry int
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// streamEvent is the on-wire shape that gets emitted for each SSE event.
|
|
27
|
+
type streamEvent struct {
|
|
28
|
+
Event string `json:"event,omitempty"`
|
|
29
|
+
ID string `json:"id,omitempty"`
|
|
30
|
+
Data json.RawMessage `json:"data"`
|
|
31
|
+
Retry int `json:"retry,omitempty"`
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// StreamSSE consumes an iter.Seq2 of SSE events and writes them to w.
|
|
35
|
+
//
|
|
36
|
+
// --output modes:
|
|
37
|
+
//
|
|
38
|
+
// ndjson (default for streams) → one JSON object per line.
|
|
39
|
+
// yaml → YAML documents separated by "---\n".
|
|
40
|
+
// table → falls back to ndjson; prints a warning to os.Stderr.
|
|
41
|
+
// json → treated as ndjson (no way to represent a live stream as one object).
|
|
42
|
+
func StreamSSE(it iter.Seq2[SSEEvent, error], w io.Writer, mode string) error {
|
|
43
|
+
if mode == "table" {
|
|
44
|
+
fmt.Fprintln(os.Stderr, "warning: --output=table is not supported for streaming endpoints; falling back to ndjson")
|
|
45
|
+
mode = "ndjson"
|
|
46
|
+
}
|
|
47
|
+
for ev, err := range it {
|
|
48
|
+
if err != nil {
|
|
49
|
+
return err
|
|
50
|
+
}
|
|
51
|
+
out := streamEvent{
|
|
52
|
+
Event: ev.Event,
|
|
53
|
+
ID: ev.ID,
|
|
54
|
+
Retry: ev.Retry,
|
|
55
|
+
}
|
|
56
|
+
if ev.Data == "" {
|
|
57
|
+
out.Data = json.RawMessage("null")
|
|
58
|
+
} else if json.Valid([]byte(ev.Data)) {
|
|
59
|
+
out.Data = json.RawMessage(ev.Data)
|
|
60
|
+
} else {
|
|
61
|
+
enc, _ := json.Marshal(ev.Data)
|
|
62
|
+
out.Data = enc
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
switch mode {
|
|
66
|
+
case "yaml":
|
|
67
|
+
enc, err := yaml.Marshal(out)
|
|
68
|
+
if err != nil {
|
|
69
|
+
return err
|
|
70
|
+
}
|
|
71
|
+
if _, err := w.Write(enc); err != nil {
|
|
72
|
+
return err
|
|
73
|
+
}
|
|
74
|
+
if _, err := fmt.Fprint(w, "---\n"); err != nil {
|
|
75
|
+
return err
|
|
76
|
+
}
|
|
77
|
+
default:
|
|
78
|
+
enc, err := json.Marshal(out)
|
|
79
|
+
if err != nil {
|
|
80
|
+
return err
|
|
81
|
+
}
|
|
82
|
+
if _, err := fmt.Fprintln(w, string(enc)); err != nil {
|
|
83
|
+
return err
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return nil
|
|
88
|
+
}
|
package/src/cli.ts
ADDED
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
import { existsSync, statSync, watch } from "node:fs"
|
|
3
|
+
import { dirname, resolve } from "node:path"
|
|
4
|
+
import { parseInitFlags, runInit } from "./init.ts"
|
|
5
|
+
import type { HoneyGoCliConfig, HoneyVitePluginConfig } from "./plugin.ts"
|
|
6
|
+
import { generateAndWrite, type getLastHoneyConfig, resolveHoneyConfig } from "./plugin.ts"
|
|
7
|
+
|
|
8
|
+
const USAGE =
|
|
9
|
+
"Usage: honey generate [--watch] [--config <path>] [--app <path>] [flags]\n" +
|
|
10
|
+
" honey init [--cf] [--force]"
|
|
11
|
+
|
|
12
|
+
type CliFlags = {
|
|
13
|
+
app?: string
|
|
14
|
+
cli?: boolean
|
|
15
|
+
cliBinaryName?: string
|
|
16
|
+
cliConfigName?: string
|
|
17
|
+
cliDefaultBaseUrl?: string
|
|
18
|
+
cliEnvPrefix?: string
|
|
19
|
+
cliModulePath?: string
|
|
20
|
+
cliOut?: string
|
|
21
|
+
cliSdkModulePath?: string
|
|
22
|
+
config?: string
|
|
23
|
+
manifest?: boolean
|
|
24
|
+
mergeTree?: string
|
|
25
|
+
sdk?: boolean
|
|
26
|
+
tree?: boolean
|
|
27
|
+
types?: boolean
|
|
28
|
+
watch?: boolean
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const BOOLEAN_FLAGS = new Set(["cli", "manifest", "sdk", "tree", "types", "watch"])
|
|
32
|
+
|
|
33
|
+
function parseArgs(args: string[]): CliFlags {
|
|
34
|
+
const parsed: Record<string, string> = {}
|
|
35
|
+
for (let i = 0; i < args.length; i++) {
|
|
36
|
+
const arg = args[i]
|
|
37
|
+
if (!arg.startsWith("--")) continue
|
|
38
|
+
const key = arg.slice(2)
|
|
39
|
+
if (BOOLEAN_FLAGS.has(key)) {
|
|
40
|
+
parsed[key] = "true"
|
|
41
|
+
} else if (i + 1 < args.length) {
|
|
42
|
+
parsed[key] = args[i + 1]
|
|
43
|
+
i++
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return {
|
|
48
|
+
app: parsed.app,
|
|
49
|
+
cli: parsed.cli !== undefined,
|
|
50
|
+
cliBinaryName: parsed["cli-binary-name"],
|
|
51
|
+
cliConfigName: parsed["cli-config-name"],
|
|
52
|
+
cliDefaultBaseUrl: parsed["cli-default-base-url"],
|
|
53
|
+
cliEnvPrefix: parsed["cli-env-prefix"],
|
|
54
|
+
cliModulePath: parsed["cli-module-path"],
|
|
55
|
+
cliOut: parsed["cli-out"],
|
|
56
|
+
cliSdkModulePath: parsed["cli-sdk-module-path"],
|
|
57
|
+
config: parsed.config,
|
|
58
|
+
manifest: parsed.manifest !== undefined,
|
|
59
|
+
mergeTree: parsed["merge-tree"],
|
|
60
|
+
sdk: parsed.sdk !== undefined,
|
|
61
|
+
tree: parsed.tree !== undefined,
|
|
62
|
+
types: parsed.types !== undefined,
|
|
63
|
+
watch: parsed.watch !== undefined,
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
async function loadConfigFromVite(configPath: string): Promise<HoneyVitePluginConfig | undefined> {
|
|
68
|
+
if (!existsSync(configPath)) return undefined
|
|
69
|
+
|
|
70
|
+
const { createJiti } = await import("jiti")
|
|
71
|
+
const jiti = createJiti(configPath, { fsCache: false, interopDefault: true, moduleCache: false })
|
|
72
|
+
|
|
73
|
+
/* importing the config executes honey() which stashes config */
|
|
74
|
+
await jiti.import(configPath)
|
|
75
|
+
|
|
76
|
+
/* read stashed config from the same module graph */
|
|
77
|
+
const pluginMod = (await jiti.import("honey/plugin")) as Record<string, unknown>
|
|
78
|
+
const getter = pluginMod.getLastHoneyConfig as typeof getLastHoneyConfig | undefined
|
|
79
|
+
return getter?.()
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function applyCodegenFlags(target: HoneyVitePluginConfig, flags: CliFlags): void {
|
|
83
|
+
if (!target.codegen) target.codegen = {}
|
|
84
|
+
const cg = target.codegen
|
|
85
|
+
if (flags.mergeTree) cg.mergeTree = flags.mergeTree
|
|
86
|
+
if (flags.manifest) cg.manifest = true
|
|
87
|
+
if (flags.sdk) cg.sdk = true
|
|
88
|
+
if (flags.tree) cg.tree = true
|
|
89
|
+
if (flags.types) cg.types = true
|
|
90
|
+
const cliOverride = buildCliConfigFromFlags(flags)
|
|
91
|
+
if (cliOverride) cg.cli = cliOverride
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function mergeCliOverrides(base: HoneyVitePluginConfig, flags: CliFlags): HoneyVitePluginConfig {
|
|
95
|
+
const merged = { ...base }
|
|
96
|
+
if (flags.app) merged.app = flags.app
|
|
97
|
+
applyCodegenFlags(merged, flags)
|
|
98
|
+
return merged
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function buildCliConfigFromFlags(flags: CliFlags): HoneyGoCliConfig | undefined {
|
|
102
|
+
if (!flags.cli && !flags.cliOut && !flags.cliBinaryName) return undefined
|
|
103
|
+
if (!flags.cliOut || !flags.cliBinaryName) {
|
|
104
|
+
throw new Error("--cli requires --cli-out and --cli-binary-name")
|
|
105
|
+
}
|
|
106
|
+
return {
|
|
107
|
+
binaryName: flags.cliBinaryName,
|
|
108
|
+
configName: flags.cliConfigName,
|
|
109
|
+
defaultBaseURL: flags.cliDefaultBaseUrl,
|
|
110
|
+
envPrefix: flags.cliEnvPrefix,
|
|
111
|
+
modulePath: flags.cliModulePath,
|
|
112
|
+
out: flags.cliOut,
|
|
113
|
+
sdkModulePath: flags.cliSdkModulePath,
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function configFromFlags(flags: CliFlags): HoneyVitePluginConfig | undefined {
|
|
118
|
+
if (!flags.app) return undefined
|
|
119
|
+
const config: HoneyVitePluginConfig = { app: flags.app }
|
|
120
|
+
applyCodegenFlags(config, flags)
|
|
121
|
+
return config
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const GEN_IGNORE_RE = /(_gen[/\\]|\.gen\.(tsx?|json|d\.ts)$)/
|
|
125
|
+
|
|
126
|
+
async function main() {
|
|
127
|
+
const args = process.argv.slice(2)
|
|
128
|
+
const command = args[0]
|
|
129
|
+
|
|
130
|
+
if (command === "init") {
|
|
131
|
+
runInit(process.cwd(), parseInitFlags(args.slice(1)))
|
|
132
|
+
return
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (command !== "generate") {
|
|
136
|
+
console.error(USAGE)
|
|
137
|
+
process.exit(1)
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const flags = parseArgs(args.slice(1))
|
|
141
|
+
const cwd = process.cwd()
|
|
142
|
+
|
|
143
|
+
/* resolve config: vite.config.ts → CLI flags fallback */
|
|
144
|
+
const configPath = resolve(cwd, flags.config ?? "vite.config.ts")
|
|
145
|
+
let rawConfig: HoneyVitePluginConfig | undefined
|
|
146
|
+
|
|
147
|
+
const viteConfig = await loadConfigFromVite(configPath)
|
|
148
|
+
if (viteConfig) {
|
|
149
|
+
rawConfig = mergeCliOverrides(viteConfig, flags)
|
|
150
|
+
} else {
|
|
151
|
+
rawConfig = configFromFlags(flags)
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (!rawConfig) {
|
|
155
|
+
console.error("No config found. Provide a vite.config.ts with honey() or use --app flag.")
|
|
156
|
+
process.exit(1)
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const resolved = resolveHoneyConfig(rawConfig)
|
|
160
|
+
|
|
161
|
+
async function generate(): Promise<void> {
|
|
162
|
+
try {
|
|
163
|
+
await generateAndWrite(resolved, cwd)
|
|
164
|
+
console.log("honey: generated")
|
|
165
|
+
} catch (err) {
|
|
166
|
+
console.error("honey: generation failed", err)
|
|
167
|
+
if (!flags.watch) process.exit(1)
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
await generate()
|
|
172
|
+
|
|
173
|
+
if (flags.watch) {
|
|
174
|
+
if (!resolved.app) throw new Error("watch mode requires --app or a vite honey() config")
|
|
175
|
+
const appAbs = resolve(cwd, resolved.app)
|
|
176
|
+
const srcDir = dirname(appAbs)
|
|
177
|
+
let debounceTimer: ReturnType<typeof setTimeout> | undefined
|
|
178
|
+
let lastMtime = existsSync(appAbs) ? statSync(appAbs).mtimeMs : 0
|
|
179
|
+
|
|
180
|
+
const schedule = (): void => {
|
|
181
|
+
clearTimeout(debounceTimer)
|
|
182
|
+
debounceTimer = setTimeout(() => generate(), 100)
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
console.log(`honey: watching ${srcDir}`)
|
|
186
|
+
watch(srcDir, { recursive: true }, (_event, filename) => {
|
|
187
|
+
if (!filename || GEN_IGNORE_RE.test(String(filename))) return
|
|
188
|
+
schedule()
|
|
189
|
+
})
|
|
190
|
+
watch(appAbs, schedule)
|
|
191
|
+
setInterval(() => {
|
|
192
|
+
if (!existsSync(appAbs)) return
|
|
193
|
+
const mtime = statSync(appAbs).mtimeMs
|
|
194
|
+
if (mtime === lastMtime) return
|
|
195
|
+
lastMtime = mtime
|
|
196
|
+
schedule()
|
|
197
|
+
}, 250)
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
main().catch((err) => {
|
|
202
|
+
console.error(err)
|
|
203
|
+
process.exit(1)
|
|
204
|
+
})
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export type ClientErrorInit<TBody = unknown> = {
|
|
2
|
+
body: TBody
|
|
3
|
+
message: string
|
|
4
|
+
response: Response
|
|
5
|
+
status: number
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export class ClientError<TBody = unknown> extends Error {
|
|
9
|
+
readonly body: TBody
|
|
10
|
+
readonly response: Response
|
|
11
|
+
readonly status: number
|
|
12
|
+
|
|
13
|
+
constructor(init: ClientErrorInit<TBody>) {
|
|
14
|
+
super(init.message)
|
|
15
|
+
if (typeof Error.captureStackTrace === "function") {
|
|
16
|
+
Error.captureStackTrace(this, ClientError)
|
|
17
|
+
}
|
|
18
|
+
this.name = "ClientError"
|
|
19
|
+
this.body = init.body
|
|
20
|
+
this.response = init.response
|
|
21
|
+
this.status = init.status
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function isClientError<T = unknown>(e: unknown): e is ClientError<T> {
|
|
26
|
+
return e instanceof ClientError
|
|
27
|
+
}
|