mirror of https://github.com/open-feature/cli.git
Compare commits
21 Commits
Author | SHA1 | Date |
---|---|---|
|
2003666eaa | |
|
cf962d967c | |
|
2835e3cf10 | |
|
288023c5dd | |
|
b867485101 | |
|
6145c2e15d | |
|
32f912a089 | |
|
594cf538be | |
|
5d7a60754a | |
|
8b70612472 | |
|
51232fea1c | |
|
4f909c1068 | |
|
1b4d062045 | |
|
35533dceb0 | |
|
a25f90a65b | |
|
11b9c34d86 | |
|
3ec8443823 | |
|
83dac705b9 | |
|
bcd11ea9c8 | |
|
79d3dceb3a | |
|
8eec77965a |
|
@ -4,6 +4,7 @@ on:
|
|||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
merge_group:
|
||||
|
||||
permissions:
|
||||
# Required: allow read access to the content for analysis.
|
||||
|
@ -28,4 +29,4 @@ jobs:
|
|||
uses: golangci/golangci-lint-action@v6
|
||||
with:
|
||||
version: v1.64
|
||||
only-new-issues: true
|
||||
only-new-issues: true
|
||||
|
|
|
@ -57,7 +57,7 @@ jobs:
|
|||
go-version-file: 'go.mod'
|
||||
|
||||
- name: Run all integration tests with Dagger
|
||||
uses: dagger/dagger-for-github@v5
|
||||
uses: dagger/dagger-for-github@b81317a976cb7f7125469707321849737cd1b3bc # v7
|
||||
with:
|
||||
workdir: .
|
||||
verb: run
|
||||
|
|
|
@ -28,4 +28,9 @@ dist
|
|||
# openfeature cli config
|
||||
.openfeature.yaml
|
||||
|
||||
.idea/
|
||||
.idea/
|
||||
|
||||
node_modules/
|
||||
npm-debug.log*
|
||||
generated/
|
||||
*.log
|
|
@ -21,6 +21,7 @@ builds:
|
|||
- linux
|
||||
- windows
|
||||
- darwin
|
||||
binary: ./cmd/openfeature
|
||||
|
||||
archives:
|
||||
- formats: tar.gz
|
||||
|
@ -35,49 +36,49 @@ archives:
|
|||
# use zip for windows archives
|
||||
format_overrides:
|
||||
- goos: windows
|
||||
formats: [ 'zip' ]
|
||||
formats: ["zip"]
|
||||
|
||||
checksum:
|
||||
name_template: 'checksums.txt'
|
||||
name_template: "checksums.txt"
|
||||
|
||||
report_sizes: true
|
||||
|
||||
dockers:
|
||||
- image_templates: ["ghcr.io/open-feature/cli:{{ .Version }}-amd64"]
|
||||
dockerfile: Dockerfile
|
||||
use: buildx
|
||||
build_flag_templates:
|
||||
- --platform=linux/amd64
|
||||
- --label=org.opencontainers.image.title={{ .ProjectName }} cli
|
||||
- --label=org.opencontainers.image.url=https://github.com/open-feature/cli
|
||||
- --label=org.opencontainers.image.source=https://github.com/open-feature/cli
|
||||
- --label=org.opencontainers.image.version={{ .Version }}
|
||||
- --label=org.opencontainers.image.created={{ time "2006-01-02T15:04:05Z07:00" }}
|
||||
- --label=org.opencontainers.image.description="OpenFeature’s official command-line tool"
|
||||
- --label=org.opencontainers.image.revision={{ .FullCommit }}
|
||||
- --label=org.opencontainers.image.licenses=Apache-2.0
|
||||
- image_templates: ["ghcr.io/open-feature/cli:{{ .Version }}-amd64"]
|
||||
dockerfile: Dockerfile
|
||||
use: buildx
|
||||
build_flag_templates:
|
||||
- --platform=linux/amd64
|
||||
- --label=org.opencontainers.image.title={{ .ProjectName }} cli
|
||||
- --label=org.opencontainers.image.url=https://github.com/open-feature/cli
|
||||
- --label=org.opencontainers.image.source=https://github.com/open-feature/cli
|
||||
- --label=org.opencontainers.image.version={{ .Version }}
|
||||
- --label=org.opencontainers.image.created={{ time "2006-01-02T15:04:05Z07:00" }}
|
||||
- --label=org.opencontainers.image.description="OpenFeature’s official command-line tool"
|
||||
- --label=org.opencontainers.image.revision={{ .FullCommit }}
|
||||
- --label=org.opencontainers.image.licenses=Apache-2.0
|
||||
|
||||
- image_templates: ["ghcr.io/open-feature/cli:{{ .Version }}-arm64"]
|
||||
goarch: arm64
|
||||
dockerfile: Dockerfile
|
||||
use: buildx
|
||||
build_flag_templates:
|
||||
- --platform=linux/arm64
|
||||
- --label=org.opencontainers.image.title={{ .ProjectName }} cli
|
||||
- --label=org.opencontainers.image.url=https://github.com/open-feature/cli
|
||||
- --label=org.opencontainers.image.source=https://github.com/open-feature/cli
|
||||
- --label=org.opencontainers.image.version={{ .Version }}
|
||||
- --label=org.opencontainers.image.created={{ time "2006-01-02T15:04:05Z07:00" }}
|
||||
- --label=org.opencontainers.image.description="OpenFeature’s official command-line tool"
|
||||
- --label=org.opencontainers.image.revision={{ .FullCommit }}
|
||||
- --label=org.opencontainers.image.licenses=Apache-2.0
|
||||
- image_templates: ["ghcr.io/open-feature/cli:{{ .Version }}-arm64"]
|
||||
goarch: arm64
|
||||
dockerfile: Dockerfile
|
||||
use: buildx
|
||||
build_flag_templates:
|
||||
- --platform=linux/arm64
|
||||
- --label=org.opencontainers.image.title={{ .ProjectName }} cli
|
||||
- --label=org.opencontainers.image.url=https://github.com/open-feature/cli
|
||||
- --label=org.opencontainers.image.source=https://github.com/open-feature/cli
|
||||
- --label=org.opencontainers.image.version={{ .Version }}
|
||||
- --label=org.opencontainers.image.created={{ time "2006-01-02T15:04:05Z07:00" }}
|
||||
- --label=org.opencontainers.image.description="OpenFeature’s official command-line tool"
|
||||
- --label=org.opencontainers.image.revision={{ .FullCommit }}
|
||||
- --label=org.opencontainers.image.licenses=Apache-2.0
|
||||
|
||||
docker_manifests:
|
||||
- name_template: ghcr.io/open-feature/cli:{{ .Version }}
|
||||
image_templates:
|
||||
- ghcr.io/open-feature/cli:{{ .Version }}-amd64
|
||||
- ghcr.io/open-feature/cli:{{ .Version }}-arm64
|
||||
- name_template: ghcr.io/open-feature/cli:latest
|
||||
image_templates:
|
||||
- ghcr.io/open-feature/cli:{{ .Version }}-amd64
|
||||
- ghcr.io/open-feature/cli:{{ .Version }}-arm64
|
||||
- name_template: ghcr.io/open-feature/cli:{{ .Version }}
|
||||
image_templates:
|
||||
- ghcr.io/open-feature/cli:{{ .Version }}-amd64
|
||||
- ghcr.io/open-feature/cli:{{ .Version }}-arm64
|
||||
- name_template: ghcr.io/open-feature/cli:latest
|
||||
image_templates:
|
||||
- ghcr.io/open-feature/cli:{{ .Version }}-amd64
|
||||
- ghcr.io/open-feature/cli:{{ .Version }}-arm64
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
FROM alpine:3.21
|
||||
FROM alpine:3.22
|
||||
|
||||
COPY ./openfeature usr/local/bin/openfeature
|
||||
|
||||
|
|
10
Makefile
10
Makefile
|
@ -10,6 +10,16 @@ test-integration-csharp:
|
|||
@echo "Running C# integration test with Dagger..."
|
||||
@go run ./test/integration/cmd/csharp/run.go
|
||||
|
||||
.PHONY: test-integration-go
|
||||
test-integration-go:
|
||||
@echo "Running Go integration test with Dagger..."
|
||||
@go run ./test/integration/cmd/go/run.go
|
||||
|
||||
.PHONY: test-integration-nodejs
|
||||
test-integration-nodejs:
|
||||
@echo "Running NodeJS integration test with Dagger..."
|
||||
@go run ./test/integration/cmd/nodejs/run.go
|
||||
|
||||
.PHONY: test-integration
|
||||
test-integration:
|
||||
@echo "Running all integration tests with Dagger..."
|
||||
|
|
|
@ -58,7 +58,7 @@ docker run -it -v $(pwd):/local -w /local ghcr.io/open-feature/cli:latest
|
|||
If you have `Go >= 1.23` installed, you can install the CLI using the following command:
|
||||
|
||||
```bash
|
||||
go install github.com/open-feature/cli@latest
|
||||
go install github.com/open-feature/cli/cmd/openfeature@latest
|
||||
```
|
||||
|
||||
### via pre-built binaries
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
package main
|
||||
|
||||
import "github.com/open-feature/cli/cmd"
|
||||
import "github.com/open-feature/cli/internal/cmd"
|
||||
|
||||
var (
|
||||
// Overridden by Go Releaser at build time
|
|
@ -6,7 +6,7 @@ import (
|
|||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/open-feature/cli/cmd"
|
||||
"github.com/open-feature/cli/internal/cmd"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/cobra/doc"
|
||||
)
|
||||
|
|
75
go.mod
75
go.mod
|
@ -2,60 +2,85 @@ module github.com/open-feature/cli
|
|||
|
||||
go 1.23.0
|
||||
|
||||
toolchain go1.24.0
|
||||
|
||||
require (
|
||||
github.com/google/go-cmp v0.6.0
|
||||
dagger.io/dagger v0.18.12
|
||||
github.com/google/go-cmp v0.7.0
|
||||
github.com/iancoleman/strcase v0.3.0
|
||||
github.com/invopop/jsonschema v0.13.0
|
||||
github.com/pterm/pterm v0.12.80
|
||||
github.com/pterm/pterm v0.12.81
|
||||
github.com/spf13/afero v1.14.0
|
||||
github.com/spf13/cobra v1.8.1
|
||||
github.com/spf13/cobra v1.9.1
|
||||
github.com/spf13/pflag v1.0.6
|
||||
github.com/spf13/viper v1.20.0
|
||||
github.com/spf13/viper v1.20.1
|
||||
github.com/stretchr/testify v1.10.0
|
||||
github.com/xeipuuv/gojsonschema v1.2.0
|
||||
golang.org/x/text v0.23.0
|
||||
golang.org/x/text v0.26.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
)
|
||||
|
||||
require (
|
||||
atomicgo.dev/cursor v0.2.0 // indirect
|
||||
atomicgo.dev/keyboard v0.2.9 // indirect
|
||||
atomicgo.dev/schedule v0.1.0 // indirect
|
||||
dagger.io/dagger v0.10.2 // indirect
|
||||
github.com/99designs/gqlgen v0.17.31 // indirect
|
||||
github.com/Khan/genqlient v0.6.0 // indirect
|
||||
github.com/adrg/xdg v0.4.0 // indirect
|
||||
github.com/99designs/gqlgen v0.17.75 // indirect
|
||||
github.com/Khan/genqlient v0.8.1 // indirect
|
||||
github.com/adrg/xdg v0.5.3 // indirect
|
||||
github.com/bahlo/generic-list-go v0.2.0 // indirect
|
||||
github.com/buger/jsonparser v1.1.1 // indirect
|
||||
github.com/containerd/console v1.0.3 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect
|
||||
github.com/cenkalti/backoff/v5 v5.0.2 // indirect
|
||||
github.com/containerd/console v1.0.5 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/fsnotify/fsnotify v1.8.0 // indirect
|
||||
github.com/fsnotify/fsnotify v1.9.0 // indirect
|
||||
github.com/go-logr/logr v1.4.3 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/go-viper/mapstructure/v2 v2.2.1 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/gookit/color v1.5.4 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/lithammer/fuzzysearch v1.1.8 // indirect
|
||||
github.com/mailru/easyjson v0.7.7 // indirect
|
||||
github.com/mailru/easyjson v0.9.0 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.16 // indirect
|
||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.3 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/rivo/uniseg v0.4.7 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/sagikazarmark/locafero v0.8.0 // indirect
|
||||
github.com/sagikazarmark/locafero v0.9.0 // indirect
|
||||
github.com/sosodev/duration v1.3.1 // indirect
|
||||
github.com/sourcegraph/conc v0.3.0 // indirect
|
||||
github.com/spf13/cast v1.7.1 // indirect
|
||||
github.com/spf13/cast v1.8.0 // indirect
|
||||
github.com/subosito/gotenv v1.6.0 // indirect
|
||||
github.com/vektah/gqlparser/v2 v2.5.6 // indirect
|
||||
github.com/vektah/gqlparser/v2 v2.5.28 // indirect
|
||||
github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
|
||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
|
||||
go.opentelemetry.io/otel v1.36.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.12.2 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.12.2 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.36.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.36.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.36.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.36.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.36.0 // indirect
|
||||
go.opentelemetry.io/otel/log v0.12.2 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.36.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk v1.36.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk/log v0.12.2 // indirect
|
||||
go.opentelemetry.io/otel/sdk/metric v1.36.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.36.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.7.0 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 // indirect
|
||||
golang.org/x/sync v0.12.0 // indirect
|
||||
golang.org/x/sys v0.31.0 // indirect
|
||||
golang.org/x/term v0.30.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
golang.org/x/exp v0.0.0-20250530174510-65e920069ea6 // indirect
|
||||
golang.org/x/net v0.41.0 // indirect
|
||||
golang.org/x/sync v0.15.0 // indirect
|
||||
golang.org/x/sys v0.33.0 // indirect
|
||||
golang.org/x/term v0.32.0 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250528174236-200df99c418a // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250528174236-200df99c418a // indirect
|
||||
google.golang.org/grpc v1.73.0 // indirect
|
||||
google.golang.org/protobuf v1.36.6 // indirect
|
||||
)
|
||||
|
|
174
go.sum
174
go.sum
|
@ -6,12 +6,18 @@ atomicgo.dev/keyboard v0.2.9 h1:tOsIid3nlPLZ3lwgG8KZMp/SFmr7P0ssEN5JUsm78K8=
|
|||
atomicgo.dev/keyboard v0.2.9/go.mod h1:BC4w9g00XkxH/f1HXhW2sXmJFOCWbKn9xrOunSFtExQ=
|
||||
atomicgo.dev/schedule v0.1.0 h1:nTthAbhZS5YZmgYbb2+DH8uQIZcTlIrd4eYr3UQxEjs=
|
||||
atomicgo.dev/schedule v0.1.0/go.mod h1:xeUa3oAkiuHYh8bKiQBRojqAMq3PXXbJujjb0hw8pEU=
|
||||
dagger.io/dagger v0.10.2 h1:8q4AwKm48qAWZdY7O2NMZ3FveteDHJeM3WwmcNKZOM0=
|
||||
dagger.io/dagger v0.10.2/go.mod h1:AonAYX6ZXNGsVvro4HhB/Uzsp9FU+aI41YfAEY9f5mI=
|
||||
github.com/99designs/gqlgen v0.17.31 h1:VncSQ82VxieHkea8tz11p7h/zSbvHSxSDZfywqWt158=
|
||||
github.com/99designs/gqlgen v0.17.31/go.mod h1:i4rEatMrzzu6RXaHydq1nmEPZkb3bKQsnxNRHS4DQB4=
|
||||
github.com/Khan/genqlient v0.6.0 h1:Bwb1170ekuNIVIwTJEqvO8y7RxBxXu639VJOkKSrwAk=
|
||||
github.com/Khan/genqlient v0.6.0/go.mod h1:rvChwWVTqXhiapdhLDV4bp9tz/Xvtewwkon4DpWWCRM=
|
||||
dagger.io/dagger v0.18.10 h1:Ibyz5LqxjjEHfLMlaU9PJ3xt3ju7p29RWy0lVfvSNU0=
|
||||
dagger.io/dagger v0.18.10/go.mod h1:VSj+2HMd/EnaCVt7gTY70p8LBW+oQDYjA1XTadr8vBE=
|
||||
dagger.io/dagger v0.18.11 h1:6lSfemlbGM2HmdOjhgevrX2+orMDGKU/xTaBMZ+otyY=
|
||||
dagger.io/dagger v0.18.11/go.mod h1:azlZ24m2br95t0jQHUBpL5SiafeqtVDLl1Itlq6GO+4=
|
||||
dagger.io/dagger v0.18.12 h1:s7v8aHlzDUogZ/jW92lHC+gljCNRML+0mosfh13R4vs=
|
||||
dagger.io/dagger v0.18.12/go.mod h1:azlZ24m2br95t0jQHUBpL5SiafeqtVDLl1Itlq6GO+4=
|
||||
github.com/99designs/gqlgen v0.17.74 h1:1FuVtkXxOc87xpKio3f6sohREmec+Jvy86PcYOuwgWo=
|
||||
github.com/99designs/gqlgen v0.17.74/go.mod h1:a+iR6mfRLNRp++kDpooFHiPWYiWX3Yu1BIilQRHgh10=
|
||||
github.com/99designs/gqlgen v0.17.75 h1:GwHJsptXWLHeY7JO8b7YueUI4w9Pom6wJTICosDtQuI=
|
||||
github.com/99designs/gqlgen v0.17.75/go.mod h1:p7gbTpdnHyl70hmSpM8XG8GiKwmCv+T5zkdY8U8bLog=
|
||||
github.com/Khan/genqlient v0.8.1 h1:wtOCc8N9rNynRLXN3k3CnfzheCUNKBcvXmVv5zt6WCs=
|
||||
github.com/Khan/genqlient v0.8.1/go.mod h1:R2G6DzjBvCbhjsEajfRjbWdVglSH/73kSivC9TLWVjU=
|
||||
github.com/MarvinJWendt/testza v0.1.0/go.mod h1:7AxNvlfeHP7Z/hDQ5JtE3OKYT3XFUeLCDE2DQninSqs=
|
||||
github.com/MarvinJWendt/testza v0.2.1/go.mod h1:God7bhG8n6uQxwdScay+gjm9/LnO4D3kkcZX4hv9Rp8=
|
||||
github.com/MarvinJWendt/testza v0.2.8/go.mod h1:nwIcjmr0Zz+Rcwfh3/4UhBp7ePKVhuBExvZqnKYWlII=
|
||||
|
@ -21,44 +27,56 @@ github.com/MarvinJWendt/testza v0.3.0/go.mod h1:eFcL4I0idjtIx8P9C6KkAuLgATNKpX4/
|
|||
github.com/MarvinJWendt/testza v0.4.2/go.mod h1:mSdhXiKH8sg/gQehJ63bINcCKp7RtYewEjXsvsVUPbE=
|
||||
github.com/MarvinJWendt/testza v0.5.2 h1:53KDo64C1z/h/d/stCYCPY69bt/OSwjq5KpFNwi+zB4=
|
||||
github.com/MarvinJWendt/testza v0.5.2/go.mod h1:xu53QFE5sCdjtMCKk8YMQ2MnymimEctc4n3EjyIYvEY=
|
||||
github.com/adrg/xdg v0.4.0 h1:RzRqFcjH4nE5C6oTAxhBtoE2IRyjBSa62SCbyPidvls=
|
||||
github.com/adrg/xdg v0.4.0/go.mod h1:N6ag73EX4wyxeaoeHctc1mas01KZgsj5tYiAIwqJE/E=
|
||||
github.com/agnivade/levenshtein v1.1.1/go.mod h1:veldBMzWxcCG2ZvUTKD2kJNRdCk5hVbJomOvKkmgYbo=
|
||||
github.com/adrg/xdg v0.5.3 h1:xRnxJXne7+oWDatRhR1JLnvuccuIeCoBu2rtuLqQB78=
|
||||
github.com/adrg/xdg v0.5.3/go.mod h1:nlTsY+NNiCBGCK2tpm09vRqfVzrc2fLmXGpBLF0zlTQ=
|
||||
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ=
|
||||
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
|
||||
github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE=
|
||||
github.com/atomicgo/cursor v0.0.1/go.mod h1:cBON2QmmrysudxNBFthvMtN32r3jxVRIvzkUiF/RuIk=
|
||||
github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk=
|
||||
github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg=
|
||||
github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs=
|
||||
github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0=
|
||||
github.com/containerd/console v1.0.3 h1:lIr7SlA5PxZyMV30bDW0MGbiOPXwc63yRuCP0ARubLw=
|
||||
github.com/cenkalti/backoff/v5 v5.0.2 h1:rIfFVxEf1QsI7E1ZHfp/B4DF/6QBAUhmgkxc0H7Zss8=
|
||||
github.com/cenkalti/backoff/v5 v5.0.2/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw=
|
||||
github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/containerd/console v1.0.5 h1:R0ymNeydRqH2DmakFNdmjR2k0t7UPuiOV/N/27/qqsc=
|
||||
github.com/containerd/console v1.0.5/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.7 h1:zbFlGlXEAKlwXpmvle3d8Oe3YnkKIK4xSRTd3sHPnBo=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.7/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48/go.mod h1:if7Fbed8SFyPtHLHbg49SI7NAdJiC5WIA09pe59rfAA=
|
||||
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
|
||||
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||
github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M=
|
||||
github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
|
||||
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
|
||||
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
|
||||
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss=
|
||||
github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gookit/color v1.4.2/go.mod h1:fqRyamkC1W8uxl+lxCQxOT09l/vYfZ+QeiX3rKQHCoQ=
|
||||
github.com/gookit/color v1.5.0/go.mod h1:43aQb+Zerm/BWh2GnrgOQm7ffz7tvQXEKV6BFMl7wAo=
|
||||
github.com/gookit/color v1.5.4 h1:FZmqs7XOyGgCAxmWyPslpiok1k05wmY3SJTytgvYFs0=
|
||||
github.com/gookit/color v1.5.4/go.mod h1:pZJOeOS8DM43rXbp4AZo1n9zCU2qjpcRko0b6/QJi9w=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 h1:5ZPtiqj0JL5oKWmcsq4VMaAW5ukBEgSGXEN89zeH1Jo=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3/go.mod h1:ndYquD05frm2vACXE1nsccT4oJzjhw2arTS2cpUD1PI=
|
||||
github.com/iancoleman/strcase v0.3.0 h1:nTXanmYxhfFAMjZL34Ov6gkzEsSJZ5DbhxWjvSASxEI=
|
||||
github.com/iancoleman/strcase v0.3.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/invopop/jsonschema v0.13.0 h1:KvpoAJWEjR3uD9Kbm2HWJmqsEaHt8lBUpd0qHcIi21E=
|
||||
github.com/invopop/jsonschema v0.13.0/go.mod h1:ffZ5Km5SWWRAIN6wbDXItl95euhFz2uON45H2qjYt+0=
|
||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/klauspost/cpuid/v2 v2.0.10/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c=
|
||||
github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c=
|
||||
|
@ -73,15 +91,15 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
|||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/lithammer/fuzzysearch v1.1.8 h1:/HIuJnjHuXS8bKaiTMeeDlW2/AyIWk2brx1V8LFgLN4=
|
||||
github.com/lithammer/fuzzysearch v1.1.8/go.mod h1:IdqeyBClc3FFqSzYq/MXESsS4S0FsZ5ajtkr5xPLts4=
|
||||
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
|
||||
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||
github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4=
|
||||
github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=
|
||||
github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
|
||||
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
|
||||
github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
|
||||
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
|
||||
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
|
@ -92,33 +110,34 @@ github.com/pterm/pterm v0.12.31/go.mod h1:32ZAWZVXD7ZfG0s8qqHXePte42kdz8ECtRyEej
|
|||
github.com/pterm/pterm v0.12.33/go.mod h1:x+h2uL+n7CP/rel9+bImHD5lF3nM9vJj80k9ybiiTTE=
|
||||
github.com/pterm/pterm v0.12.36/go.mod h1:NjiL09hFhT/vWjQHSj1athJpx6H8cjpHXNAK5bUw8T8=
|
||||
github.com/pterm/pterm v0.12.40/go.mod h1:ffwPLwlbXxP+rxT0GsgDTzS3y3rmpAO1NMjUkGTYf8s=
|
||||
github.com/pterm/pterm v0.12.80 h1:mM55B+GnKUnLMUSqhdINe4s6tOuVQIetQ3my8JGyAIg=
|
||||
github.com/pterm/pterm v0.12.80/go.mod h1:c6DeF9bSnOSeFPZlfs4ZRAFcf5SCoTwvwQ5xaKGQlHo=
|
||||
github.com/pterm/pterm v0.12.81 h1:ju+j5I2++FO1jBKMmscgh5h5DPFDFMB7epEjSoKehKA=
|
||||
github.com/pterm/pterm v0.12.81/go.mod h1:TyuyrPjnxfwP+ccJdBTeWHtd/e0ybQHkOS/TakajZCw=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
||||
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
|
||||
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
|
||||
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/sagikazarmark/locafero v0.8.0 h1:mXaMVw7IqxNBxfv3LdWt9MDmcWDQ1fagDH918lOdVaQ=
|
||||
github.com/sagikazarmark/locafero v0.8.0/go.mod h1:UBUyz37V+EdMS3hDF3QWIiVr/2dPrx49OMO0Bn0hJqk=
|
||||
github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ=
|
||||
github.com/sagikazarmark/locafero v0.9.0 h1:GbgQGNtTrEmddYDSAH9QLRyfAHY12md+8YFTqyMTC9k=
|
||||
github.com/sagikazarmark/locafero v0.9.0/go.mod h1:UBUyz37V+EdMS3hDF3QWIiVr/2dPrx49OMO0Bn0hJqk=
|
||||
github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
|
||||
github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8=
|
||||
github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I=
|
||||
github.com/sosodev/duration v1.3.1 h1:qtHBDMQ6lvMQsL15g4aopM4HEfOaYuhWBw3NPTtlqq4=
|
||||
github.com/sosodev/duration v1.3.1/go.mod h1:RQIBBX0+fMLc/D9+Jb/fwvVmo0eZvDDEERAikUR6SDg=
|
||||
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
|
||||
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
|
||||
github.com/spf13/afero v1.14.0 h1:9tH6MapGnn/j0eb0yIXiLjERO8RB6xIVZRDCX7PtqWA=
|
||||
github.com/spf13/afero v1.14.0/go.mod h1:acJQ8t0ohCGuMN3O+Pv0V0hgMxNYDlvdk+VTfyZmbYo=
|
||||
github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y=
|
||||
github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
|
||||
github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
|
||||
github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/cast v1.8.0 h1:gEN9K4b8Xws4EX0+a0reLmhq8moKn7ntRlQYgjPeCDk=
|
||||
github.com/spf13/cast v1.8.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
|
||||
github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
|
||||
github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
|
||||
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
|
||||
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/viper v1.20.0 h1:zrxIyR3RQIOsarIrgL8+sAvALXul9jeEPa06Y0Ph6vY=
|
||||
github.com/spf13/viper v1.20.0/go.mod h1:P9Mdzt1zoHIG8m2eZQinpiBjo6kCmZSKBClNNqjJvu4=
|
||||
github.com/spf13/viper v1.20.1 h1:ZMi+z/lvLyPSCoNtFCpqjy0S4kPbirhpTMwl8BkW9X4=
|
||||
github.com/spf13/viper v1.20.1/go.mod h1:P9Mdzt1zoHIG8m2eZQinpiBjo6kCmZSKBClNNqjJvu4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
|
@ -128,12 +147,15 @@ github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOf
|
|||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
|
||||
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
|
||||
github.com/vektah/gqlparser/v2 v2.5.6 h1:Ou14T0N1s191eRMZ1gARVqohcbe1e8FrcONScsq8cRU=
|
||||
github.com/vektah/gqlparser/v2 v2.5.6/go.mod h1:z8xXUff237NntSuH8mLFijZ+1tjV1swDbpDqjJmk6ME=
|
||||
github.com/vektah/gqlparser/v2 v2.5.27 h1:RHPD3JOplpk5mP5JGX8RKZkt2/Vwj/PZv0HxTdwFp0s=
|
||||
github.com/vektah/gqlparser/v2 v2.5.27/go.mod h1:D1/VCZtV3LPnQrcPBeR/q5jkSQIPti0uYCP/RI0gIeo=
|
||||
github.com/vektah/gqlparser/v2 v2.5.28 h1:bIulcl3LF69ba6EiZVGD88y4MkM+Jxrf3P2MX8xLRkY=
|
||||
github.com/vektah/gqlparser/v2 v2.5.28/go.mod h1:D1/VCZtV3LPnQrcPBeR/q5jkSQIPti0uYCP/RI0gIeo=
|
||||
github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/fJgbpc=
|
||||
github.com/wk8/go-ordered-map/v2 v2.1.8/go.mod h1:5nJHM5DyteebpVlHnWMV0rPz6Zp7+xBAnxjb1X5vnTw=
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo=
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
|
||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
|
||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
|
||||
github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=
|
||||
|
@ -142,61 +164,107 @@ github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778/go.mod h1:2MuV+tbUrU1z
|
|||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
|
||||
go.opentelemetry.io/otel v1.36.0 h1:UumtzIklRBY6cI/lllNZlALOF5nNIzJVb16APdvgTXg=
|
||||
go.opentelemetry.io/otel v1.36.0/go.mod h1:/TcFMXYjyRNh8khOAO9ybYkqaDBb/70aVwkNML4pP8E=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.12.2 h1:06ZeJRe5BnYXceSM9Vya83XXVaNGe3H1QqsvqRANQq8=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.12.2/go.mod h1:DvPtKE63knkDVP88qpatBj81JxN+w1bqfVbsbCbj1WY=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.12.2 h1:tPLwQlXbJ8NSOfZc4OkgU5h2A38M4c9kfHSVc4PFQGs=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.12.2/go.mod h1:QTnxBwT/1rBIgAG1goq6xMydfYOBKU6KTiYF4fp5zL8=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.36.0 h1:zwdo1gS2eH26Rg+CoqVQpEK1h8gvt5qyU5Kk5Bixvow=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.36.0/go.mod h1:rUKCPscaRWWcqGT6HnEmYrK+YNe5+Sw64xgQTOJ5b30=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.36.0 h1:gAU726w9J8fwr4qRDqu1GYMNNs4gXrU+Pv20/N1UpB4=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.36.0/go.mod h1:RboSDkp7N292rgu+T0MgVt2qgFGu6qa1RpZDOtpL76w=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.36.0 h1:dNzwXjZKpMpE2JhmO+9HsPl42NIXFIFSUSSs0fiqra0=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.36.0/go.mod h1:90PoxvaEB5n6AOdZvi+yWJQoE95U8Dhhw2bSyRqnTD0=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.36.0 h1:JgtbA0xkWHnTmYk7YusopJFX6uleBmAuZ8n05NEh8nQ=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.36.0/go.mod h1:179AK5aar5R3eS9FucPy6rggvU0g52cvKId8pv4+v0c=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.36.0 h1:nRVXXvf78e00EwY6Wp0YII8ww2JVWshZ20HfTlE11AM=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.36.0/go.mod h1:r49hO7CgrxY9Voaj3Xe8pANWtr0Oq916d0XAmOoCZAQ=
|
||||
go.opentelemetry.io/otel/log v0.12.2 h1:yob9JVHn2ZY24byZeaXpTVoPS6l+UrrxmxmPKohXTwc=
|
||||
go.opentelemetry.io/otel/log v0.12.2/go.mod h1:ShIItIxSYxufUMt+1H5a2wbckGli3/iCfuEbVZi/98E=
|
||||
go.opentelemetry.io/otel/metric v1.36.0 h1:MoWPKVhQvJ+eeXWHFBOPoBOi20jh6Iq2CcCREuTYufE=
|
||||
go.opentelemetry.io/otel/metric v1.36.0/go.mod h1:zC7Ks+yeyJt4xig9DEw9kuUFe5C3zLbVjV2PzT6qzbs=
|
||||
go.opentelemetry.io/otel/sdk v1.36.0 h1:b6SYIuLRs88ztox4EyrvRti80uXIFy+Sqzoh9kFULbs=
|
||||
go.opentelemetry.io/otel/sdk v1.36.0/go.mod h1:+lC+mTgD+MUWfjJubi2vvXWcVxyr9rmlshZni72pXeY=
|
||||
go.opentelemetry.io/otel/sdk/log v0.12.2 h1:yNoETvTByVKi7wHvYS6HMcZrN5hFLD7I++1xIZ/k6W0=
|
||||
go.opentelemetry.io/otel/sdk/log v0.12.2/go.mod h1:DcpdmUXHJgSqN/dh+XMWa7Vf89u9ap0/AAk/XGLnEzY=
|
||||
go.opentelemetry.io/otel/sdk/log/logtest v0.0.0-20250521073539-a85ae98dcedc h1:uqxdywfHqqCl6LmZzI3pUnXT1RGFYyUgxj0AkWPFxi0=
|
||||
go.opentelemetry.io/otel/sdk/log/logtest v0.0.0-20250521073539-a85ae98dcedc/go.mod h1:TY/N/FT7dmFrP/r5ym3g0yysP1DefqGpAZr4f82P0dE=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.36.0 h1:r0ntwwGosWGaa0CrSt8cuNuTcccMXERFwHX4dThiPis=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.36.0/go.mod h1:qTNOhFDfKRwX0yXOqJYegL5WRaW376QbB7P4Pb0qva4=
|
||||
go.opentelemetry.io/otel/trace v1.36.0 h1:ahxWNuqZjpdiFAyrIoQ4GIiAIhxAunQR6MUoKrsNd4w=
|
||||
go.opentelemetry.io/otel/trace v1.36.0/go.mod h1:gQ+OnDZzrybY4k4seLzPAWNwVBBVlF2szhehOBB/tGA=
|
||||
go.opentelemetry.io/proto/otlp v1.7.0 h1:jX1VolD6nHuFzOYso2E73H85i92Mv8JQYk0K9vz09os=
|
||||
go.opentelemetry.io/proto/otlp v1.7.0/go.mod h1:fSKjH6YJ7HDlwzltzyMj036AJ3ejJLCgCSHGj4efDDo=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
||||
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM=
|
||||
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc=
|
||||
golang.org/x/exp v0.0.0-20250530174510-65e920069ea6 h1:gllJVKwONftmCc4KlNbN8o/LvmbxotqQy6zzi6yDQOQ=
|
||||
golang.org/x/exp v0.0.0-20250530174510-65e920069ea6/go.mod h1:U6Lno4MTRCDY+Ba7aCcauB9T60gsv5s4ralQzP72ZoQ=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw=
|
||||
golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
|
||||
golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8=
|
||||
golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211013075003-97ac67df715c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
|
||||
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
|
||||
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y=
|
||||
golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g=
|
||||
golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg=
|
||||
golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
|
||||
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
|
||||
golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
|
||||
golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250528174236-200df99c418a h1:SGktgSolFCo75dnHJF2yMvnns6jCmHFJ0vE4Vn2JKvQ=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250528174236-200df99c418a/go.mod h1:a77HrdMjoeKbnd2jmgcWdaS++ZLZAEq3orIOAEIKiVw=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250528174236-200df99c418a h1:v2PbRU4K3llS09c7zodFpNePeamkAwG3mPrAery9VeE=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250528174236-200df99c418a/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
|
||||
google.golang.org/grpc v1.73.0 h1:VIWSmpI2MegBtTuFt5/JWy2oXxtjJ/e89Z70ImfD2ok=
|
||||
google.golang.org/grpc v1.73.0/go.mod h1:50sbHOUqWoCQGI8V2HQLJM0B+LMlIUjNSZmow7EVBQc=
|
||||
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
|
||||
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
|
|
|
@ -199,10 +199,10 @@ func renderFlatDiff(changes []manifest.Change, cmd *cobra.Command) error {
|
|||
func renderJSONDiff(changes []manifest.Change, cmd *cobra.Command) error {
|
||||
// Create a structured response that can be easily consumed by tools
|
||||
type structuredOutput struct {
|
||||
TotalChanges int `json:"totalChanges" yaml:"totalChanges"`
|
||||
Additions []manifest.Change `json:"additions" yaml:"additions"`
|
||||
Removals []manifest.Change `json:"removals" yaml:"removals"`
|
||||
Modifications []manifest.Change `json:"modifications" yaml:"modifications"`
|
||||
TotalChanges int `json:"totalChanges" yaml:"totalChanges"`
|
||||
Additions []manifest.Change `json:"additions" yaml:"additions"`
|
||||
Removals []manifest.Change `json:"removals" yaml:"removals"`
|
||||
Modifications []manifest.Change `json:"modifications" yaml:"modifications"`
|
||||
}
|
||||
|
||||
// Group changes by type
|
||||
|
@ -235,10 +235,10 @@ func renderJSONDiff(changes []manifest.Change, cmd *cobra.Command) error {
|
|||
func renderYAMLDiff(changes []manifest.Change, cmd *cobra.Command) error {
|
||||
// Use the same structured output type as JSON but with YAML tags
|
||||
type structuredOutput struct {
|
||||
TotalChanges int `json:"totalChanges" yaml:"totalChanges"`
|
||||
Additions []manifest.Change `json:"additions" yaml:"additions"`
|
||||
Removals []manifest.Change `json:"removals" yaml:"removals"`
|
||||
Modifications []manifest.Change `json:"modifications" yaml:"modifications"`
|
||||
TotalChanges int `json:"totalChanges" yaml:"totalChanges"`
|
||||
Additions []manifest.Change `json:"additions" yaml:"additions"`
|
||||
Removals []manifest.Change `json:"removals" yaml:"removals"`
|
||||
Modifications []manifest.Change `json:"modifications" yaml:"modifications"`
|
||||
}
|
||||
|
||||
// Group changes by type
|
|
@ -153,6 +153,38 @@ namespace TestNamespace
|
|||
return await _client.GetStringDetailsAsync("greetingMessage", "Hello there!", evaluationContext, options);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Allows customization of theme colors.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>Flag key: themeCustomization</para>
|
||||
/// <para>Default value: new Value(Structure.Builder().Set("primaryColor", "#007bff").Set("secondaryColor", "#6c757d").Build())</para>
|
||||
/// <para>Type: object</para>
|
||||
/// </remarks>
|
||||
/// <param name="evaluationContext">Optional context for the flag evaluation</param>
|
||||
/// <param name="options">Options for flag evaluation</param>
|
||||
/// <returns>The flag value</returns>
|
||||
public async Task<Value> ThemeCustomizationAsync(EvaluationContext? evaluationContext = null, FlagEvaluationOptions? options = null)
|
||||
{
|
||||
return await _client.GetObjectValueAsync("themeCustomization", new Value(Structure.Builder().Set("primaryColor", "#007bff").Set("secondaryColor", "#6c757d").Build()), evaluationContext, options);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Allows customization of theme colors.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>Flag key: themeCustomization</para>
|
||||
/// <para>Default value: new Value(Structure.Builder().Set("primaryColor", "#007bff").Set("secondaryColor", "#6c757d").Build())</para>
|
||||
/// <para>Type: object</para>
|
||||
/// </remarks>
|
||||
/// <param name="evaluationContext">Optional context for the flag evaluation</param>
|
||||
/// <param name="options">Options for flag evaluation</param>
|
||||
/// <returns>The evaluation details containing the flag value and metadata</returns>
|
||||
public async Task<FlagEvaluationDetails<Value>> ThemeCustomizationDetailsAsync(EvaluationContext? evaluationContext = null, FlagEvaluationOptions? options = null)
|
||||
{
|
||||
return await _client.GetObjectDetailsAsync("themeCustomization", new Value(Structure.Builder().Set("primaryColor", "#007bff").Set("secondaryColor", "#6c757d").Build()), evaluationContext, options);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Maximum allowed length for usernames.
|
||||
/// </summary>
|
|
@ -14,6 +14,8 @@ type IntProvider func(ctx context.Context, evalCtx openfeature.EvaluationContext
|
|||
type IntProviderDetails func(ctx context.Context, evalCtx openfeature.EvaluationContext) (openfeature.IntEvaluationDetails, error)
|
||||
type StringProvider func(ctx context.Context, evalCtx openfeature.EvaluationContext) (string, error)
|
||||
type StringProviderDetails func(ctx context.Context, evalCtx openfeature.EvaluationContext) (openfeature.StringEvaluationDetails, error)
|
||||
type ObjectProvider func(ctx context.Context, evalCtx openfeature.EvaluationContext) (any, error)
|
||||
type ObjectProviderDetails func(ctx context.Context, evalCtx openfeature.EvaluationContext) (openfeature.InterfaceEvaluationDetails, error)
|
||||
|
||||
var client openfeature.IClient = nil
|
||||
// Discount percentage applied to purchases.
|
||||
|
@ -67,6 +69,23 @@ var GreetingMessage = struct {
|
|||
return client.StringValueDetails(ctx, "greetingMessage", "Hello there!", evalCtx)
|
||||
},
|
||||
}
|
||||
// Allows customization of theme colors.
|
||||
var ThemeCustomization = struct {
|
||||
// Value returns the value of the flag ThemeCustomization,
|
||||
// as well as the evaluation error, if present.
|
||||
Value ObjectProvider
|
||||
|
||||
// ValueWithDetails returns the value of the flag ThemeCustomization,
|
||||
// the evaluation error, if any, and the evaluation details.
|
||||
ValueWithDetails ObjectProviderDetails
|
||||
}{
|
||||
Value: func(ctx context.Context, evalCtx openfeature.EvaluationContext) (any, error) {
|
||||
return client.ObjectValue(ctx, "themeCustomization", map[string]any{"primaryColor": "#007bff", "secondaryColor": "#6c757d"}, evalCtx)
|
||||
},
|
||||
ValueWithDetails: func(ctx context.Context, evalCtx openfeature.EvaluationContext) (openfeature.InterfaceEvaluationDetails, error){
|
||||
return client.ObjectValueDetails(ctx, "themeCustomization", map[string]any{"primaryColor": "#007bff", "secondaryColor": "#6c757d"}, evalCtx)
|
||||
},
|
||||
}
|
||||
// Maximum allowed length for usernames.
|
||||
var UsernameMaxLength = struct {
|
||||
// Value returns the value of the flag UsernameMaxLength,
|
|
@ -71,7 +71,27 @@ public final class OpenFeature {
|
|||
* Returns the evaluation details containing the flag value and metadata
|
||||
*/
|
||||
FlagEvaluationDetails<String> greetingMessageDetails(EvaluationContext ctx);
|
||||
|
||||
/**
|
||||
* Allows customization of theme colors.
|
||||
* Details:
|
||||
* - Flag key: themeCustomization
|
||||
* - Type: Object
|
||||
* - Default value: Map.of("primaryColor", "#007bff", "secondaryColor", "#6c757d")
|
||||
* Returns the flag value
|
||||
*/
|
||||
Object themeCustomization(EvaluationContext ctx);
|
||||
|
||||
/**
|
||||
* Allows customization of theme colors.
|
||||
* Details:
|
||||
* - Flag key: themeCustomization
|
||||
* - Type: Object
|
||||
* - Default value: Map.of("primaryColor", "#007bff", "secondaryColor", "#6c757d")
|
||||
* Returns the evaluation details containing the flag value and metadata
|
||||
*/
|
||||
FlagEvaluationDetails<Object> themeCustomizationDetails(EvaluationContext ctx);
|
||||
|
||||
/**
|
||||
* Maximum allowed length for usernames.
|
||||
* Details:
|
||||
|
@ -131,7 +151,17 @@ public final class OpenFeature {
|
|||
public FlagEvaluationDetails<String> greetingMessageDetails(EvaluationContext ctx) {
|
||||
return client.getStringDetails("greetingMessage", "Hello there!", ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object themeCustomization(EvaluationContext ctx) {
|
||||
return client.getObjectValue("themeCustomization", Map.of("primaryColor", "#007bff", "secondaryColor", "#6c757d"), ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FlagEvaluationDetails<Object> themeCustomizationDetails(EvaluationContext ctx) {
|
||||
return client.getObjectDetails("themeCustomization", Map.of("primaryColor", "#007bff", "secondaryColor", "#6c757d"), ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer usernameMaxLength(EvaluationContext ctx) {
|
||||
return client.getIntegerValue("usernameMaxLength", 50, ctx);
|
|
@ -8,8 +8,9 @@ import type {
|
|||
EvaluationContext,
|
||||
EvaluationDetails,
|
||||
OpenFeatureModuleOptions,
|
||||
JsonValue
|
||||
} from "@openfeature/nestjs-sdk";
|
||||
import { OpenFeatureModule, BooleanFeatureFlag, StringFeatureFlag, NumberFeatureFlag } from "@openfeature/nestjs-sdk";
|
||||
import { OpenFeatureModule, BooleanFeatureFlag, StringFeatureFlag, NumberFeatureFlag, ObjectFeatureFlag } from "@openfeature/nestjs-sdk";
|
||||
|
||||
import type { GeneratedClient } from "./openfeature";
|
||||
import { getGeneratedClient } from "./openfeature";
|
||||
|
@ -133,7 +134,7 @@ export function DiscountPercentage(props?: TypedFeatureProps): ParameterDecorato
|
|||
* @Get("/")
|
||||
* public async handleRequest(
|
||||
* @EnableFeatureA()
|
||||
* enableFeatureA: Observable<EvaluationDetails<number>>,
|
||||
* enableFeatureA: Observable<EvaluationDetails<boolean>>,
|
||||
* )
|
||||
* ```
|
||||
* @param {TypedFeatureProps} props The options for injecting the feature flag.
|
||||
|
@ -158,7 +159,7 @@ export function EnableFeatureA(props?: TypedFeatureProps): ParameterDecorator {
|
|||
* @Get("/")
|
||||
* public async handleRequest(
|
||||
* @GreetingMessage()
|
||||
* greetingMessage: Observable<EvaluationDetails<number>>,
|
||||
* greetingMessage: Observable<EvaluationDetails<string>>,
|
||||
* )
|
||||
* ```
|
||||
* @param {TypedFeatureProps} props The options for injecting the feature flag.
|
||||
|
@ -168,6 +169,31 @@ export function GreetingMessage(props?: TypedFeatureProps): ParameterDecorator {
|
|||
return StringFeatureFlag({ flagKey: "greetingMessage", defaultValue: "Hello there!", ...props });
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the {@link EvaluationDetails} for `themeCustomization` from a domain scoped or the default OpenFeature
|
||||
* client and populates the annotated parameter with the {@link EvaluationDetails} wrapped in an {@link Observable}.
|
||||
*
|
||||
* **Details:**
|
||||
* - flag key: `themeCustomization`
|
||||
* - description: `Allows customization of theme colors.`
|
||||
* - default value: `{"primaryColor":"#007bff","secondaryColor":"#6c757d"}`
|
||||
* - type: `JsonValue`
|
||||
*
|
||||
* Usage:
|
||||
* ```typescript
|
||||
* @Get("/")
|
||||
* public async handleRequest(
|
||||
* @ThemeCustomization()
|
||||
* themeCustomization: Observable<EvaluationDetails<JsonValue>>,
|
||||
* )
|
||||
* ```
|
||||
* @param {TypedFeatureProps} props The options for injecting the feature flag.
|
||||
* @returns {ParameterDecorator} The decorator function.
|
||||
*/
|
||||
export function ThemeCustomization(props?: TypedFeatureProps): ParameterDecorator {
|
||||
return ObjectFeatureFlag({ flagKey: "themeCustomization", defaultValue: {"primaryColor":"#007bff","secondaryColor":"#6c757d"}, ...props });
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the {@link EvaluationDetails} for `usernameMaxLength` from a domain scoped or the default OpenFeature
|
||||
* client and populates the annotated parameter with the {@link EvaluationDetails} wrapped in an {@link Observable}.
|
|
@ -3,6 +3,7 @@ import {
|
|||
OpenFeature,
|
||||
stringOrUndefined,
|
||||
objectOrUndefined,
|
||||
JsonValue,
|
||||
} from "@openfeature/server-sdk";
|
||||
import type {
|
||||
EvaluationContext,
|
||||
|
@ -101,6 +102,36 @@ export interface GeneratedClient {
|
|||
*/
|
||||
greetingMessageDetails(context?: EvaluationContext, options?: FlagEvaluationOptions): Promise<EvaluationDetails<string>>;
|
||||
|
||||
/**
|
||||
* Allows customization of theme colors.
|
||||
*
|
||||
* **Details:**
|
||||
* - flag key: `themeCustomization`
|
||||
* - default value: `{"primaryColor":"#007bff","secondaryColor":"#6c757d"}`
|
||||
* - type: `JsonValue`
|
||||
*
|
||||
* Performs a flag evaluation that returns a object.
|
||||
* @param {EvaluationContext} context The evaluation context used on an individual flag evaluation
|
||||
* @param {FlagEvaluationOptions} options Additional flag evaluation options
|
||||
* @returns {Promise<JsonValue>} Flag evaluation response
|
||||
*/
|
||||
themeCustomization(context?: EvaluationContext, options?: FlagEvaluationOptions): Promise<JsonValue>;
|
||||
|
||||
/**
|
||||
* Allows customization of theme colors.
|
||||
*
|
||||
* **Details:**
|
||||
* - flag key: `themeCustomization`
|
||||
* - default value: `{"primaryColor":"#007bff","secondaryColor":"#6c757d"}`
|
||||
* - type: `JsonValue`
|
||||
*
|
||||
* Performs a flag evaluation that a returns an evaluation details object.
|
||||
* @param {EvaluationContext} context The evaluation context used on an individual flag evaluation
|
||||
* @param {FlagEvaluationOptions} options Additional flag evaluation options
|
||||
* @returns {Promise<EvaluationDetails<JsonValue>>} Flag evaluation details response
|
||||
*/
|
||||
themeCustomizationDetails(context?: EvaluationContext, options?: FlagEvaluationOptions): Promise<EvaluationDetails<JsonValue>>;
|
||||
|
||||
/**
|
||||
* Maximum allowed length for usernames.
|
||||
*
|
||||
|
@ -185,6 +216,14 @@ export function getGeneratedClient(domainOrContext?: string | EvaluationContext,
|
|||
return client.getStringDetails("greetingMessage", "Hello there!", context, options);
|
||||
},
|
||||
|
||||
themeCustomization: (context?: EvaluationContext, options?: FlagEvaluationOptions): Promise<JsonValue> => {
|
||||
return client.getObjectValue("themeCustomization", {"primaryColor":"#007bff","secondaryColor":"#6c757d"}, context, options);
|
||||
},
|
||||
|
||||
themeCustomizationDetails: (context?: EvaluationContext, options?: FlagEvaluationOptions): Promise<EvaluationDetails<JsonValue>> => {
|
||||
return client.getObjectDetails("themeCustomization", {"primaryColor":"#007bff","secondaryColor":"#6c757d"}, context, options);
|
||||
},
|
||||
|
||||
usernameMaxLength: (context?: EvaluationContext, options?: FlagEvaluationOptions): Promise<number> => {
|
||||
return client.getNumberValue("usernameMaxLength", 50, context, options);
|
||||
},
|
|
@ -278,6 +278,94 @@ class GeneratedClient:
|
|||
flag_evaluation_options=flag_evaluation_options,
|
||||
)
|
||||
|
||||
def theme_customization(
|
||||
self,
|
||||
evaluation_context: Optional[EvaluationContext] = None,
|
||||
flag_evaluation_options: Optional[FlagEvaluationOptions] = None,
|
||||
) -> object:
|
||||
"""
|
||||
Allows customization of theme colors.
|
||||
|
||||
**Details:**
|
||||
- flag key: `themeCustomization`
|
||||
- default value: `{"primaryColor": "#007bff", "secondaryColor": "#6c757d"}`
|
||||
- type: `object`
|
||||
|
||||
Performs a flag evaluation that returns a `object`.
|
||||
"""
|
||||
return self.client.get_object_value(
|
||||
flag_key="themeCustomization",
|
||||
default_value={"primaryColor": "#007bff", "secondaryColor": "#6c757d"},
|
||||
evaluation_context=evaluation_context,
|
||||
flag_evaluation_options=flag_evaluation_options,
|
||||
)
|
||||
|
||||
def theme_customization_details(
|
||||
self,
|
||||
evaluation_context: Optional[EvaluationContext] = None,
|
||||
flag_evaluation_options: Optional[FlagEvaluationOptions] = None,
|
||||
) -> FlagEvaluationDetails:
|
||||
"""
|
||||
Allows customization of theme colors.
|
||||
|
||||
**Details:**
|
||||
- flag key: `themeCustomization`
|
||||
- default value: `{"primaryColor": "#007bff", "secondaryColor": "#6c757d"}`
|
||||
- type: `object`
|
||||
|
||||
Performs a flag evaluation that returns a `FlagEvaluationDetails` instance.
|
||||
"""
|
||||
return self.client.get_object_details(
|
||||
flag_key="themeCustomization",
|
||||
default_value={"primaryColor": "#007bff", "secondaryColor": "#6c757d"},
|
||||
evaluation_context=evaluation_context,
|
||||
flag_evaluation_options=flag_evaluation_options,
|
||||
)
|
||||
|
||||
async def theme_customization_async(
|
||||
self,
|
||||
evaluation_context: Optional[EvaluationContext] = None,
|
||||
flag_evaluation_options: Optional[FlagEvaluationOptions] = None,
|
||||
) -> object:
|
||||
"""
|
||||
Allows customization of theme colors.
|
||||
|
||||
**Details:**
|
||||
- flag key: `themeCustomization`
|
||||
- default value: `{"primaryColor": "#007bff", "secondaryColor": "#6c757d"}`
|
||||
- type: `object`
|
||||
|
||||
Performs a flag evaluation asynchronously and returns a `object`.
|
||||
"""
|
||||
return await self.client.get_object_value_async(
|
||||
flag_key="themeCustomization",
|
||||
default_value={"primaryColor": "#007bff", "secondaryColor": "#6c757d"},
|
||||
evaluation_context=evaluation_context,
|
||||
flag_evaluation_options=flag_evaluation_options,
|
||||
)
|
||||
|
||||
async def theme_customization_details_async(
|
||||
self,
|
||||
evaluation_context: Optional[EvaluationContext] = None,
|
||||
flag_evaluation_options: Optional[FlagEvaluationOptions] = None,
|
||||
) -> FlagEvaluationDetails:
|
||||
"""
|
||||
Allows customization of theme colors.
|
||||
|
||||
**Details:**
|
||||
- flag key: `themeCustomization`
|
||||
- default value: `{"primaryColor": "#007bff", "secondaryColor": "#6c757d"}`
|
||||
- type: `object`
|
||||
|
||||
Performs a flag evaluation asynchronously and returns a `FlagEvaluationDetails` instance.
|
||||
"""
|
||||
return await self.client.get_object_details_async(
|
||||
flag_key="themeCustomization",
|
||||
default_value={"primaryColor": "#007bff", "secondaryColor": "#6c757d"},
|
||||
evaluation_context=evaluation_context,
|
||||
flag_evaluation_options=flag_evaluation_options,
|
||||
)
|
||||
|
||||
def username_max_length(
|
||||
self,
|
||||
evaluation_context: Optional[EvaluationContext] = None,
|
|
@ -5,6 +5,7 @@ import {
|
|||
type ReactFlagEvaluationNoSuspenseOptions,
|
||||
useFlag,
|
||||
useSuspenseFlag,
|
||||
JsonValue
|
||||
} from "@openfeature/react-sdk";
|
||||
|
||||
/**
|
||||
|
@ -88,6 +89,33 @@ export const useSuspenseGreetingMessage = (options?: ReactFlagEvaluationNoSuspen
|
|||
return useSuspenseFlag("greetingMessage", "Hello there!", options);
|
||||
};
|
||||
|
||||
/**
|
||||
* Allows customization of theme colors.
|
||||
*
|
||||
* **Details:**
|
||||
* - flag key: `themeCustomization`
|
||||
* - default value: `{"primaryColor":"#007bff","secondaryColor":"#6c757d"}`
|
||||
* - type: `JsonValue`
|
||||
*/
|
||||
export const useThemeCustomization = (options?: ReactFlagEvaluationOptions) => {
|
||||
return useFlag("themeCustomization", {"primaryColor":"#007bff","secondaryColor":"#6c757d"}, options);
|
||||
};
|
||||
|
||||
/**
|
||||
* Allows customization of theme colors.
|
||||
*
|
||||
* **Details:**
|
||||
* - flag key: `themeCustomization`
|
||||
* - default value: `{"primaryColor":"#007bff","secondaryColor":"#6c757d"}`
|
||||
* - type: `JsonValue`
|
||||
*
|
||||
* Equivalent to useFlag with options: `{ suspend: true }`
|
||||
* @experimental — Suspense is an experimental feature subject to change in future versions.
|
||||
*/
|
||||
export const useSuspenseThemeCustomization = (options?: ReactFlagEvaluationNoSuspenseOptions) => {
|
||||
return useSuspenseFlag("themeCustomization", {"primaryColor":"#007bff","secondaryColor":"#6c757d"}, options);
|
||||
};
|
||||
|
||||
/**
|
||||
* Maximum allowed length for usernames.
|
||||
*
|
|
@ -5,6 +5,7 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/open-feature/cli/internal/filesystem"
|
||||
"github.com/open-feature/cli/internal/manifest"
|
||||
|
@ -64,7 +65,7 @@ func Load(manifestPath string) (*Flagset, error) {
|
|||
if err != nil {
|
||||
return nil, err
|
||||
} else if len(validationErrors) > 0 {
|
||||
return nil, fmt.Errorf("validation failed: %v", validationErrors)
|
||||
return nil, errors.New(FormatValidationError(validationErrors))
|
||||
}
|
||||
|
||||
var flagset Flagset
|
||||
|
@ -132,3 +133,43 @@ func (fs *Flagset) UnmarshalJSON(data []byte) error {
|
|||
|
||||
return nil
|
||||
}
|
||||
func FormatValidationError(issues []manifest.ValidationError) string {
|
||||
var sb strings.Builder
|
||||
sb.WriteString("flag manifest validation failed:\n\n")
|
||||
|
||||
// Group messages by flag path
|
||||
grouped := make(map[string]struct {
|
||||
flagType string
|
||||
messages []string
|
||||
})
|
||||
|
||||
for _, issue := range issues {
|
||||
entry := grouped[issue.Path]
|
||||
entry.flagType = issue.Type
|
||||
entry.messages = append(entry.messages, issue.Message)
|
||||
grouped[issue.Path] = entry
|
||||
}
|
||||
|
||||
// Sort paths for consistent output
|
||||
paths := make([]string, 0, len(grouped))
|
||||
for path := range grouped {
|
||||
paths = append(paths, path)
|
||||
}
|
||||
sort.Strings(paths)
|
||||
|
||||
// Format each row
|
||||
for _, path := range paths {
|
||||
entry := grouped[path]
|
||||
flagType := entry.flagType
|
||||
if flagType == "" {
|
||||
flagType = "missing"
|
||||
}
|
||||
sb.WriteString(fmt.Sprintf(
|
||||
"- flagType: %s\n flagPath: %s\n errors:\n ~ %s\n \tSuggestions:\n \t- flagType: boolean\n \t- defaultValue: true\n\n",
|
||||
flagType,
|
||||
path,
|
||||
strings.Join(entry.messages, "\n ~ "),
|
||||
))
|
||||
}
|
||||
return sb.String()
|
||||
}
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
package flagset
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/open-feature/cli/internal/manifest"
|
||||
)
|
||||
|
||||
// Sample test for FormatValidationError
|
||||
func TestFormatValidationError_SortsByPath(t *testing.T) {
|
||||
issues := []manifest.ValidationError{
|
||||
{Path: "zeta.flag", Type: "boolean", Message: "must not be empty"},
|
||||
{Path: "alpha.flag", Type: "string", Message: "invalid value"},
|
||||
{Path: "beta.flag", Type: "number", Message: "must be greater than zero"},
|
||||
}
|
||||
|
||||
output := FormatValidationError(issues)
|
||||
|
||||
// The output should mention 'alpha.flag' before 'beta.flag', and 'beta.flag' before 'zeta.flag'
|
||||
alphaIdx := strings.Index(output, "flagPath: alpha.flag")
|
||||
betaIdx := strings.Index(output, "flagPath: beta.flag")
|
||||
zetaIdx := strings.Index(output, "flagPath: zeta.flag")
|
||||
|
||||
if !(alphaIdx < betaIdx && betaIdx < zetaIdx) {
|
||||
t.Errorf("flag paths are not sorted: alphaIdx=%d, betaIdx=%d, zetaIdx=%d\nOutput:\n%s",
|
||||
alphaIdx, betaIdx, zetaIdx, output)
|
||||
}
|
||||
}
|
|
@ -3,6 +3,9 @@ package csharp
|
|||
import (
|
||||
_ "embed"
|
||||
"fmt"
|
||||
"maps"
|
||||
"slices"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
"github.com/open-feature/cli/internal/flagset"
|
||||
|
@ -31,6 +34,8 @@ func openFeatureType(t flagset.FlagType) string {
|
|||
return "bool"
|
||||
case flagset.StringType:
|
||||
return "string"
|
||||
case flagset.ObjectType:
|
||||
return "object"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
|
@ -50,10 +55,77 @@ func formatDefaultValue(flag flagset.Flag) string {
|
|||
}
|
||||
}
|
||||
|
||||
func toCSharpDict(value any) string {
|
||||
assertedMap, ok := value.(map[string]any)
|
||||
if !ok {
|
||||
return "null"
|
||||
}
|
||||
|
||||
keys := slices.Sorted(maps.Keys(assertedMap))
|
||||
|
||||
var builder strings.Builder
|
||||
builder.WriteString("new Value(Structure.Builder()")
|
||||
|
||||
for _, key := range keys {
|
||||
val := assertedMap[key]
|
||||
|
||||
builder.WriteString(fmt.Sprintf(".Set(%q, %s)", key, formatNestedValue(val)))
|
||||
}
|
||||
builder.WriteString(".Build())")
|
||||
|
||||
return builder.String()
|
||||
}
|
||||
|
||||
func formatNestedValue(value any) string {
|
||||
switch val := value.(type) {
|
||||
case string:
|
||||
flag := flagset.Flag{
|
||||
Type: flagset.StringType,
|
||||
DefaultValue: val,
|
||||
}
|
||||
return formatDefaultValue(flag)
|
||||
case bool:
|
||||
flag := flagset.Flag{
|
||||
Type: flagset.BoolType,
|
||||
DefaultValue: val,
|
||||
}
|
||||
return formatDefaultValue(flag)
|
||||
case int, int64:
|
||||
flag := flagset.Flag{
|
||||
Type: flagset.IntType,
|
||||
DefaultValue: val,
|
||||
}
|
||||
return formatDefaultValue(flag)
|
||||
case float64:
|
||||
flag := flagset.Flag{
|
||||
Type: flagset.FloatType,
|
||||
DefaultValue: val,
|
||||
}
|
||||
return formatDefaultValue(flag)
|
||||
case map[string]any:
|
||||
return toCSharpDict(val)
|
||||
case []any:
|
||||
var sliceBuilder strings.Builder
|
||||
sliceBuilder.WriteString("new Value(new List<Value>{")
|
||||
for index, elem := range val {
|
||||
if index > 0 {
|
||||
sliceBuilder.WriteString(", ")
|
||||
}
|
||||
|
||||
sliceBuilder.WriteString(formatNestedValue(elem))
|
||||
}
|
||||
sliceBuilder.WriteString("})")
|
||||
return sliceBuilder.String()
|
||||
default:
|
||||
return fmt.Sprintf("new Value(%s)", val)
|
||||
}
|
||||
}
|
||||
|
||||
func (g *CsharpGenerator) Generate(params *generators.Params[Params]) error {
|
||||
funcs := template.FuncMap{
|
||||
"OpenFeatureType": openFeatureType,
|
||||
"FormatDefaultValue": formatDefaultValue,
|
||||
"ToCSharpDict": toCSharpDict,
|
||||
}
|
||||
|
||||
newParams := &generators.Params[any]{
|
||||
|
@ -67,8 +139,6 @@ func (g *CsharpGenerator) Generate(params *generators.Params[Params]) error {
|
|||
// NewGenerator creates a generator for C#.
|
||||
func NewGenerator(fs *flagset.Flagset) *CsharpGenerator {
|
||||
return &CsharpGenerator{
|
||||
CommonGenerator: *generators.NewGenerator(fs, map[flagset.FlagType]bool{
|
||||
flagset.ObjectType: true,
|
||||
}),
|
||||
CommonGenerator: *generators.NewGenerator(fs, map[flagset.FlagType]bool{}),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -64,13 +64,13 @@ namespace {{ if .Params.Custom.Namespace }}{{ .Params.Custom.Namespace }}{{ else
|
|||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>Flag key: {{ .Key }}</para>
|
||||
/// <para>Default value: {{ .DefaultValue }}</para>
|
||||
/// <para>Default value: {{ if eq (.Type | OpenFeatureType) "object" }}{{ .DefaultValue | ToCSharpDict }}{{ else }}{{ .DefaultValue }}{{ end }}</para>
|
||||
/// <para>Type: {{ .Type | OpenFeatureType }}</para>
|
||||
/// </remarks>
|
||||
/// <param name="evaluationContext">Optional context for the flag evaluation</param>
|
||||
/// <param name="options">Options for flag evaluation</param>
|
||||
/// <returns>The flag value</returns>
|
||||
public async Task<{{ .Type | OpenFeatureType }}> {{ .Key | ToPascal }}Async(EvaluationContext? evaluationContext = null, FlagEvaluationOptions? options = null)
|
||||
public async Task<{{ if eq (.Type | OpenFeatureType) "object" }}Value{{ else }}{{ .Type | OpenFeatureType }}{{ end }}> {{ .Key | ToPascal }}Async(EvaluationContext? evaluationContext = null, FlagEvaluationOptions? options = null)
|
||||
{
|
||||
{{- if eq .Type 1 }}
|
||||
return await _client.GetIntegerValueAsync("{{ .Key }}", {{ . | FormatDefaultValue }}, evaluationContext, options);
|
||||
|
@ -80,6 +80,8 @@ namespace {{ if .Params.Custom.Namespace }}{{ .Params.Custom.Namespace }}{{ else
|
|||
return await _client.GetBooleanValueAsync("{{ .Key }}", {{ . | FormatDefaultValue }}, evaluationContext, options);
|
||||
{{- else if eq .Type 4 }}
|
||||
return await _client.GetStringValueAsync("{{ .Key }}", {{ . | FormatDefaultValue }}, evaluationContext, options);
|
||||
{{- else if eq .Type 5 }}
|
||||
return await _client.GetObjectValueAsync("{{ .Key }}", {{ if eq (.Type | OpenFeatureType) "object" }}{{ .DefaultValue | ToCSharpDict }}{{ else }}{{ . | FormatDefaultValue }}{{ end }}, evaluationContext, options);
|
||||
{{- else }}
|
||||
throw new NotSupportedException("Unsupported flag type");
|
||||
{{- end }}
|
||||
|
@ -90,13 +92,13 @@ namespace {{ if .Params.Custom.Namespace }}{{ .Params.Custom.Namespace }}{{ else
|
|||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>Flag key: {{ .Key }}</para>
|
||||
/// <para>Default value: {{ .DefaultValue }}</para>
|
||||
/// <para>Default value: {{ if eq (.Type | OpenFeatureType) "object" }}{{ .DefaultValue | ToCSharpDict }}{{ else }}{{ .DefaultValue }}{{ end }}</para>
|
||||
/// <para>Type: {{ .Type | OpenFeatureType }}</para>
|
||||
/// </remarks>
|
||||
/// <param name="evaluationContext">Optional context for the flag evaluation</param>
|
||||
/// <param name="options">Options for flag evaluation</param>
|
||||
/// <returns>The evaluation details containing the flag value and metadata</returns>
|
||||
public async Task<FlagEvaluationDetails<{{ .Type | OpenFeatureType }}>> {{ .Key | ToPascal }}DetailsAsync(EvaluationContext? evaluationContext = null, FlagEvaluationOptions? options = null)
|
||||
public async Task<FlagEvaluationDetails<{{ if eq (.Type | OpenFeatureType) "object" }}Value{{ else }}{{ .Type | OpenFeatureType }}{{ end }}>> {{ .Key | ToPascal }}DetailsAsync(EvaluationContext? evaluationContext = null, FlagEvaluationOptions? options = null)
|
||||
{
|
||||
{{- if eq .Type 1 }}
|
||||
return await _client.GetIntegerDetailsAsync("{{ .Key }}", {{ . | FormatDefaultValue }}, evaluationContext, options);
|
||||
|
@ -106,6 +108,8 @@ namespace {{ if .Params.Custom.Namespace }}{{ .Params.Custom.Namespace }}{{ else
|
|||
return await _client.GetBooleanDetailsAsync("{{ .Key }}", {{ . | FormatDefaultValue }}, evaluationContext, options);
|
||||
{{- else if eq .Type 4 }}
|
||||
return await _client.GetStringDetailsAsync("{{ .Key }}", {{ . | FormatDefaultValue }}, evaluationContext, options);
|
||||
{{- else if eq .Type 5 }}
|
||||
return await _client.GetObjectDetailsAsync("{{ .Key }}", {{ if eq (.Type | OpenFeatureType) "object" }}{{ .DefaultValue | ToCSharpDict }}{{ else }}{{ . | FormatDefaultValue }}{{ end }}, evaluationContext, options);
|
||||
{{- else }}
|
||||
throw new NotSupportedException("Unsupported flag type");
|
||||
{{- end }}
|
||||
|
|
|
@ -2,7 +2,12 @@ package golang
|
|||
|
||||
import (
|
||||
_ "embed"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"maps"
|
||||
"slices"
|
||||
"sort"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
"github.com/open-feature/cli/internal/flagset"
|
||||
|
@ -30,6 +35,8 @@ func openFeatureType(t flagset.FlagType) string {
|
|||
return "Boolean"
|
||||
case flagset.StringType:
|
||||
return "String"
|
||||
case flagset.ObjectType:
|
||||
return "Object"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
|
@ -45,6 +52,8 @@ func typeString(flagType flagset.FlagType) string {
|
|||
return "bool"
|
||||
case flagset.FloatType:
|
||||
return "float64"
|
||||
case flagset.ObjectType:
|
||||
return "map[string]any"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
|
@ -60,11 +69,68 @@ func supportImports(flags []flagset.Flag) []string {
|
|||
return res
|
||||
}
|
||||
|
||||
func toMapLiteral(value any) string {
|
||||
assertedMap, ok := value.(map[string]any)
|
||||
if !ok {
|
||||
return "nil"
|
||||
}
|
||||
|
||||
// To have a determined order of the object for comparison
|
||||
keys := slices.Sorted(maps.Keys(assertedMap))
|
||||
|
||||
var builder strings.Builder
|
||||
builder.WriteString("map[string]any{")
|
||||
|
||||
for index, key := range keys {
|
||||
if index > 0 {
|
||||
builder.WriteString(", ")
|
||||
}
|
||||
val := assertedMap[key]
|
||||
|
||||
builder.WriteString(fmt.Sprintf(`%q: %s`, key, formatNestedValue(val)))
|
||||
}
|
||||
|
||||
builder.WriteString("}")
|
||||
return builder.String()
|
||||
}
|
||||
|
||||
func formatNestedValue(value any) string {
|
||||
switch val := value.(type) {
|
||||
case string:
|
||||
return fmt.Sprintf("%q", val)
|
||||
case bool:
|
||||
return fmt.Sprintf("%t", val)
|
||||
case int, int64, float64:
|
||||
return fmt.Sprintf("%v", val)
|
||||
case map[string]any:
|
||||
return toMapLiteral(val)
|
||||
case []any:
|
||||
var sliceBuilder strings.Builder
|
||||
sliceBuilder.WriteString("[]any{")
|
||||
for index, elem := range val {
|
||||
if index > 0 {
|
||||
sliceBuilder.WriteString(", ")
|
||||
}
|
||||
|
||||
sliceBuilder.WriteString(formatNestedValue(elem))
|
||||
}
|
||||
sliceBuilder.WriteString("}")
|
||||
return sliceBuilder.String()
|
||||
default:
|
||||
jsonBytes, err := json.Marshal(val)
|
||||
if err != nil {
|
||||
return "nil"
|
||||
}
|
||||
return fmt.Sprintf("%q", string(jsonBytes))
|
||||
}
|
||||
}
|
||||
|
||||
func (g *GolangGenerator) Generate(params *generators.Params[Params]) error {
|
||||
funcs := template.FuncMap{
|
||||
"SupportImports": supportImports,
|
||||
"OpenFeatureType": openFeatureType,
|
||||
"TypeString": typeString,
|
||||
"ToMapLiteral": toMapLiteral,
|
||||
}
|
||||
|
||||
newParams := &generators.Params[any]{
|
||||
|
@ -80,8 +146,6 @@ func (g *GolangGenerator) Generate(params *generators.Params[Params]) error {
|
|||
// NewGenerator creates a generator for Go.
|
||||
func NewGenerator(fs *flagset.Flagset) *GolangGenerator {
|
||||
return &GolangGenerator{
|
||||
CommonGenerator: *generators.NewGenerator(fs, map[flagset.FlagType]bool{
|
||||
flagset.ObjectType: true,
|
||||
}),
|
||||
CommonGenerator: *generators.NewGenerator(fs, map[flagset.FlagType]bool{}),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,6 +15,8 @@ type IntProvider func(ctx context.Context, evalCtx openfeature.EvaluationContext
|
|||
type IntProviderDetails func(ctx context.Context, evalCtx openfeature.EvaluationContext) (openfeature.IntEvaluationDetails, error)
|
||||
type StringProvider func(ctx context.Context, evalCtx openfeature.EvaluationContext) (string, error)
|
||||
type StringProviderDetails func(ctx context.Context, evalCtx openfeature.EvaluationContext) (openfeature.StringEvaluationDetails, error)
|
||||
type ObjectProvider func(ctx context.Context, evalCtx openfeature.EvaluationContext) (any, error)
|
||||
type ObjectProviderDetails func(ctx context.Context, evalCtx openfeature.EvaluationContext) (openfeature.InterfaceEvaluationDetails, error)
|
||||
|
||||
var client openfeature.IClient = nil
|
||||
|
||||
|
@ -29,11 +31,11 @@ var {{ .Key | ToPascal }} = struct {
|
|||
// the evaluation error, if any, and the evaluation details.
|
||||
ValueWithDetails {{ .Type | OpenFeatureType }}ProviderDetails
|
||||
}{
|
||||
Value: func(ctx context.Context, evalCtx openfeature.EvaluationContext) ({{ .Type | TypeString }}, error) {
|
||||
return client.{{ .Type | OpenFeatureType }}Value(ctx, {{ .Key | Quote }}, {{ .DefaultValue | QuoteString }}, evalCtx)
|
||||
Value: func(ctx context.Context, evalCtx openfeature.EvaluationContext) ({{- if eq (.Type | OpenFeatureType) "Object"}}any{{- else}}{{ .Type | TypeString }}{{- end}}, error) {
|
||||
return client.{{ .Type | OpenFeatureType }}Value(ctx, {{ .Key | Quote }}, {{ if eq (.Type | OpenFeatureType) "Object" }}{{.DefaultValue | ToMapLiteral }}{{- else }}{{ .DefaultValue | QuoteString }}{{- end}}, evalCtx)
|
||||
},
|
||||
ValueWithDetails: func(ctx context.Context, evalCtx openfeature.EvaluationContext) (openfeature.{{ .Type | OpenFeatureType }}EvaluationDetails, error){
|
||||
return client.{{ .Type | OpenFeatureType }}ValueDetails(ctx, {{ .Key | Quote }}, {{ .DefaultValue | QuoteString }}, evalCtx)
|
||||
ValueWithDetails: func(ctx context.Context, evalCtx openfeature.EvaluationContext) (openfeature.{{- if eq (.Type | OpenFeatureType) "Object"}}Interface{{- else }}{{ .Type | OpenFeatureType }}{{- end}}EvaluationDetails, error){
|
||||
return client.{{ .Type | OpenFeatureType }}ValueDetails(ctx, {{ .Key | Quote }}, {{ if eq (.Type | OpenFeatureType) "Object" }}{{.DefaultValue | ToMapLiteral }}{{- else }}{{ .DefaultValue | QuoteString }}{{- end}}, evalCtx)
|
||||
},
|
||||
}
|
||||
{{- end}}
|
||||
|
|
|
@ -2,7 +2,11 @@ package java
|
|||
|
||||
import (
|
||||
_ "embed"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"maps"
|
||||
"slices"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
"github.com/open-feature/cli/internal/flagset"
|
||||
|
@ -31,6 +35,8 @@ func openFeatureType(t flagset.FlagType) string {
|
|||
return "Boolean"
|
||||
case flagset.StringType:
|
||||
return "String"
|
||||
case flagset.ObjectType:
|
||||
return "Object"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
|
@ -50,10 +56,84 @@ func formatDefaultValueForJava(flag flagset.Flag) string {
|
|||
}
|
||||
}
|
||||
|
||||
func toMapLiteral(value any) string {
|
||||
assertedMap, ok := value.(map[string]any)
|
||||
if !ok {
|
||||
return "null"
|
||||
}
|
||||
|
||||
keys := slices.Sorted(maps.Keys(assertedMap))
|
||||
|
||||
var builder strings.Builder
|
||||
builder.WriteString("Map.of(")
|
||||
|
||||
for index, key := range keys {
|
||||
if index > 0 {
|
||||
builder.WriteString(", ")
|
||||
}
|
||||
val := assertedMap[key]
|
||||
|
||||
builder.WriteString(fmt.Sprintf("%q, %s", key, formatNestedValue(val)))
|
||||
}
|
||||
builder.WriteString(")")
|
||||
|
||||
return builder.String()
|
||||
}
|
||||
|
||||
func formatNestedValue(value any) string {
|
||||
switch val := value.(type) {
|
||||
case string:
|
||||
flag := flagset.Flag{
|
||||
Type: flagset.StringType,
|
||||
DefaultValue: val,
|
||||
}
|
||||
return formatDefaultValueForJava(flag)
|
||||
case bool:
|
||||
flag := flagset.Flag{
|
||||
Type: flagset.BoolType,
|
||||
DefaultValue: val,
|
||||
}
|
||||
return formatDefaultValueForJava(flag)
|
||||
case int, int64:
|
||||
flag := flagset.Flag{
|
||||
Type: flagset.IntType,
|
||||
DefaultValue: val,
|
||||
}
|
||||
return formatDefaultValueForJava(flag)
|
||||
case float64:
|
||||
flag := flagset.Flag{
|
||||
Type: flagset.FloatType,
|
||||
DefaultValue: val,
|
||||
}
|
||||
return formatDefaultValueForJava(flag)
|
||||
case map[string]any:
|
||||
return toMapLiteral(val)
|
||||
case []any:
|
||||
var sliceBuilder strings.Builder
|
||||
sliceBuilder.WriteString("List.of(")
|
||||
for index, elem := range val {
|
||||
if index > 0 {
|
||||
sliceBuilder.WriteString(", ")
|
||||
}
|
||||
|
||||
sliceBuilder.WriteString(formatNestedValue(elem))
|
||||
}
|
||||
sliceBuilder.WriteString(")")
|
||||
return sliceBuilder.String()
|
||||
default:
|
||||
jsonBytes, err := json.Marshal(val)
|
||||
if err != nil {
|
||||
return "null"
|
||||
}
|
||||
return fmt.Sprintf("%q", string(jsonBytes))
|
||||
}
|
||||
}
|
||||
|
||||
func (g *JavaGenerator) Generate(params *generators.Params[Params]) error {
|
||||
funcs := template.FuncMap{
|
||||
"OpenFeatureType": openFeatureType,
|
||||
"FormatDefaultValue": formatDefaultValueForJava,
|
||||
"ToMapLiteral": toMapLiteral,
|
||||
}
|
||||
|
||||
newParams := &generators.Params[any]{
|
||||
|
@ -67,8 +147,6 @@ func (g *JavaGenerator) Generate(params *generators.Params[Params]) error {
|
|||
// NewGenerator creates a generator for Java.
|
||||
func NewGenerator(fs *flagset.Flagset) *JavaGenerator {
|
||||
return &JavaGenerator{
|
||||
CommonGenerator: *generators.NewGenerator(fs, map[flagset.FlagType]bool{
|
||||
flagset.ObjectType: true,
|
||||
}),
|
||||
CommonGenerator: *generators.NewGenerator(fs, map[flagset.FlagType]bool{}),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ public final class OpenFeature {
|
|||
* Details:
|
||||
* - Flag key: {{ .Key }}
|
||||
* - Type: {{ .Type | OpenFeatureType }}
|
||||
* - Default value: {{ .DefaultValue }}
|
||||
* - Default value: {{ if eq (.Type | OpenFeatureType) "Object" }}{{ .DefaultValue | ToMapLiteral }}{{ else }}{{ .DefaultValue }}{{ end }}
|
||||
* Returns the flag value
|
||||
*/
|
||||
{{ .Type | OpenFeatureType }} {{ .Key | ToCamel }}(EvaluationContext ctx);
|
||||
|
@ -27,7 +27,7 @@ public final class OpenFeature {
|
|||
* Details:
|
||||
* - Flag key: {{ .Key }}
|
||||
* - Type: {{ .Type | OpenFeatureType }}
|
||||
* - Default value: {{ .DefaultValue }}
|
||||
* - Default value: {{ if eq (.Type | OpenFeatureType) "Object" }}{{ .DefaultValue | ToMapLiteral }}{{ else }}{{ .DefaultValue }}{{ end }}
|
||||
* Returns the evaluation details containing the flag value and metadata
|
||||
*/
|
||||
FlagEvaluationDetails<{{ .Type | OpenFeatureType }}> {{ .Key | ToCamel }}Details(EvaluationContext ctx);
|
||||
|
@ -44,12 +44,12 @@ public final class OpenFeature {
|
|||
{{ range .Flagset.Flags }}
|
||||
@Override
|
||||
public {{ .Type | OpenFeatureType }} {{ .Key | ToCamel }}(EvaluationContext ctx) {
|
||||
return client.get{{ .Type | OpenFeatureType | ToPascal }}Value("{{ .Key }}", {{ . | FormatDefaultValue }}, ctx);
|
||||
return client.get{{ .Type | OpenFeatureType | ToPascal }}Value("{{ .Key }}", {{ if eq (.Type | OpenFeatureType) "Object" }}{{ .DefaultValue | ToMapLiteral }}{{ else }}{{ . | FormatDefaultValue }}{{ end }}, ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FlagEvaluationDetails<{{ .Type | OpenFeatureType }}> {{ .Key | ToCamel }}Details(EvaluationContext ctx) {
|
||||
return client.get{{ .Type | OpenFeatureType | ToPascal }}Details("{{ .Key }}", {{ . | FormatDefaultValue }}, ctx);
|
||||
return client.get{{ .Type | OpenFeatureType | ToPascal }}Details("{{ .Key }}", {{ if eq (.Type | OpenFeatureType) "Object" }}{{ .DefaultValue | ToMapLiteral }}{{ else }}{{ . | FormatDefaultValue }}{{ end }}, ctx);
|
||||
}
|
||||
{{ end }}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package nestjs
|
|||
|
||||
import (
|
||||
_ "embed"
|
||||
"encoding/json"
|
||||
"text/template"
|
||||
|
||||
"github.com/open-feature/cli/internal/flagset"
|
||||
|
@ -28,14 +29,25 @@ func openFeatureType(t flagset.FlagType) string {
|
|||
return "boolean"
|
||||
case flagset.StringType:
|
||||
return "string"
|
||||
case flagset.ObjectType:
|
||||
return "object"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
func toJSONString(value any) string {
|
||||
bytes, err := json.Marshal(value)
|
||||
if err != nil {
|
||||
return "{}"
|
||||
}
|
||||
return string(bytes)
|
||||
}
|
||||
|
||||
func (g *NestJsGenerator) Generate(params *generators.Params[Params]) error {
|
||||
funcs := template.FuncMap{
|
||||
"OpenFeatureType": openFeatureType,
|
||||
"ToJSONString": toJSONString,
|
||||
}
|
||||
|
||||
newParams := &generators.Params[any]{
|
||||
|
@ -49,8 +61,6 @@ func (g *NestJsGenerator) Generate(params *generators.Params[Params]) error {
|
|||
// NewGenerator creates a generator for NestJS.
|
||||
func NewGenerator(fs *flagset.Flagset) *NestJsGenerator {
|
||||
return &NestJsGenerator{
|
||||
CommonGenerator: *generators.NewGenerator(fs, map[flagset.FlagType]bool{
|
||||
flagset.ObjectType: true,
|
||||
}),
|
||||
CommonGenerator: *generators.NewGenerator(fs, map[flagset.FlagType]bool{}),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,8 +8,9 @@ import type {
|
|||
EvaluationContext,
|
||||
EvaluationDetails,
|
||||
OpenFeatureModuleOptions,
|
||||
JsonValue
|
||||
} from "@openfeature/nestjs-sdk";
|
||||
import { OpenFeatureModule, BooleanFeatureFlag, StringFeatureFlag, NumberFeatureFlag } from "@openfeature/nestjs-sdk";
|
||||
import { OpenFeatureModule, BooleanFeatureFlag, StringFeatureFlag, NumberFeatureFlag, ObjectFeatureFlag } from "@openfeature/nestjs-sdk";
|
||||
|
||||
import type { GeneratedClient } from "./openfeature";
|
||||
import { getGeneratedClient } from "./openfeature";
|
||||
|
@ -100,21 +101,21 @@ interface TypedFeatureProps {
|
|||
* **Details:**
|
||||
* - flag key: `{{ .Key }}`
|
||||
* - description: `{{ .Description }}`
|
||||
* - default value: `{{ .DefaultValue }}`
|
||||
* - type: `{{ .Type | OpenFeatureType }}`
|
||||
* - default value: `{{ if eq (.Type | OpenFeatureType) "object"}}{{ .DefaultValue | ToJSONString }}{{ else }}{{ .DefaultValue }}{{ end }}`
|
||||
* - type: `{{ if eq (.Type | OpenFeatureType) "object" }}JsonValue{{ else }}{{ .Type | OpenFeatureType }}{{ end }}`
|
||||
*
|
||||
* Usage:
|
||||
* ```typescript
|
||||
* @Get("/")
|
||||
* public async handleRequest(
|
||||
* @{{ .Key | ToPascal }}()
|
||||
* {{ .Key | ToCamel }}: Observable<EvaluationDetails<number>>,
|
||||
* {{ .Key | ToCamel }}: Observable<EvaluationDetails<{{ if eq (.Type | OpenFeatureType) "object" }}JsonValue{{ else }}{{ .Type | OpenFeatureType }}{{ end }}>>,
|
||||
* )
|
||||
* ```
|
||||
* @param {TypedFeatureProps} props The options for injecting the feature flag.
|
||||
* @returns {ParameterDecorator} The decorator function.
|
||||
*/
|
||||
export function {{ .Key | ToPascal }}(props?: TypedFeatureProps): ParameterDecorator {
|
||||
return {{ .Type | OpenFeatureType | ToPascal }}FeatureFlag({ flagKey: {{ .Key | Quote }}, defaultValue: {{ .DefaultValue | QuoteString }}, ...props });
|
||||
return {{ .Type | OpenFeatureType | ToPascal }}FeatureFlag({ flagKey: {{ .Key | Quote }}, defaultValue: {{ if eq (.Type | OpenFeatureType) "object"}}{{ .DefaultValue | ToJSONString }}{{ else }}{{ .DefaultValue | QuoteString }}{{ end }}, ...props });
|
||||
}
|
||||
{{ end -}}
|
||||
|
|
|
@ -2,6 +2,7 @@ package nodejs
|
|||
|
||||
import (
|
||||
_ "embed"
|
||||
"encoding/json"
|
||||
"text/template"
|
||||
|
||||
"github.com/open-feature/cli/internal/flagset"
|
||||
|
@ -28,14 +29,25 @@ func openFeatureType(t flagset.FlagType) string {
|
|||
return "boolean"
|
||||
case flagset.StringType:
|
||||
return "string"
|
||||
case flagset.ObjectType:
|
||||
return "object"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
func toJSONString(value any) string {
|
||||
bytes, err := json.Marshal(value)
|
||||
if err != nil {
|
||||
return "{}"
|
||||
}
|
||||
return string(bytes)
|
||||
}
|
||||
|
||||
func (g *NodejsGenerator) Generate(params *generators.Params[Params]) error {
|
||||
funcs := template.FuncMap{
|
||||
"OpenFeatureType": openFeatureType,
|
||||
"ToJSONString": toJSONString,
|
||||
}
|
||||
|
||||
newParams := &generators.Params[any]{
|
||||
|
@ -49,8 +61,6 @@ func (g *NodejsGenerator) Generate(params *generators.Params[Params]) error {
|
|||
// NewGenerator creates a generator for NodeJS.
|
||||
func NewGenerator(fs *flagset.Flagset) *NodejsGenerator {
|
||||
return &NodejsGenerator{
|
||||
CommonGenerator: *generators.NewGenerator(fs, map[flagset.FlagType]bool{
|
||||
flagset.ObjectType: true,
|
||||
}),
|
||||
CommonGenerator: *generators.NewGenerator(fs, map[flagset.FlagType]bool{}),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ import {
|
|||
OpenFeature,
|
||||
stringOrUndefined,
|
||||
objectOrUndefined,
|
||||
JsonValue,
|
||||
} from "@openfeature/server-sdk";
|
||||
import type {
|
||||
EvaluationContext,
|
||||
|
@ -17,30 +18,30 @@ export interface GeneratedClient {
|
|||
*
|
||||
* **Details:**
|
||||
* - flag key: `{{ .Key }}`
|
||||
* - default value: `{{ .DefaultValue }}`
|
||||
* - type: `{{ .Type | OpenFeatureType }}`
|
||||
* - default value: `{{ if eq (.Type | OpenFeatureType) "object"}}{{ .DefaultValue | ToJSONString }}{{ else }}{{ .DefaultValue }}{{ end }}`
|
||||
* - type: `{{ if eq (.Type | OpenFeatureType) "object" }}JsonValue{{ else }}{{ .Type | OpenFeatureType }}{{ end }}`
|
||||
*
|
||||
* Performs a flag evaluation that returns a {{ .Type | OpenFeatureType }}.
|
||||
* @param {EvaluationContext} context The evaluation context used on an individual flag evaluation
|
||||
* @param {FlagEvaluationOptions} options Additional flag evaluation options
|
||||
* @returns {Promise<{{ .Type | OpenFeatureType }}>} Flag evaluation response
|
||||
* @returns {Promise<{{ if eq (.Type | OpenFeatureType) "object" }}JsonValue{{ else }}{{ .Type | OpenFeatureType }}{{ end }}>} Flag evaluation response
|
||||
*/
|
||||
{{ .Key | ToCamel }}(context?: EvaluationContext, options?: FlagEvaluationOptions): Promise<{{ .Type | OpenFeatureType }}>;
|
||||
{{ .Key | ToCamel }}(context?: EvaluationContext, options?: FlagEvaluationOptions): Promise<{{ if eq (.Type | OpenFeatureType) "object" }}JsonValue{{ else }}{{ .Type | OpenFeatureType }}{{ end }}>;
|
||||
|
||||
/**
|
||||
* {{ .Description }}
|
||||
*
|
||||
* **Details:**
|
||||
* - flag key: `{{ .Key }}`
|
||||
* - default value: `{{ .DefaultValue }}`
|
||||
* - type: `{{ .Type | OpenFeatureType }}`
|
||||
* - default value: `{{ if eq (.Type | OpenFeatureType) "object"}}{{ .DefaultValue | ToJSONString }}{{ else }}{{ .DefaultValue }}{{ end }}`
|
||||
* - type: `{{ if eq (.Type | OpenFeatureType) "object" }}JsonValue{{ else }}{{ .Type | OpenFeatureType }}{{ end }}`
|
||||
*
|
||||
* Performs a flag evaluation that a returns an evaluation details object.
|
||||
* @param {EvaluationContext} context The evaluation context used on an individual flag evaluation
|
||||
* @param {FlagEvaluationOptions} options Additional flag evaluation options
|
||||
* @returns {Promise<EvaluationDetails<{{ .Type | OpenFeatureType }}>>} Flag evaluation details response
|
||||
* @returns {Promise<EvaluationDetails<{{ if eq (.Type | OpenFeatureType) "object" }}JsonValue{{ else }}{{ .Type | OpenFeatureType }}{{ end }}>>} Flag evaluation details response
|
||||
*/
|
||||
{{ .Key | ToCamel }}Details(context?: EvaluationContext, options?: FlagEvaluationOptions): Promise<EvaluationDetails<{{ .Type | OpenFeatureType }}>>;
|
||||
{{ .Key | ToCamel }}Details(context?: EvaluationContext, options?: FlagEvaluationOptions): Promise<EvaluationDetails<{{ if eq (.Type | OpenFeatureType) "object" }}JsonValue{{ else }}{{ .Type | OpenFeatureType }}{{ end }}>>;
|
||||
{{ end -}}
|
||||
}
|
||||
|
||||
|
@ -74,12 +75,12 @@ export function getGeneratedClient(domainOrContext?: string | EvaluationContext,
|
|||
|
||||
return {
|
||||
{{- range .Flagset.Flags }}
|
||||
{{ .Key | ToCamel }}: (context?: EvaluationContext, options?: FlagEvaluationOptions): Promise<{{ .Type | OpenFeatureType }}> => {
|
||||
return client.get{{ .Type | OpenFeatureType | ToPascal }}Value({{ .Key | Quote }}, {{ .DefaultValue | QuoteString }}, context, options);
|
||||
{{ .Key | ToCamel }}: (context?: EvaluationContext, options?: FlagEvaluationOptions): Promise<{{ if eq (.Type | OpenFeatureType) "object" }}JsonValue{{ else }}{{ .Type | OpenFeatureType }}{{ end }}> => {
|
||||
return client.get{{ .Type | OpenFeatureType | ToPascal }}Value({{ .Key | Quote }}, {{ if eq (.Type | OpenFeatureType) "object"}}{{ .DefaultValue | ToJSONString }}{{ else }}{{ .DefaultValue | QuoteString }}{{ end }}, context, options);
|
||||
},
|
||||
|
||||
{{ .Key | ToCamel }}Details: (context?: EvaluationContext, options?: FlagEvaluationOptions): Promise<EvaluationDetails<{{ .Type | OpenFeatureType }}>> => {
|
||||
return client.get{{ .Type | OpenFeatureType | ToPascal }}Details({{ .Key | Quote }}, {{ .DefaultValue | QuoteString }}, context, options);
|
||||
{{ .Key | ToCamel }}Details: (context?: EvaluationContext, options?: FlagEvaluationOptions): Promise<EvaluationDetails<{{ if eq (.Type | OpenFeatureType) "object" }}JsonValue{{ else }}{{ .Type | OpenFeatureType }}{{ end }}>> => {
|
||||
return client.get{{ .Type | OpenFeatureType | ToPascal }}Details({{ .Key | Quote }}, {{ if eq (.Type | OpenFeatureType) "object"}}{{ .DefaultValue | ToJSONString }}{{ else }}{{ .DefaultValue | QuoteString }}{{ end }}, context, options);
|
||||
},
|
||||
{{ end -}}
|
||||
{{ printf " " }}}
|
||||
|
|
|
@ -2,6 +2,11 @@ package python
|
|||
|
||||
import (
|
||||
_ "embed"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"maps"
|
||||
"slices"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
"github.com/open-feature/cli/internal/flagset"
|
||||
|
@ -43,6 +48,8 @@ func methodType(flagType flagset.FlagType) string {
|
|||
return "boolean"
|
||||
case flagset.FloatType:
|
||||
return "float"
|
||||
case flagset.ObjectType:
|
||||
return "object"
|
||||
default:
|
||||
panic("unsupported flag type")
|
||||
}
|
||||
|
@ -64,7 +71,7 @@ func typedDetailsMethodAsync(flagType flagset.FlagType) string {
|
|||
return "get_" + methodType(flagType) + "_details_async"
|
||||
}
|
||||
|
||||
func pythonBoolLiteral(value interface{}) interface{} {
|
||||
func pythonBoolLiteral(value any) any {
|
||||
if v, ok := value.(bool); ok {
|
||||
if v {
|
||||
return "True"
|
||||
|
@ -74,6 +81,63 @@ func pythonBoolLiteral(value interface{}) interface{} {
|
|||
return value
|
||||
}
|
||||
|
||||
func toPythonDict(value any) string {
|
||||
assertedMap, ok := value.(map[string]any)
|
||||
if !ok {
|
||||
return "None"
|
||||
}
|
||||
|
||||
// To have a determined order of the object for comparison
|
||||
keys := slices.Sorted(maps.Keys(assertedMap))
|
||||
|
||||
var builder strings.Builder
|
||||
builder.WriteString("{")
|
||||
|
||||
for index, key := range keys {
|
||||
if index != 0 {
|
||||
builder.WriteString(", ")
|
||||
}
|
||||
val := assertedMap[key]
|
||||
|
||||
builder.WriteString(fmt.Sprintf(`%q: %s`, key, formatNestedValue(val)))
|
||||
}
|
||||
|
||||
builder.WriteString("}")
|
||||
return builder.String()
|
||||
}
|
||||
|
||||
func formatNestedValue(value any) string {
|
||||
switch val := value.(type) {
|
||||
case string:
|
||||
return fmt.Sprintf("%q", val)
|
||||
case bool:
|
||||
return fmt.Sprintf(pythonBoolLiteral(val).(string))
|
||||
case int, int64, float64:
|
||||
return fmt.Sprintf("%v", val)
|
||||
case map[string]any:
|
||||
return toPythonDict(val)
|
||||
case []any:
|
||||
var sliceBuilder strings.Builder
|
||||
sliceBuilder.WriteString("[")
|
||||
for index, elem := range val {
|
||||
if index > 0 {
|
||||
sliceBuilder.WriteString(", ")
|
||||
}
|
||||
|
||||
sliceBuilder.WriteString(formatNestedValue(elem))
|
||||
}
|
||||
sliceBuilder.WriteString("]")
|
||||
return sliceBuilder.String()
|
||||
default:
|
||||
jsonBytes, err := json.Marshal(val)
|
||||
if err != nil {
|
||||
return "None"
|
||||
}
|
||||
return strings.ReplaceAll(string(jsonBytes), "null", "None")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (g *PythonGenerator) Generate(params *generators.Params[Params]) error {
|
||||
funcs := template.FuncMap{
|
||||
"OpenFeatureType": openFeatureType,
|
||||
|
@ -82,6 +146,7 @@ func (g *PythonGenerator) Generate(params *generators.Params[Params]) error {
|
|||
"TypedDetailsMethodSync": typedDetailsMethodSync,
|
||||
"TypedDetailsMethodAsync": typedDetailsMethodAsync,
|
||||
"PythonBoolLiteral": pythonBoolLiteral,
|
||||
"ToPythonDict": toPythonDict,
|
||||
}
|
||||
|
||||
newParams := &generators.Params[any]{
|
||||
|
@ -95,8 +160,6 @@ func (g *PythonGenerator) Generate(params *generators.Params[Params]) error {
|
|||
// NewGenerator creates a generator for Python.
|
||||
func NewGenerator(fs *flagset.Flagset) *PythonGenerator {
|
||||
return &PythonGenerator{
|
||||
CommonGenerator: *generators.NewGenerator(fs, map[flagset.FlagType]bool{
|
||||
flagset.ObjectType: true,
|
||||
}),
|
||||
CommonGenerator: *generators.NewGenerator(fs, map[flagset.FlagType]bool{}),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,14 +25,14 @@ class GeneratedClient:
|
|||
|
||||
**Details:**
|
||||
- flag key: `{{ .Key }}`
|
||||
- default value: `{{ .DefaultValue | PythonBoolLiteral }}`
|
||||
- default value: `{{- if eq (.Type | OpenFeatureType) "object"}}{{.DefaultValue | ToPythonDict }}{{- else }}{{ .DefaultValue | PythonBoolLiteral }}{{- end }}`
|
||||
- type: `{{ .Type | OpenFeatureType }}`
|
||||
|
||||
Performs a flag evaluation that returns a `{{ .Type | OpenFeatureType }}`.
|
||||
"""
|
||||
return self.client.{{ .Type | TypedGetMethodSync }}(
|
||||
flag_key={{ .Key | Quote }},
|
||||
default_value={{ .DefaultValue | QuoteString | PythonBoolLiteral }},
|
||||
default_value={{- if eq (.Type | OpenFeatureType) "object"}}{{.DefaultValue | ToPythonDict }}{{- else }}{{ .DefaultValue | QuoteString | PythonBoolLiteral }}{{- end }},
|
||||
evaluation_context=evaluation_context,
|
||||
flag_evaluation_options=flag_evaluation_options,
|
||||
)
|
||||
|
@ -47,14 +47,14 @@ class GeneratedClient:
|
|||
|
||||
**Details:**
|
||||
- flag key: `{{ .Key }}`
|
||||
- default value: `{{ .DefaultValue | PythonBoolLiteral }}`
|
||||
- default value: `{{- if eq (.Type | OpenFeatureType) "object"}}{{.DefaultValue | ToPythonDict }}{{- else }}{{ .DefaultValue | PythonBoolLiteral }}{{- end }}`
|
||||
- type: `{{ .Type | OpenFeatureType }}`
|
||||
|
||||
Performs a flag evaluation that returns a `FlagEvaluationDetails` instance.
|
||||
"""
|
||||
return self.client.{{ .Type | TypedDetailsMethodSync }}(
|
||||
flag_key={{ .Key | Quote }},
|
||||
default_value={{ .DefaultValue | QuoteString | PythonBoolLiteral }},
|
||||
default_value={{- if eq (.Type | OpenFeatureType) "object"}}{{.DefaultValue | ToPythonDict }}{{- else }}{{ .DefaultValue | QuoteString | PythonBoolLiteral }}{{- end }},
|
||||
evaluation_context=evaluation_context,
|
||||
flag_evaluation_options=flag_evaluation_options,
|
||||
)
|
||||
|
@ -69,14 +69,14 @@ class GeneratedClient:
|
|||
|
||||
**Details:**
|
||||
- flag key: `{{ .Key }}`
|
||||
- default value: `{{ .DefaultValue | PythonBoolLiteral }}`
|
||||
- default value: `{{- if eq (.Type | OpenFeatureType) "object"}}{{.DefaultValue | ToPythonDict }}{{- else }}{{ .DefaultValue | PythonBoolLiteral }}{{- end }}`
|
||||
- type: `{{ .Type | OpenFeatureType }}`
|
||||
|
||||
Performs a flag evaluation asynchronously and returns a `{{ .Type | OpenFeatureType }}`.
|
||||
"""
|
||||
return await self.client.{{ .Type | TypedGetMethodAsync }}(
|
||||
flag_key={{ .Key | Quote }},
|
||||
default_value={{ .DefaultValue | QuoteString | PythonBoolLiteral }},
|
||||
default_value={{- if eq (.Type | OpenFeatureType) "object"}}{{.DefaultValue | ToPythonDict }}{{- else }}{{ .DefaultValue | QuoteString | PythonBoolLiteral }}{{- end }},
|
||||
evaluation_context=evaluation_context,
|
||||
flag_evaluation_options=flag_evaluation_options,
|
||||
)
|
||||
|
@ -91,14 +91,14 @@ class GeneratedClient:
|
|||
|
||||
**Details:**
|
||||
- flag key: `{{ .Key }}`
|
||||
- default value: `{{ .DefaultValue | PythonBoolLiteral }}`
|
||||
- default value: `{{- if eq (.Type | OpenFeatureType) "object"}}{{.DefaultValue | ToPythonDict }}{{- else }}{{ .DefaultValue | PythonBoolLiteral }}{{- end }}`
|
||||
- type: `{{ .Type | OpenFeatureType }}`
|
||||
|
||||
Performs a flag evaluation asynchronously and returns a `FlagEvaluationDetails` instance.
|
||||
"""
|
||||
return await self.client.{{ .Type | TypedDetailsMethodAsync }}(
|
||||
flag_key={{ .Key | Quote }},
|
||||
default_value={{ .DefaultValue | QuoteString | PythonBoolLiteral }},
|
||||
default_value={{- if eq (.Type | OpenFeatureType) "object"}}{{.DefaultValue | ToPythonDict }}{{- else }}{{ .DefaultValue | QuoteString | PythonBoolLiteral }}{{- end }},
|
||||
evaluation_context=evaluation_context,
|
||||
flag_evaluation_options=flag_evaluation_options,
|
||||
)
|
||||
|
|
|
@ -2,6 +2,7 @@ package react
|
|||
|
||||
import (
|
||||
_ "embed"
|
||||
"encoding/json"
|
||||
"text/template"
|
||||
|
||||
"github.com/open-feature/cli/internal/flagset"
|
||||
|
@ -28,14 +29,25 @@ func openFeatureType(t flagset.FlagType) string {
|
|||
return "boolean"
|
||||
case flagset.StringType:
|
||||
return "string"
|
||||
case flagset.ObjectType:
|
||||
return "object"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
func toJSONString(value any) string {
|
||||
bytes, err := json.Marshal(value)
|
||||
if err != nil {
|
||||
return "{}"
|
||||
}
|
||||
return string(bytes)
|
||||
}
|
||||
|
||||
func (g *ReactGenerator) Generate(params *generators.Params[Params]) error {
|
||||
funcs := template.FuncMap{
|
||||
"OpenFeatureType": openFeatureType,
|
||||
"ToJSONString": toJSONString,
|
||||
}
|
||||
|
||||
newParams := &generators.Params[any]{
|
||||
|
@ -49,8 +61,6 @@ func (g *ReactGenerator) Generate(params *generators.Params[Params]) error {
|
|||
// NewGenerator creates a generator for React.
|
||||
func NewGenerator(fs *flagset.Flagset) *ReactGenerator {
|
||||
return &ReactGenerator{
|
||||
CommonGenerator: *generators.NewGenerator(fs, map[flagset.FlagType]bool{
|
||||
flagset.ObjectType: true,
|
||||
}),
|
||||
CommonGenerator: *generators.NewGenerator(fs, map[flagset.FlagType]bool{}),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import {
|
|||
type ReactFlagEvaluationNoSuspenseOptions,
|
||||
useFlag,
|
||||
useSuspenseFlag,
|
||||
JsonValue
|
||||
} from "@openfeature/react-sdk";
|
||||
{{ range .Flagset.Flags }}
|
||||
/**
|
||||
|
@ -12,11 +13,11 @@ import {
|
|||
*
|
||||
* **Details:**
|
||||
* - flag key: `{{ .Key }}`
|
||||
* - default value: `{{ .DefaultValue }}`
|
||||
* - type: `{{ .Type | OpenFeatureType }}`
|
||||
* - default value: `{{ if eq (.Type | OpenFeatureType) "object"}}{{ .DefaultValue | ToJSONString }}{{ else }}{{ .DefaultValue }}{{ end }}`
|
||||
* - type: `{{ if eq (.Type | OpenFeatureType) "object" }}JsonValue{{ else }}{{ .Type | OpenFeatureType }}{{ end }}`
|
||||
*/
|
||||
export const use{{ .Key | ToPascal }} = (options?: ReactFlagEvaluationOptions) => {
|
||||
return useFlag({{ .Key | Quote }}, {{ .DefaultValue | QuoteString }}, options);
|
||||
return useFlag({{ .Key | Quote }}, {{ if eq (.Type | OpenFeatureType) "object"}}{{ .DefaultValue | ToJSONString }}{{ else }}{{ .DefaultValue | QuoteString }}{{ end }}, options);
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -24,13 +25,13 @@ export const use{{ .Key | ToPascal }} = (options?: ReactFlagEvaluationOptions) =
|
|||
*
|
||||
* **Details:**
|
||||
* - flag key: `{{ .Key }}`
|
||||
* - default value: `{{ .DefaultValue }}`
|
||||
* - type: `{{ .Type | OpenFeatureType }}`
|
||||
* - default value: `{{ if eq (.Type | OpenFeatureType) "object"}}{{ .DefaultValue | ToJSONString }}{{ else }}{{ .DefaultValue }}{{ end }}`
|
||||
* - type: `{{ if eq (.Type | OpenFeatureType) "object" }}JsonValue{{ else }}{{ .Type | OpenFeatureType }}{{ end }}`
|
||||
*
|
||||
* Equivalent to useFlag with options: `{ suspend: true }`
|
||||
* @experimental — Suspense is an experimental feature subject to change in future versions.
|
||||
*/
|
||||
export const useSuspense{{ .Key | ToPascal }} = (options?: ReactFlagEvaluationNoSuspenseOptions) => {
|
||||
return useSuspenseFlag({{ .Key | Quote }}, {{ .DefaultValue | QuoteString }}, options);
|
||||
return useSuspenseFlag({{ .Key | Quote }}, {{ if eq (.Type | OpenFeatureType) "object"}}{{ .DefaultValue | ToJSONString }}{{ else }}{{ .DefaultValue | QuoteString }}{{ end }}, options);
|
||||
};
|
||||
{{ end}}
|
|
@ -32,4 +32,4 @@ func GetValidOutputFormats() []string {
|
|||
string(OutputFormatJSON),
|
||||
string(OutputFormatYAML),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||
"extends": ["github>open-feature/community-tooling"]
|
||||
}
|
|
@ -9,8 +9,8 @@
|
|||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
|
||||
<PackageReference Include="OpenFeature" Version="2.3.2" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.6" />
|
||||
<PackageReference Include="OpenFeature" Version="2.7.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
@ -111,6 +111,38 @@ namespace OpenFeature
|
|||
{
|
||||
return await _client.GetStringDetailsAsync("greetingMessage", "Hello there!", evaluationContext);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Allows customization of theme colors.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>Flag key: themeCustomization</para>
|
||||
/// <para>Default value: new Value(Structure.Builder().Set("primaryColor", "#007bff").Set("secondaryColor", "#6c757d").Build())</para>
|
||||
/// <para>Type: object</para>
|
||||
/// </remarks>
|
||||
/// <param name="evaluationContext">Optional context for the flag evaluation</param>
|
||||
/// <param name="options">Options for flag evaluation</param>
|
||||
/// <returns>The flag value</returns>
|
||||
public async Task<Value> ThemeCustomizationAsync(EvaluationContext? evaluationContext = null, FlagEvaluationOptions? options = null)
|
||||
{
|
||||
return await _client.GetObjectValueAsync("themeCustomization", new Value(Structure.Builder().Set("primaryColor", "#007bff").Set("secondaryColor", "#6c757d").Build()), evaluationContext, options);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Allows customization of theme colors.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>Flag key: themeCustomization</para>
|
||||
/// <para>Default value: new Value(Structure.Builder().Set("primaryColor", "#007bff").Set("secondaryColor", "#6c757d").Build())</para>
|
||||
/// <para>Type: object</para>
|
||||
/// </remarks>
|
||||
/// <param name="evaluationContext">Optional context for the flag evaluation</param>
|
||||
/// <param name="options">Options for flag evaluation</param>
|
||||
/// <returns>The evaluation details containing the flag value and metadata</returns>
|
||||
public async Task<FlagEvaluationDetails<Value>> ThemeCustomizationDetailsAsync(EvaluationContext? evaluationContext = null, FlagEvaluationOptions? options = null)
|
||||
{
|
||||
return await _client.GetObjectDetailsAsync("themeCustomization", new Value(Structure.Builder().Set("primaryColor", "#007bff").Set("secondaryColor", "#6c757d").Build()), evaluationContext, options);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Maximum allowed length for usernames.
|
||||
|
|
|
@ -159,15 +159,15 @@ namespace TestNamespace
|
|||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>Flag key: themeCustomization</para>
|
||||
/// <para>Default value: map[primaryColor:#007bff secondaryColor:#6c757d]</para>
|
||||
/// <para>Type: Value</para>
|
||||
/// <para>Default value: new Value(Structure.Builder().Set("primaryColor", "#007bff").Set("secondaryColor", "#6c757d").Build())</para>
|
||||
/// <para>Type: object</para>
|
||||
/// </remarks>
|
||||
/// <param name="evaluationContext">Optional context for the flag evaluation</param>
|
||||
/// <param name="options">Options for flag evaluation</param>
|
||||
/// <returns>The flag value</returns>
|
||||
public async Task<Value> ThemeCustomizationAsync(EvaluationContext? evaluationContext = null, FlagEvaluationOptions? options = null)
|
||||
{
|
||||
throw new NotSupportedException("Unsupported flag type");
|
||||
return await _client.GetObjectValueAsync("themeCustomization", new Value(Structure.Builder().Set("primaryColor", "#007bff").Set("secondaryColor", "#6c757d").Build()), evaluationContext, options);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -175,15 +175,15 @@ namespace TestNamespace
|
|||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>Flag key: themeCustomization</para>
|
||||
/// <para>Default value: map[primaryColor:#007bff secondaryColor:#6c757d]</para>
|
||||
/// <para>Type: Value</para>
|
||||
/// <para>Default value: new Value(Structure.Builder().Set("primaryColor", "#007bff").Set("secondaryColor", "#6c757d").Build())</para>
|
||||
/// <para>Type: object</para>
|
||||
/// </remarks>
|
||||
/// <param name="evaluationContext">Optional context for the flag evaluation</param>
|
||||
/// <param name="options">Options for flag evaluation</param>
|
||||
/// <returns>The evaluation details containing the flag value and metadata</returns>
|
||||
public async Task<FlagEvaluationDetails<Value>> ThemeCustomizationDetailsAsync(EvaluationContext? evaluationContext = null, FlagEvaluationOptions? options = null)
|
||||
{
|
||||
throw new NotSupportedException("Unsupported flag type");
|
||||
return await _client.GetObjectDetailsAsync("themeCustomization", new Value(Structure.Builder().Set("primaryColor", "#007bff").Set("secondaryColor", "#6c757d").Build()), evaluationContext, options);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -152,6 +152,38 @@ namespace TestNamespace
|
|||
{
|
||||
return await _client.GetStringDetailsAsync("greetingMessage", "Hello there!", evaluationContext, options);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Allows customization of theme colors.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>Flag key: themeCustomization</para>
|
||||
/// <para>Default value: new Value(Structure.Builder().Set("primaryColor", "#007bff").Set("secondaryColor", "#6c757d").Build())</para>
|
||||
/// <para>Type: object</para>
|
||||
/// </remarks>
|
||||
/// <param name="evaluationContext">Optional context for the flag evaluation</param>
|
||||
/// <param name="options">Options for flag evaluation</param>
|
||||
/// <returns>The flag value</returns>
|
||||
public async Task<Value> ThemeCustomizationAsync(EvaluationContext? evaluationContext = null, FlagEvaluationOptions? options = null)
|
||||
{
|
||||
return await _client.GetObjectValueAsync("themeCustomization", new Value(Structure.Builder().Set("primaryColor", "#007bff").Set("secondaryColor", "#6c757d").Build()), evaluationContext, options);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Allows customization of theme colors.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>Flag key: themeCustomization</para>
|
||||
/// <para>Default value: new Value(Structure.Builder().Set("primaryColor", "#007bff").Set("secondaryColor", "#6c757d").Build())</para>
|
||||
/// <para>Type: object</para>
|
||||
/// </remarks>
|
||||
/// <param name="evaluationContext">Optional context for the flag evaluation</param>
|
||||
/// <param name="options">Options for flag evaluation</param>
|
||||
/// <returns>The evaluation details containing the flag value and metadata</returns>
|
||||
public async Task<FlagEvaluationDetails<Value>> ThemeCustomizationDetailsAsync(EvaluationContext? evaluationContext = null, FlagEvaluationOptions? options = null)
|
||||
{
|
||||
return await _client.GetObjectDetailsAsync("themeCustomization", new Value(Structure.Builder().Set("primaryColor", "#007bff").Set("secondaryColor", "#6c757d").Build()), evaluationContext, options);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Maximum allowed length for usernames.
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
module github.com/open-feature/cli/test/go-integration
|
||||
|
||||
go 1.22
|
||||
|
||||
require github.com/open-feature/go-sdk v1.15.0
|
||||
|
||||
require (
|
||||
github.com/go-logr/logr v1.4.1 // indirect
|
||||
github.com/golang/protobuf v1.5.3 // indirect
|
||||
github.com/google/go-cmp v0.6.0 // indirect
|
||||
golang.org/x/net v0.21.0 // indirect
|
||||
google.golang.org/protobuf v1.32.0 // indirect
|
||||
)
|
|
@ -0,0 +1,106 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
generated "github.com/open-feature/cli/test/go-integration/openfeature"
|
||||
"github.com/open-feature/go-sdk/openfeature"
|
||||
"github.com/open-feature/go-sdk/openfeature/memprovider"
|
||||
)
|
||||
|
||||
func main() {
|
||||
if err := run(); err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func run() error {
|
||||
// Set up the in-memory provider with test flags
|
||||
provider := memprovider.NewInMemoryProvider(map[string]memprovider.InMemoryFlag{
|
||||
"discountPercentage": {
|
||||
State: memprovider.Enabled,
|
||||
DefaultVariant: "default",
|
||||
Variants: map[string]any{
|
||||
"default": 0.15,
|
||||
},
|
||||
},
|
||||
"enableFeatureA": {
|
||||
State: memprovider.Enabled,
|
||||
DefaultVariant: "default",
|
||||
Variants: map[string]any{
|
||||
"default": false,
|
||||
},
|
||||
},
|
||||
"greetingMessage": {
|
||||
State: memprovider.Enabled,
|
||||
DefaultVariant: "default",
|
||||
Variants: map[string]any{
|
||||
"default": "Hello there!",
|
||||
},
|
||||
},
|
||||
"usernameMaxLength": {
|
||||
State: memprovider.Enabled,
|
||||
DefaultVariant: "default",
|
||||
Variants: map[string]any{
|
||||
"default": 50,
|
||||
},
|
||||
},
|
||||
"themeCustomization": {
|
||||
State: memprovider.Enabled,
|
||||
DefaultVariant: "default",
|
||||
Variants: map[string]any{
|
||||
"default": map[string]any{
|
||||
"primaryColor": "#007bff",
|
||||
"secondaryColor": "#6c757d",
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
// Set the provider and wait for it to be ready
|
||||
err := openfeature.SetProviderAndWait(provider)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to set provider: %w", err)
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
evalCtx := openfeature.NewEvaluationContext("someid", map[string]any{})
|
||||
|
||||
// Use the generated code for all flag evaluations
|
||||
enableFeatureA, err := generated.EnableFeatureA.Value(ctx, evalCtx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error evaluating boolean flag: %w", err)
|
||||
}
|
||||
fmt.Printf("enableFeatureA: %v\n", enableFeatureA)
|
||||
|
||||
discount, err := generated.DiscountPercentage.Value(ctx, evalCtx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to get discount: %w", err)
|
||||
}
|
||||
fmt.Printf("Discount Percentage: %.2f\n", discount)
|
||||
|
||||
greetingMessage, err := generated.GreetingMessage.Value(ctx, evalCtx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error evaluating string flag: %w", err)
|
||||
}
|
||||
fmt.Printf("greetingMessage: %v\n", greetingMessage)
|
||||
|
||||
usernameMaxLength, err := generated.UsernameMaxLength.Value(ctx, evalCtx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error evaluating int flag: %v\n", err)
|
||||
}
|
||||
fmt.Printf("usernameMaxLength: %v\n", usernameMaxLength)
|
||||
|
||||
themeCustomization, err := generated.ThemeCustomization.Value(ctx, evalCtx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error evaluating int flag: %v\n", err)
|
||||
}
|
||||
fmt.Printf("themeCustomization: %v\n", themeCustomization)
|
||||
|
||||
fmt.Println("Generated Go code compiles successfully!")
|
||||
|
||||
return nil
|
||||
}
|
|
@ -39,7 +39,7 @@ func (t *Test) Run(ctx context.Context, client *dagger.Client) (*dagger.Containe
|
|||
From("golang:1.24-alpine").
|
||||
WithDirectory("/src", source).
|
||||
WithWorkdir("/src").
|
||||
WithExec([]string{"go", "build", "-o", "cli"})
|
||||
WithExec([]string{"go", "build", "-o", "cli", "./cmd/openfeature"})
|
||||
|
||||
// Generate C# client
|
||||
generated := cli.WithExec([]string{
|
||||
|
|
|
@ -0,0 +1,101 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"dagger.io/dagger"
|
||||
"github.com/open-feature/cli/test/integration"
|
||||
)
|
||||
|
||||
// Test implements the integration test for the Go generator
|
||||
type Test struct {
|
||||
// ProjectDir is the absolute path to the root of the project
|
||||
ProjectDir string
|
||||
// TestDir is the absolute path to the test directory
|
||||
TestDir string
|
||||
}
|
||||
|
||||
// New creates a new Test
|
||||
func New(projectDir, testDir string) *Test {
|
||||
return &Test{
|
||||
ProjectDir: projectDir,
|
||||
TestDir: testDir,
|
||||
}
|
||||
}
|
||||
|
||||
// Run executes the Go integration test using Dagger
|
||||
func (t *Test) Run(ctx context.Context, client *dagger.Client) (*dagger.Container, error) {
|
||||
// Source code container
|
||||
source := client.Host().Directory(t.ProjectDir)
|
||||
testFiles := client.Host().Directory(t.TestDir, dagger.HostDirectoryOpts{
|
||||
Include: []string{"test.go", "go.mod"},
|
||||
})
|
||||
|
||||
// Build the CLI
|
||||
cli := client.Container().
|
||||
From("golang:1.23-alpine").
|
||||
WithExec([]string{"apk", "add", "--no-cache", "git"}).
|
||||
WithDirectory("/src", source).
|
||||
WithWorkdir("/src").
|
||||
WithExec([]string{"go", "mod", "tidy"}).
|
||||
WithExec([]string{"go", "mod", "download"}).
|
||||
WithExec([]string{"go", "build", "-o", "cli", "./cmd/openfeature"})
|
||||
|
||||
// Generate Go client
|
||||
generated := cli.WithExec([]string{
|
||||
"./cli", "generate", "go",
|
||||
"--manifest=/src/sample/sample_manifest.json",
|
||||
"--output=/tmp/generated",
|
||||
"--package-name=openfeature",
|
||||
})
|
||||
|
||||
// Get generated files
|
||||
generatedFiles := generated.Directory("/tmp/generated")
|
||||
|
||||
// Test Go compilation with the generated files
|
||||
goContainer := client.Container().
|
||||
From("golang:1.23-alpine").
|
||||
WithExec([]string{"apk", "add", "--no-cache", "git"}).
|
||||
WithWorkdir("/app").
|
||||
WithDirectory("/app", testFiles).
|
||||
WithDirectory("/app/openfeature", generatedFiles).
|
||||
WithExec([]string{"go", "mod", "tidy"}).
|
||||
WithExec([]string{"go", "build", "-o", "test", "-v"}).
|
||||
WithExec([]string{"./test"})
|
||||
|
||||
return goContainer, nil
|
||||
}
|
||||
|
||||
// Name returns the name of the integration test
|
||||
func (t *Test) Name() string {
|
||||
return "go"
|
||||
}
|
||||
|
||||
func main() {
|
||||
ctx := context.Background()
|
||||
|
||||
// Get project root
|
||||
projectDir, err := filepath.Abs(os.Getenv("PWD"))
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Failed to get project dir: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Get test directory
|
||||
testDir, err := filepath.Abs(filepath.Join(projectDir, "test/go-integration"))
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Failed to get test dir: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Create and run the Go integration test
|
||||
test := New(projectDir, testDir)
|
||||
|
||||
if err := integration.RunTest(ctx, test); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"dagger.io/dagger"
|
||||
"fmt"
|
||||
"github.com/open-feature/cli/test/integration"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
type Test struct {
|
||||
ProjectDir string
|
||||
TestDir string
|
||||
}
|
||||
|
||||
func New(projectDir, testDir string) *Test {
|
||||
return &Test{
|
||||
ProjectDir: projectDir,
|
||||
TestDir: testDir,
|
||||
}
|
||||
}
|
||||
func (t *Test) Run(ctx context.Context, client *dagger.Client) (*dagger.Container, error) {
|
||||
source := client.Host().Directory(t.ProjectDir)
|
||||
testFiles := client.Host().Directory(t.TestDir, dagger.HostDirectoryOpts{
|
||||
Include: []string{"package.json", "test.ts"},
|
||||
})
|
||||
|
||||
cli := client.Container().
|
||||
From("golang:1.24-alpine").
|
||||
WithDirectory("/src", source).
|
||||
WithWorkdir("/src").
|
||||
WithExec([]string{"go", "build", "-o", "cli", "./cmd/openfeature"})
|
||||
|
||||
generated := cli.WithExec([]string{
|
||||
"./cli", "generate", "nodejs",
|
||||
"--manifest=/src/sample/sample_manifest.json",
|
||||
"--output=/tmp/generated",
|
||||
})
|
||||
|
||||
generatedFiles := generated.Directory("/tmp/generated")
|
||||
|
||||
nodeContainer := client.Container().
|
||||
From("node:22-alpine").
|
||||
WithExec([]string{"npm", "install", "-g", "typescript"}).
|
||||
WithDirectory("/app/generated", generatedFiles).
|
||||
WithDirectory("/app", testFiles).
|
||||
WithWorkdir("/app").
|
||||
WithExec([]string{"npm", "install"}).
|
||||
WithExec([]string{"npm", "test"})
|
||||
|
||||
return nodeContainer, nil
|
||||
}
|
||||
func (t *Test) Name() string {
|
||||
return "nodejs"
|
||||
}
|
||||
func main() {
|
||||
ctx := context.Background()
|
||||
|
||||
projectDir, err := filepath.Abs(os.Getenv("PWD"))
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Failed to get project dir: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
testDir, err := filepath.Abs(filepath.Join(projectDir, "test/nodejs-integration"))
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Failed to get test dir: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
test := New(projectDir, testDir)
|
||||
|
||||
if err := integration.RunTest(ctx, test); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
|
@ -20,6 +20,23 @@ func main() {
|
|||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Run the Go integration test
|
||||
goCmd := exec.Command("go", "run", "github.com/open-feature/cli/test/integration/cmd/go")
|
||||
goCmd.Stdout = os.Stdout
|
||||
goCmd.Stderr = os.Stderr
|
||||
if err := goCmd.Run(); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error running Go integration test: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
//Run the nodejs test
|
||||
nodeCmd := exec.Command("go", "run", "github.com/open-feature/cli/test/integration/cmd/nodejs")
|
||||
nodeCmd.Stdout = os.Stdout
|
||||
nodeCmd.Stderr = os.Stderr
|
||||
if err := nodeCmd.Run(); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error running nodejs integration test: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Add more tests here as they are available
|
||||
|
||||
fmt.Println("=== All integration tests passed successfully ===")
|
||||
|
|
|
@ -0,0 +1,598 @@
|
|||
{
|
||||
"name": "nodejs-integration",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "nodejs-integration",
|
||||
"version": "1.0.0",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@openfeature/server-sdk": "^1.18.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^20.19.0",
|
||||
"tsx": "^4.0.0",
|
||||
"typescript": "^5.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=22"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/aix-ppc64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.5.tgz",
|
||||
"integrity": "sha512-9o3TMmpmftaCMepOdA5k/yDw8SfInyzWWTjYTFCX3kPSDJMROQTb8jg+h9Cnwnmm1vOzvxN7gIfB5V2ewpjtGA==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"aix"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/android-arm": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.5.tgz",
|
||||
"integrity": "sha512-AdJKSPeEHgi7/ZhuIPtcQKr5RQdo6OO2IL87JkianiMYMPbCtot9fxPbrMiBADOWWm3T2si9stAiVsGbTQFkbA==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/android-arm64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.5.tgz",
|
||||
"integrity": "sha512-VGzGhj4lJO+TVGV1v8ntCZWJktV7SGCs3Pn1GRWI1SBFtRALoomm8k5E9Pmwg3HOAal2VDc2F9+PM/rEY6oIDg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/android-x64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.5.tgz",
|
||||
"integrity": "sha512-D2GyJT1kjvO//drbRT3Hib9XPwQeWd9vZoBJn+bu/lVsOZ13cqNdDeqIF/xQ5/VmWvMduP6AmXvylO/PIc2isw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/darwin-arm64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.5.tgz",
|
||||
"integrity": "sha512-GtaBgammVvdF7aPIgH2jxMDdivezgFu6iKpmT+48+F8Hhg5J/sfnDieg0aeG/jfSvkYQU2/pceFPDKlqZzwnfQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/darwin-x64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.5.tgz",
|
||||
"integrity": "sha512-1iT4FVL0dJ76/q1wd7XDsXrSW+oLoquptvh4CLR4kITDtqi2e/xwXwdCVH8hVHU43wgJdsq7Gxuzcs6Iq/7bxQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/freebsd-arm64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.5.tgz",
|
||||
"integrity": "sha512-nk4tGP3JThz4La38Uy/gzyXtpkPW8zSAmoUhK9xKKXdBCzKODMc2adkB2+8om9BDYugz+uGV7sLmpTYzvmz6Sw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"freebsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/freebsd-x64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.5.tgz",
|
||||
"integrity": "sha512-PrikaNjiXdR2laW6OIjlbeuCPrPaAl0IwPIaRv+SMV8CiM8i2LqVUHFC1+8eORgWyY7yhQY+2U2fA55mBzReaw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"freebsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-arm": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.5.tgz",
|
||||
"integrity": "sha512-cPzojwW2okgh7ZlRpcBEtsX7WBuqbLrNXqLU89GxWbNt6uIg78ET82qifUy3W6OVww6ZWobWub5oqZOVtwolfw==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-arm64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.5.tgz",
|
||||
"integrity": "sha512-Z9kfb1v6ZlGbWj8EJk9T6czVEjjq2ntSYLY2cw6pAZl4oKtfgQuS4HOq41M/BcoLPzrUbNd+R4BXFyH//nHxVg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-ia32": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.5.tgz",
|
||||
"integrity": "sha512-sQ7l00M8bSv36GLV95BVAdhJ2QsIbCuCjh/uYrWiMQSUuV+LpXwIqhgJDcvMTj+VsQmqAHL2yYaasENvJ7CDKA==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-loong64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.5.tgz",
|
||||
"integrity": "sha512-0ur7ae16hDUC4OL5iEnDb0tZHDxYmuQyhKhsPBV8f99f6Z9KQM02g33f93rNH5A30agMS46u2HP6qTdEt6Q1kg==",
|
||||
"cpu": [
|
||||
"loong64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-mips64el": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.5.tgz",
|
||||
"integrity": "sha512-kB/66P1OsHO5zLz0i6X0RxlQ+3cu0mkxS3TKFvkb5lin6uwZ/ttOkP3Z8lfR9mJOBk14ZwZ9182SIIWFGNmqmg==",
|
||||
"cpu": [
|
||||
"mips64el"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-ppc64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.5.tgz",
|
||||
"integrity": "sha512-UZCmJ7r9X2fe2D6jBmkLBMQetXPXIsZjQJCjgwpVDz+YMcS6oFR27alkgGv3Oqkv07bxdvw7fyB71/olceJhkQ==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-riscv64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.5.tgz",
|
||||
"integrity": "sha512-kTxwu4mLyeOlsVIFPfQo+fQJAV9mh24xL+y+Bm6ej067sYANjyEw1dNHmvoqxJUCMnkBdKpvOn0Ahql6+4VyeA==",
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-s390x": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.5.tgz",
|
||||
"integrity": "sha512-K2dSKTKfmdh78uJ3NcWFiqyRrimfdinS5ErLSn3vluHNeHVnBAFWC8a4X5N+7FgVE1EjXS1QDZbpqZBjfrqMTQ==",
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-x64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.5.tgz",
|
||||
"integrity": "sha512-uhj8N2obKTE6pSZ+aMUbqq+1nXxNjZIIjCjGLfsWvVpy7gKCOL6rsY1MhRh9zLtUtAI7vpgLMK6DxjO8Qm9lJw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/netbsd-arm64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.5.tgz",
|
||||
"integrity": "sha512-pwHtMP9viAy1oHPvgxtOv+OkduK5ugofNTVDilIzBLpoWAM16r7b/mxBvfpuQDpRQFMfuVr5aLcn4yveGvBZvw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"netbsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/netbsd-x64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.5.tgz",
|
||||
"integrity": "sha512-WOb5fKrvVTRMfWFNCroYWWklbnXH0Q5rZppjq0vQIdlsQKuw6mdSihwSo4RV/YdQ5UCKKvBy7/0ZZYLBZKIbwQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"netbsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/openbsd-arm64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.5.tgz",
|
||||
"integrity": "sha512-7A208+uQKgTxHd0G0uqZO8UjK2R0DDb4fDmERtARjSHWxqMTye4Erz4zZafx7Di9Cv+lNHYuncAkiGFySoD+Mw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"openbsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/openbsd-x64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.5.tgz",
|
||||
"integrity": "sha512-G4hE405ErTWraiZ8UiSoesH8DaCsMm0Cay4fsFWOOUcz8b8rC6uCvnagr+gnioEjWn0wC+o1/TAHt+It+MpIMg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"openbsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/sunos-x64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.5.tgz",
|
||||
"integrity": "sha512-l+azKShMy7FxzY0Rj4RCt5VD/q8mG/e+mDivgspo+yL8zW7qEwctQ6YqKX34DTEleFAvCIUviCFX1SDZRSyMQA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"sunos"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/win32-arm64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.5.tgz",
|
||||
"integrity": "sha512-O2S7SNZzdcFG7eFKgvwUEZ2VG9D/sn/eIiz8XRZ1Q/DO5a3s76Xv0mdBzVM5j5R639lXQmPmSo0iRpHqUUrsxw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/win32-ia32": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.5.tgz",
|
||||
"integrity": "sha512-onOJ02pqs9h1iMJ1PQphR+VZv8qBMQ77Klcsqv9CNW2w6yLqoURLcgERAIurY6QE63bbLuqgP9ATqajFLK5AMQ==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/win32-x64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.5.tgz",
|
||||
"integrity": "sha512-TXv6YnJ8ZMVdX+SXWVBo/0p8LTcrUYngpWjvm91TMjjBQii7Oz11Lw5lbDV5Y0TzuhSJHwiH4hEtC1I42mMS0g==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@openfeature/core": {
|
||||
"version": "1.8.0",
|
||||
"resolved": "https://registry.npmjs.org/@openfeature/core/-/core-1.8.0.tgz",
|
||||
"integrity": "sha512-FX/B6yMD2s4BlMKtB0PqSMl94eLaTwh0VK9URcMvjww0hqMOeGZnGv4uv9O5E58krAan7yCOCm4NBCoh2IATqw==",
|
||||
"license": "Apache-2.0",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@openfeature/server-sdk": {
|
||||
"version": "1.18.0",
|
||||
"resolved": "https://registry.npmjs.org/@openfeature/server-sdk/-/server-sdk-1.18.0.tgz",
|
||||
"integrity": "sha512-uP8nqEGBS58s3iWXx6d8vnJ6ZVt3DACJL4PWADOAuwIS4xXpID91783e9f6zQ0i1ijQpj3yx+3ZuCB2LfQMUMA==",
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@openfeature/core": "^1.7.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "20.19.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.0.tgz",
|
||||
"integrity": "sha512-hfrc+1tud1xcdVTABC2JiomZJEklMcXYNTVtZLAeqTVWD+qL5jkHKT+1lOtqDdGxt+mB53DTtiz673vfjU8D1Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"undici-types": "~6.21.0"
|
||||
}
|
||||
},
|
||||
"node_modules/esbuild": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.5.tgz",
|
||||
"integrity": "sha512-P8OtKZRv/5J5hhz0cUAdu/cLuPIKXpQl1R9pZtvmHWQvrAUVd0UNIPT4IB4W3rNOqVO0rlqHmCIbSwxh/c9yUQ==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"esbuild": "bin/esbuild"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@esbuild/aix-ppc64": "0.25.5",
|
||||
"@esbuild/android-arm": "0.25.5",
|
||||
"@esbuild/android-arm64": "0.25.5",
|
||||
"@esbuild/android-x64": "0.25.5",
|
||||
"@esbuild/darwin-arm64": "0.25.5",
|
||||
"@esbuild/darwin-x64": "0.25.5",
|
||||
"@esbuild/freebsd-arm64": "0.25.5",
|
||||
"@esbuild/freebsd-x64": "0.25.5",
|
||||
"@esbuild/linux-arm": "0.25.5",
|
||||
"@esbuild/linux-arm64": "0.25.5",
|
||||
"@esbuild/linux-ia32": "0.25.5",
|
||||
"@esbuild/linux-loong64": "0.25.5",
|
||||
"@esbuild/linux-mips64el": "0.25.5",
|
||||
"@esbuild/linux-ppc64": "0.25.5",
|
||||
"@esbuild/linux-riscv64": "0.25.5",
|
||||
"@esbuild/linux-s390x": "0.25.5",
|
||||
"@esbuild/linux-x64": "0.25.5",
|
||||
"@esbuild/netbsd-arm64": "0.25.5",
|
||||
"@esbuild/netbsd-x64": "0.25.5",
|
||||
"@esbuild/openbsd-arm64": "0.25.5",
|
||||
"@esbuild/openbsd-x64": "0.25.5",
|
||||
"@esbuild/sunos-x64": "0.25.5",
|
||||
"@esbuild/win32-arm64": "0.25.5",
|
||||
"@esbuild/win32-ia32": "0.25.5",
|
||||
"@esbuild/win32-x64": "0.25.5"
|
||||
}
|
||||
},
|
||||
"node_modules/fsevents": {
|
||||
"version": "2.3.3",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
||||
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/get-tsconfig": {
|
||||
"version": "4.10.1",
|
||||
"resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.10.1.tgz",
|
||||
"integrity": "sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"resolve-pkg-maps": "^1.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/privatenumber/get-tsconfig?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/resolve-pkg-maps": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz",
|
||||
"integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/tsx": {
|
||||
"version": "4.19.4",
|
||||
"resolved": "https://registry.npmjs.org/tsx/-/tsx-4.19.4.tgz",
|
||||
"integrity": "sha512-gK5GVzDkJK1SI1zwHf32Mqxf2tSJkNx+eYcNly5+nHvWqXUJYUkWBQtKauoESz3ymezAI++ZwT855x5p5eop+Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"esbuild": "~0.25.0",
|
||||
"get-tsconfig": "^4.7.5"
|
||||
},
|
||||
"bin": {
|
||||
"tsx": "dist/cli.mjs"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"fsevents": "~2.3.3"
|
||||
}
|
||||
},
|
||||
"node_modules/typescript": {
|
||||
"version": "5.8.3",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz",
|
||||
"integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.17"
|
||||
}
|
||||
},
|
||||
"node_modules/undici-types": {
|
||||
"version": "6.21.0",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
|
||||
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"name": "nodejs-integration",
|
||||
"version": "1.0.0",
|
||||
"description": "Integration test for OpenFeature CLI Node.js generator",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"test": "tsx test.ts"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@openfeature/server-sdk": "^1.18.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^20.19.0",
|
||||
"tsx": "^4.0.0",
|
||||
"typescript": "^5.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=22"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,99 @@
|
|||
import { OpenFeature, JsonValue } from "@openfeature/server-sdk";
|
||||
import { existsSync, readdirSync } from 'node:fs';
|
||||
import { join, dirname } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = dirname(__filename);
|
||||
|
||||
// Simple test provider
|
||||
class TestProvider {
|
||||
get name() { return 'test-provider'; }
|
||||
get metadata() { return { name: 'test-provider' }; }
|
||||
|
||||
async resolveBooleanEvaluation(flagKey: string, defaultValue: boolean) {
|
||||
return { value: flagKey === 'enableFeatureA' ? true : defaultValue, reason: 'STATIC' };
|
||||
}
|
||||
|
||||
async resolveStringEvaluation(flagKey: string, defaultValue: string) {
|
||||
return { value: flagKey === 'greetingMessage' ? 'Hello from test!' : defaultValue, reason: 'STATIC' };
|
||||
}
|
||||
|
||||
async resolveNumberEvaluation(flagKey: string, defaultValue: number) {
|
||||
const values: Record<string, number> = { usernameMaxLength: 100, discountPercentage: 0.25 };
|
||||
return { value: values[flagKey] ?? defaultValue, reason: 'STATIC' };
|
||||
}
|
||||
async resolveObjectEvaluation<T>(flagKey: string, defaultValue: JsonValue) {
|
||||
const values: Record<string, JsonValue> = {
|
||||
themeCustomization: {
|
||||
primaryColor: '#ff0000',
|
||||
secondaryColor: '#00ff00'
|
||||
} as T
|
||||
};
|
||||
return { value: values[flagKey] ?? defaultValue, reason: 'STATIC' };
|
||||
}
|
||||
}
|
||||
|
||||
async function main() {
|
||||
try {
|
||||
console.log('🚀 Node.js OpenFeature Integration Test');
|
||||
|
||||
// 1. Check generated files
|
||||
const generatedDir = join(__dirname, 'generated');
|
||||
if (!existsSync(generatedDir)) {
|
||||
throw new Error('Generated directory not found');
|
||||
}
|
||||
|
||||
const files = readdirSync(generatedDir);
|
||||
const clientFile = files.find(file => file.includes('openfeature'));
|
||||
if (!clientFile) {
|
||||
throw new Error('openfeature.ts not found');
|
||||
}
|
||||
console.log(`✅ Found: ${clientFile}`);
|
||||
|
||||
|
||||
const clientPath = join(generatedDir, clientFile);
|
||||
|
||||
// 3. Setup OpenFeature provider and test
|
||||
await OpenFeature.setProvider(new TestProvider());
|
||||
|
||||
|
||||
const { getGeneratedClient } = await import(clientPath);
|
||||
const client = getGeneratedClient();
|
||||
|
||||
console.log('🧪 Testing flags...');
|
||||
|
||||
// Test each flag
|
||||
const tests = [
|
||||
{ name: 'enableFeatureA', expected: 'boolean' },
|
||||
{ name: 'greetingMessage', expected: 'string' },
|
||||
{ name: 'usernameMaxLength', expected: 'number' },
|
||||
{ name: 'discountPercentage', expected: 'number' },
|
||||
{ name: 'themeCustomization', expected: 'object' }
|
||||
];
|
||||
|
||||
for (const test of tests) {
|
||||
if (client[test.name]) {
|
||||
const result = await client[test.name]();
|
||||
const type = typeof result;
|
||||
if (type === test.expected) {
|
||||
console.log(`✅ ${test.name}: ${result} | type: (${type})`);
|
||||
} else {
|
||||
throw new Error(`${test.name} returned ${type}, expected ${test.expected}`);
|
||||
}
|
||||
} else {
|
||||
console.log(`⚠️ ${test.name} method not found`);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
console.log('🎉 All tests passed!');
|
||||
process.exit(0);
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ Test failed:', error.message);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
Loading…
Reference in New Issue