create-svc 0.1.62 → 0.1.64

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/README.md +9 -6
  2. package/package.json +2 -1
  3. package/src/cli.test.ts +1 -0
  4. package/src/cli.ts +17 -6
  5. package/src/naming.test.ts +6 -0
  6. package/src/naming.ts +1 -1
  7. package/src/scaffold.test.ts +19 -3
  8. package/src/scaffold.ts +10 -0
  9. package/src/service-runtime/authctl.ts +32 -0
  10. package/src/service-runtime/cloudrun/bootstrap.ts +3 -4
  11. package/src/service-runtime/cloudrun/cleanup.ts +6 -1
  12. package/src/service-runtime/cloudrun/cli.ts +52 -12
  13. package/src/service-runtime/cloudrun/config.ts +3 -0
  14. package/src/service-runtime/cloudrun/deploy-args.ts +17 -0
  15. package/src/service-runtime/cloudrun/deploy.ts +25 -0
  16. package/src/service-runtime/cloudrun/lib.test.ts +12 -1
  17. package/src/service-runtime/cloudrun/lib.ts +55 -15
  18. package/src/service-runtime/cloudrun/temporal-config.test.ts +66 -0
  19. package/src/service-runtime/cloudrun/temporal-config.ts +84 -0
  20. package/src/service-runtime/workers/cli.ts +88 -0
  21. package/src/service.test.ts +15 -2
  22. package/src/service.ts +31 -1
  23. package/templates/shared/.env.example +1 -1
  24. package/templates/shared/README.md +41 -9
  25. package/templates/shared/scripts/dev.ts +37 -5
  26. package/templates/shared/service.jsonc +8 -2
  27. package/templates/shared/service.yaml +4 -1
  28. package/templates/targets/workers/.github/workflows/deploy.yml +3 -0
  29. package/templates/targets/workers/.github/workflows/preview.yml +3 -0
  30. package/templates/targets/workers/README.md +28 -0
  31. package/templates/targets/workers/package.json +6 -1
  32. package/templates/targets/workers/src/index.ts +36 -25
  33. package/templates/targets/workers/src/trigger.ts +81 -0
  34. package/templates/targets/workers/test/app.test.ts +46 -1
  35. package/templates/targets/workers/trigger/waitlist-follow-up.ts +24 -0
  36. package/templates/targets/workers/trigger.config.ts +24 -0
  37. package/templates/targets/workers/tsconfig.json +1 -1
  38. package/templates/targets/workers/wrangler.toml +2 -0
  39. package/templates/variants/bun-connectrpc/package.json +1 -1
  40. package/templates/variants/bun-connectrpc/src/index.ts +2 -6
  41. package/templates/variants/bun-connectrpc/src/temporal/client.ts +28 -0
  42. package/templates/variants/bun-connectrpc/src/temporal.ts +56 -0
  43. package/templates/variants/bun-connectrpc/src/waitlist/service.ts +16 -1
  44. package/templates/variants/bun-connectrpc/src/worker.ts +21 -0
  45. package/templates/variants/bun-connectrpc/test/app.test.ts +22 -0
  46. package/templates/variants/bun-hono/package.json +1 -1
  47. package/templates/variants/bun-hono/src/index.ts +2 -6
  48. package/templates/variants/bun-hono/src/temporal/client.ts +28 -0
  49. package/templates/variants/bun-hono/src/temporal.ts +56 -0
  50. package/templates/variants/bun-hono/src/waitlist/service.ts +10 -1
  51. package/templates/variants/bun-hono/src/worker.ts +21 -0
  52. package/templates/variants/go-chi/Dockerfile +2 -0
  53. package/templates/variants/go-chi/Makefile +1 -1
  54. package/templates/variants/go-chi/cmd/server/main.go +5 -4
  55. package/templates/variants/go-chi/cmd/worker/main.go +56 -0
  56. package/templates/variants/go-chi/internal/app/service.go +35 -3
  57. package/templates/variants/go-chi/internal/config/config.go +34 -3
  58. package/templates/variants/go-chi/internal/config/config_test.go +63 -0
  59. package/templates/variants/go-chi/internal/temporal/client.go +55 -0
  60. package/templates/variants/go-connectrpc/Dockerfile +2 -0
  61. package/templates/variants/go-connectrpc/Makefile +1 -1
  62. package/templates/variants/go-connectrpc/cmd/server/main.go +5 -4
  63. package/templates/variants/go-connectrpc/cmd/worker/main.go +56 -0
  64. package/templates/variants/go-connectrpc/internal/app/service.go +35 -3
  65. package/templates/variants/go-connectrpc/internal/config/config.go +34 -3
  66. package/templates/variants/go-connectrpc/internal/config/config_test.go +63 -0
  67. package/templates/variants/go-connectrpc/internal/temporal/client.go +55 -0
@@ -24,9 +24,9 @@ func Load() (Config, error) {
24
24
  cfg := Config{
25
25
  Port: envOr("PORT", "8080"),
26
26
  DatabaseURL: strings.TrimSpace(os.Getenv("DATABASE_URL")),
27
- TemporalEnabled: envBool("TEMPORAL_ENABLED"),
28
- TemporalAddress: envOr("TEMPORAL_ADDRESS", "localhost:7233"),
29
- TemporalNamespace: envOr("TEMPORAL_NAMESPACE", "default"),
27
+ TemporalEnabled: envBoolDefault("TEMPORAL_ENABLED", true),
28
+ TemporalAddress: envOrRuntime("TEMPORAL_ADDRESS", "localhost:7233"),
29
+ TemporalNamespace: envOrRuntime("TEMPORAL_NAMESPACE", "default"),
30
30
  TemporalTaskQueue: envOr("TEMPORAL_TASK_QUEUE", "{{SERVICE_NAME}}"),
31
31
  TemporalAPIKey: strings.TrimSpace(os.Getenv("TEMPORAL_API_KEY")),
32
32
  AuthEnabled: envBool("AUTH_ENABLED"),
@@ -37,6 +37,18 @@ func Load() (Config, error) {
37
37
  if cfg.DatabaseURL == "" {
38
38
  return Config{}, errors.New("DATABASE_URL is required")
39
39
  }
40
+ if cfg.TemporalEnabled {
41
+ missing := make([]string, 0, 2)
42
+ if cfg.TemporalAddress == "" {
43
+ missing = append(missing, "TEMPORAL_ADDRESS")
44
+ }
45
+ if cfg.TemporalNamespace == "" {
46
+ missing = append(missing, "TEMPORAL_NAMESPACE")
47
+ }
48
+ if len(missing) > 0 {
49
+ return Config{}, errors.New(strings.Join(missing, " and ") + " required when Temporal is enabled")
50
+ }
51
+ }
40
52
  return cfg, nil
41
53
  }
42
54
 
@@ -45,6 +57,14 @@ func envBool(key string) bool {
45
57
  return value == "1" || value == "true" || value == "yes" || value == "on"
46
58
  }
47
59
 
60
+ func envBoolDefault(key string, fallback bool) bool {
61
+ value := strings.TrimSpace(strings.ToLower(os.Getenv(key)))
62
+ if value == "" {
63
+ return fallback
64
+ }
65
+ return value == "1" || value == "true" || value == "yes" || value == "on"
66
+ }
67
+
48
68
  func envOr(key string, fallback string) string {
49
69
  value := os.Getenv(key)
50
70
  if value != "" {
@@ -52,3 +72,14 @@ func envOr(key string, fallback string) string {
52
72
  }
53
73
  return fallback
54
74
  }
75
+
76
+ func envOrRuntime(key string, localFallback string) string {
77
+ value := os.Getenv(key)
78
+ if value != "" {
79
+ return value
80
+ }
81
+ if strings.TrimSpace(os.Getenv("K_SERVICE")) != "" {
82
+ return ""
83
+ }
84
+ return localFallback
85
+ }
@@ -0,0 +1,63 @@
1
+ package config
2
+
3
+ import (
4
+ "strings"
5
+ "testing"
6
+ )
7
+
8
+ func TestLoadTemporalDefaultsToLocalDevelopment(t *testing.T) {
9
+ setRequiredEnv(t)
10
+
11
+ cfg, err := Load()
12
+ if err != nil {
13
+ t.Fatal(err)
14
+ }
15
+
16
+ if !cfg.TemporalEnabled {
17
+ t.Fatal("Temporal should default to enabled")
18
+ }
19
+ if cfg.TemporalAddress != "localhost:7233" {
20
+ t.Fatalf("unexpected Temporal address: %s", cfg.TemporalAddress)
21
+ }
22
+ if cfg.TemporalNamespace != "default" {
23
+ t.Fatalf("unexpected Temporal namespace: %s", cfg.TemporalNamespace)
24
+ }
25
+ if cfg.TemporalTaskQueue != "{{SERVICE_NAME}}" {
26
+ t.Fatalf("unexpected Temporal task queue: %s", cfg.TemporalTaskQueue)
27
+ }
28
+ }
29
+
30
+ func TestLoadTemporalOptOut(t *testing.T) {
31
+ setRequiredEnv(t)
32
+ t.Setenv("K_SERVICE", "svc")
33
+ t.Setenv("TEMPORAL_ENABLED", "false")
34
+
35
+ cfg, err := Load()
36
+ if err != nil {
37
+ t.Fatal(err)
38
+ }
39
+
40
+ if cfg.TemporalEnabled {
41
+ t.Fatal("Temporal should be disabled")
42
+ }
43
+ }
44
+
45
+ func TestLoadTemporalCloudRunFailsWithoutConnectionSettings(t *testing.T) {
46
+ setRequiredEnv(t)
47
+ t.Setenv("K_SERVICE", "svc")
48
+
49
+ _, err := Load()
50
+ if err == nil {
51
+ t.Fatal("expected Temporal configuration error")
52
+ }
53
+ if !strings.Contains(err.Error(), "TEMPORAL_ADDRESS and TEMPORAL_NAMESPACE") {
54
+ t.Fatalf("unexpected error: %v", err)
55
+ }
56
+ }
57
+
58
+ func setRequiredEnv(t *testing.T) {
59
+ t.Helper()
60
+ t.Setenv("DATABASE_URL", "postgres://postgres:postgres@127.0.0.1:5432/app")
61
+ t.Setenv("ATTACHMENT_BUCKET", "bucket")
62
+ t.Setenv("ATTACHMENT_PUBLIC_BASE_URL", "https://storage.test")
63
+ }
@@ -0,0 +1,55 @@
1
+ package temporalapp
2
+
3
+ import (
4
+ "context"
5
+
6
+ "{{MODULE_PATH}}/internal/app"
7
+
8
+ "go.temporal.io/sdk/client"
9
+ )
10
+
11
+ type TriggerDispatcher struct {
12
+ client client.Client
13
+ taskQueue string
14
+ }
15
+
16
+ func NewTriggerDispatcher(cfg WorkerConfig) (*TriggerDispatcher, error) {
17
+ options := client.Options{
18
+ HostPort: cfg.Address,
19
+ Namespace: cfg.Namespace,
20
+ }
21
+ if cfg.APIKey != "" {
22
+ options.Credentials = client.NewAPIKeyStaticCredentials(cfg.APIKey)
23
+ }
24
+
25
+ temporalClient, err := client.Dial(options)
26
+ if err != nil {
27
+ return nil, err
28
+ }
29
+ return &TriggerDispatcher{client: temporalClient, taskQueue: cfg.TaskQueue}, nil
30
+ }
31
+
32
+ func (d *TriggerDispatcher) Close() {
33
+ d.client.Close()
34
+ }
35
+
36
+ func (d *TriggerDispatcher) DispatchWaitlistFollowUp(ctx context.Context, trigger app.WaitlistTrigger) error {
37
+ _, err := d.client.ExecuteWorkflow(ctx, client.StartWorkflowOptions{
38
+ ID: "waitlist-follow-up-" + trigger.ID,
39
+ TaskQueue: d.taskQueue,
40
+ }, WaitlistFollowUpWorkflow, WaitlistFollowUpInput{
41
+ TriggerID: trigger.ID,
42
+ Email: triggerEmail(trigger.Payload),
43
+ Type: trigger.Type,
44
+ })
45
+ return err
46
+ }
47
+
48
+ func triggerEmail(payload any) string {
49
+ values, ok := payload.(map[string]any)
50
+ if !ok {
51
+ return ""
52
+ }
53
+ email, _ := values["email"].(string)
54
+ return email
55
+ }