create-svc 0.1.10 → 0.1.12

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 (171) hide show
  1. package/README.md +51 -47
  2. package/index.ts +2 -2
  3. package/package.json +10 -9
  4. package/src/cli.test.ts +28 -10
  5. package/src/cli.ts +196 -33
  6. package/src/git-bootstrap.test.ts +40 -0
  7. package/src/git-bootstrap.ts +110 -0
  8. package/src/naming.test.ts +1 -0
  9. package/src/naming.ts +23 -0
  10. package/src/post-scaffold.test.ts +19 -0
  11. package/src/post-scaffold.ts +17 -4
  12. package/src/profiles.ts +2 -5
  13. package/src/scaffold.test.ts +232 -41
  14. package/src/scaffold.ts +81 -36
  15. package/src/service.test.ts +30 -0
  16. package/src/service.ts +65 -0
  17. package/src/vault.test.ts +61 -1
  18. package/src/vault.ts +77 -15
  19. package/templates/shared/.github/workflows/ci.yml +2 -1
  20. package/templates/shared/.github/workflows/deploy.yml +2 -0
  21. package/templates/shared/README.md +124 -47
  22. package/templates/shared/grafana/alerts.yaml +54 -0
  23. package/templates/shared/grafana/waitlist-dashboard.json +63 -0
  24. package/templates/shared/scripts/authctl.ts +231 -0
  25. package/templates/shared/scripts/cloudrun/bootstrap.ts +14 -5
  26. package/templates/shared/scripts/cloudrun/cleanup.ts +64 -4
  27. package/templates/shared/scripts/cloudrun/cli.ts +329 -7
  28. package/templates/shared/scripts/cloudrun/config.ts +11 -4
  29. package/templates/shared/scripts/cloudrun/deploy.ts +0 -4
  30. package/templates/shared/scripts/cloudrun/lib.ts +174 -41
  31. package/templates/shared/scripts/cloudrun/neon.ts +45 -0
  32. package/templates/shared/scripts/dev.ts +22 -0
  33. package/templates/shared/scripts/ensure-local-db.ts +3 -0
  34. package/templates/shared/scripts/local-docker.ts +63 -0
  35. package/templates/shared/scripts/local-env.ts +27 -0
  36. package/templates/shared/scripts/seed.ts +73 -0
  37. package/templates/shared/scripts/wait-for-db.ts +32 -0
  38. package/templates/shared/service.config.ts +59 -0
  39. package/templates/shared/service.yaml +24 -44
  40. package/templates/targets/workers/.github/workflows/ci.yml +19 -0
  41. package/templates/targets/workers/.github/workflows/deploy.yml +19 -0
  42. package/templates/targets/workers/Makefile +33 -0
  43. package/templates/targets/workers/README.md +75 -0
  44. package/templates/targets/workers/package.json +35 -0
  45. package/templates/targets/workers/scripts/workers/cli.ts +402 -0
  46. package/templates/targets/workers/src/auth.ts +178 -0
  47. package/templates/targets/workers/src/index.ts +198 -0
  48. package/templates/targets/workers/src/storage.ts +370 -0
  49. package/templates/targets/workers/test/app.test.ts +108 -0
  50. package/templates/targets/workers/tsconfig.json +11 -0
  51. package/templates/targets/workers/wrangler.toml +24 -0
  52. package/templates/variants/bun-connectrpc/Makefile +14 -8
  53. package/templates/variants/bun-connectrpc/gen/protos/waitlist/v1/waitlist_pb.ts +424 -0
  54. package/templates/variants/bun-connectrpc/migrations/0000_init.sql +12 -55
  55. package/templates/variants/bun-connectrpc/package.json +12 -5
  56. package/templates/variants/bun-connectrpc/protos/waitlist/v1/waitlist.proto +91 -0
  57. package/templates/variants/bun-connectrpc/scripts/codegen.ts +1 -1
  58. package/templates/variants/bun-connectrpc/scripts/migrate.ts +4 -1
  59. package/templates/variants/bun-connectrpc/src/auth.ts +200 -0
  60. package/templates/variants/bun-connectrpc/src/db/repository.ts +67 -420
  61. package/templates/variants/bun-connectrpc/src/db/schema.ts +15 -64
  62. package/templates/variants/bun-connectrpc/src/index.ts +76 -176
  63. package/templates/variants/bun-connectrpc/src/temporal/activities.ts +14 -0
  64. package/templates/variants/bun-connectrpc/src/temporal/worker.ts +38 -0
  65. package/templates/variants/bun-connectrpc/src/temporal/workflows.ts +10 -0
  66. package/templates/variants/bun-connectrpc/src/waitlist/service.ts +172 -0
  67. package/templates/variants/bun-connectrpc/src/waitlist/types.ts +45 -0
  68. package/templates/variants/bun-connectrpc/test/app.test.ts +4 -4
  69. package/templates/variants/bun-connectrpc/test/waitlist.integration.test.ts +71 -0
  70. package/templates/variants/bun-hono/Makefile +14 -8
  71. package/templates/variants/bun-hono/migrations/0000_init.sql +12 -55
  72. package/templates/variants/bun-hono/package.json +12 -5
  73. package/templates/variants/bun-hono/scripts/migrate.ts +4 -1
  74. package/templates/variants/bun-hono/src/auth.ts +181 -0
  75. package/templates/variants/bun-hono/src/db/repository.ts +68 -421
  76. package/templates/variants/bun-hono/src/db/schema.ts +15 -64
  77. package/templates/variants/bun-hono/src/index.ts +65 -180
  78. package/templates/variants/bun-hono/src/temporal/activities.ts +14 -0
  79. package/templates/variants/bun-hono/src/temporal/worker.ts +38 -0
  80. package/templates/variants/bun-hono/src/temporal/workflows.ts +10 -0
  81. package/templates/variants/bun-hono/src/waitlist/service.ts +166 -0
  82. package/templates/variants/bun-hono/src/waitlist/types.ts +50 -0
  83. package/templates/variants/bun-hono/test/app.test.ts +72 -41
  84. package/templates/variants/bun-hono/test/waitlist.integration.test.ts +102 -0
  85. package/templates/variants/go-chi/Makefile +27 -11
  86. package/templates/variants/go-chi/atlas.hcl +8 -0
  87. package/templates/variants/go-chi/cmd/server/main.go +21 -10
  88. package/templates/variants/go-chi/go.mod +1 -3
  89. package/templates/variants/go-chi/internal/app/service.go +202 -685
  90. package/templates/variants/go-chi/internal/auth/middleware.go +289 -0
  91. package/templates/variants/go-chi/internal/auth/middleware_test.go +38 -0
  92. package/templates/variants/go-chi/internal/config/config.go +27 -11
  93. package/templates/variants/go-chi/internal/httpapi/routes.go +78 -157
  94. package/templates/variants/go-chi/internal/httpapi/waitlist_integration_test.go +199 -0
  95. package/templates/variants/go-chi/internal/temporal/activities.go +27 -0
  96. package/templates/variants/go-chi/internal/temporal/worker.go +42 -0
  97. package/templates/variants/go-chi/internal/temporal/workflows.go +18 -0
  98. package/templates/variants/go-chi/migrations/0000_init.sql +12 -55
  99. package/templates/variants/go-chi/migrations/atlas.sum +2 -0
  100. package/templates/variants/go-chi/package.json +7 -1
  101. package/templates/variants/go-connectrpc/Makefile +26 -9
  102. package/templates/variants/go-connectrpc/atlas.hcl +8 -0
  103. package/templates/variants/go-connectrpc/buf.gen.yaml +2 -2
  104. package/templates/variants/go-connectrpc/cmd/server/main.go +23 -12
  105. package/templates/variants/go-connectrpc/gen/waitlist/v1/waitlist.pb.go +960 -0
  106. package/templates/variants/go-connectrpc/gen/waitlist/v1/waitlistv1connect/waitlist.connect.go +283 -0
  107. package/templates/variants/go-connectrpc/go.mod +1 -1
  108. package/templates/variants/go-connectrpc/internal/app/service.go +202 -685
  109. package/templates/variants/go-connectrpc/internal/auth/middleware.go +289 -0
  110. package/templates/variants/go-connectrpc/internal/auth/middleware_test.go +38 -0
  111. package/templates/variants/go-connectrpc/internal/config/config.go +27 -11
  112. package/templates/variants/go-connectrpc/internal/connectapi/handler.go +78 -201
  113. package/templates/variants/go-connectrpc/internal/connectapi/waitlist_integration_test.go +122 -0
  114. package/templates/variants/go-connectrpc/internal/httpapi/routes.go +147 -9
  115. package/templates/variants/go-connectrpc/internal/temporal/activities.go +27 -0
  116. package/templates/variants/go-connectrpc/internal/temporal/worker.go +42 -0
  117. package/templates/variants/go-connectrpc/internal/temporal/workflows.go +18 -0
  118. package/templates/variants/go-connectrpc/migrations/0000_init.sql +12 -55
  119. package/templates/variants/go-connectrpc/migrations/atlas.sum +2 -0
  120. package/templates/variants/go-connectrpc/package.json +7 -1
  121. package/templates/variants/go-connectrpc/protos/waitlist/v1/waitlist.proto +93 -0
  122. package/templates/root/.github/workflows/buf-publish.yml +0 -19
  123. package/templates/root/.github/workflows/ci.yml +0 -26
  124. package/templates/root/.github/workflows/deploy.yml +0 -22
  125. package/templates/root/Dockerfile +0 -23
  126. package/templates/root/README.md +0 -69
  127. package/templates/root/buf.gen.yaml +0 -10
  128. package/templates/root/buf.yaml +0 -9
  129. package/templates/root/cmd/server/main.go +0 -44
  130. package/templates/root/gen/dns/v1/dns.pb.go +0 -623
  131. package/templates/root/gen/dns/v1/dnsv1connect/dns.connect.go +0 -192
  132. package/templates/root/go.mod +0 -10
  133. package/templates/root/internal/app/service.go +0 -152
  134. package/templates/root/internal/app/token_source.go +0 -50
  135. package/templates/root/internal/cloudflare/client.go +0 -160
  136. package/templates/root/internal/config/config.go +0 -55
  137. package/templates/root/internal/connectapi/handler.go +0 -79
  138. package/templates/root/internal/httpapi/routes.go +0 -93
  139. package/templates/root/internal/vault/client.go +0 -148
  140. package/templates/root/package.json +0 -12
  141. package/templates/root/protos/dns/v1/dns.proto +0 -58
  142. package/templates/root/scripts/cloudrun/bootstrap.ts +0 -65
  143. package/templates/root/scripts/cloudrun/config.ts +0 -50
  144. package/templates/root/scripts/cloudrun/deploy.ts +0 -41
  145. package/templates/root/scripts/cloudrun/lib.ts +0 -244
  146. package/templates/root/service.yaml +0 -50
  147. package/templates/root/test/go.test.ts +0 -19
  148. package/templates/shared/scripts/cloudrun/integrations.ts +0 -111
  149. package/templates/variants/bun-connectrpc/gen/protos/chat/v1/chat_pb.ts +0 -1078
  150. package/templates/variants/bun-connectrpc/protos/chat/v1/chat.proto +0 -228
  151. package/templates/variants/bun-connectrpc/src/chat/service.ts +0 -384
  152. package/templates/variants/bun-connectrpc/src/chat/types.ts +0 -142
  153. package/templates/variants/bun-connectrpc/src/storage.ts +0 -72
  154. package/templates/variants/bun-connectrpc/src/webhooks.ts +0 -35
  155. package/templates/variants/bun-connectrpc/test/list-messages.integration.test.ts +0 -182
  156. package/templates/variants/bun-hono/src/chat/service.ts +0 -384
  157. package/templates/variants/bun-hono/src/chat/types.ts +0 -142
  158. package/templates/variants/bun-hono/src/storage.ts +0 -72
  159. package/templates/variants/bun-hono/src/webhooks.ts +0 -35
  160. package/templates/variants/bun-hono/test/list-messages.integration.test.ts +0 -256
  161. package/templates/variants/go-chi/buf.gen.yaml +0 -12
  162. package/templates/variants/go-chi/buf.yaml +0 -9
  163. package/templates/variants/go-chi/cmd/migrate/main.go +0 -101
  164. package/templates/variants/go-chi/internal/httpapi/list_messages_integration_test.go +0 -298
  165. package/templates/variants/go-chi/protos/chat/v1/chat.proto +0 -219
  166. package/templates/variants/go-connectrpc/cmd/migrate/main.go +0 -101
  167. package/templates/variants/go-connectrpc/gen/chat/v1/chat.pb.go +0 -2512
  168. package/templates/variants/go-connectrpc/gen/chat/v1/chatv1connect/chat.connect.go +0 -571
  169. package/templates/variants/go-connectrpc/internal/connectapi/list_messages_integration_test.go +0 -216
  170. package/templates/variants/go-connectrpc/protos/chat/v1/chat.proto +0 -232
  171. /package/bin/{create-svc.mjs → service.mjs} +0 -0
@@ -1,17 +1,48 @@
1
1
  #!/usr/bin/env bun
2
2
 
3
+ import { mkdir } from "node:fs/promises";
4
+ import { ensureAuthResourceServer, runAuthCommand, runAuthDoctor } from "../authctl";
3
5
  import { bootstrap } from "./bootstrap";
4
6
  import { cleanup } from "./cleanup";
5
7
  import { deploy } from "./deploy";
6
- import { runMain } from "./lib";
8
+ import { config } from "./config";
9
+ import {
10
+ accessSecretVersion,
11
+ assertProductionDomainAvailable,
12
+ assertServiceNameAvailable,
13
+ describeProductionDomainMapping,
14
+ formatError,
15
+ gcloud,
16
+ ensureProductionDomainMapping,
17
+ requireCommand,
18
+ requireGcloudAuth,
19
+ resolveDeploymentTarget,
20
+ run,
21
+ runMain,
22
+ runStep,
23
+ serviceOrigin,
24
+ } from "./lib";
7
25
 
8
26
  async function main(argv = Bun.argv.slice(2)) {
9
27
  const [command, ...rest] = argv;
10
28
 
11
- if (command === "bootstrap") {
12
- await runMain("Bootstrap", async () => {
29
+ if (!command || command === "--help" || command === "-h" || command === "help") {
30
+ console.log("Usage: service <create|deploy|migrate|seed|dashboards|dns|doctor|destroy|auth|sdk> [args]");
31
+ return;
32
+ }
33
+
34
+ if (command === "create") {
35
+ await runMain("Create", async () => {
36
+ assertServiceNameAvailable(config.serviceName);
37
+ assertProductionDomainAvailable(config.serviceName);
38
+ await runStep("Registering auth resource server", () => ensureAuthResourceServer());
13
39
  await bootstrap();
14
- return "Bootstrap finished";
40
+ const target = resolveDeploymentTarget("main");
41
+ const databaseUrl = await runStep("Reading production database URL", () => accessSecretVersion(target.databaseSecretName));
42
+ await runStep("Applying production migrations", () => runLanguageTask("migrate", { DATABASE_URL: databaseUrl }));
43
+ const origin = await deploy(["--ci"]);
44
+ await runOptionalBunScript("seed", { DATABASE_URL: databaseUrl });
45
+ return `Created ${origin}`;
15
46
  });
16
47
  return;
17
48
  }
@@ -21,12 +52,303 @@ async function main(argv = Bun.argv.slice(2)) {
21
52
  return;
22
53
  }
23
54
 
24
- if (command === "cleanup") {
25
- await runMain("Cleanup", () => cleanup(rest));
55
+ if (command === "migrate") {
56
+ await runMain("Migrate", () => runLanguageTask("migrate"));
57
+ return;
58
+ }
59
+
60
+ if (command === "seed") {
61
+ await runMain("Seed", () => runOptionalBunScript("seed"));
62
+ return;
63
+ }
64
+
65
+ if (command === "dashboards") {
66
+ await runMain("Dashboards", () => runDashboards());
67
+ return;
68
+ }
69
+
70
+ if (command === "dns") {
71
+ await runMain("DNS", () => repairDns());
72
+ return;
73
+ }
74
+
75
+ if (command === "doctor") {
76
+ await runMain("Doctor", () => runDoctor());
77
+ return;
78
+ }
79
+
80
+ if (command === "auth") {
81
+ await runMain("Auth", () => runAuthCommand(rest));
82
+ return;
83
+ }
84
+
85
+ if (command === "destroy") {
86
+ await runMain("Destroy", () => cleanup(rest));
26
87
  return;
27
88
  }
28
89
 
29
- throw new Error("Usage: svc-cloudrun <bootstrap|deploy|cleanup> [args]");
90
+ if (command === "sdk") {
91
+ await runMain("SDK", () => runSdk(rest));
92
+ return;
93
+ }
94
+
95
+ throw new Error("Usage: service <create|deploy|migrate|seed|dashboards|dns|doctor|destroy|auth|sdk> [args]");
96
+ }
97
+
98
+ function runLanguageTask(task: "migrate", env?: Record<string, string | undefined>) {
99
+ if (config.runtime === "bun") {
100
+ run("bun", ["run", `./scripts/${task}.ts`], { env });
101
+ return `${task} finished`;
102
+ }
103
+
104
+ if (task === "migrate") {
105
+ run("make", ["migrate"], { env });
106
+ return `${task} finished`;
107
+ }
108
+
109
+ throw new Error(`${task} is not available for ${config.runtime}`);
110
+ }
111
+
112
+ async function runOptionalBunScript(name: string, env?: Record<string, string | undefined>) {
113
+ const scriptPath = `./scripts/${name}.ts`;
114
+ if (!(await Bun.file(scriptPath).exists())) {
115
+ return `${name} script is not configured`;
116
+ }
117
+
118
+ run("bun", ["run", scriptPath], { env });
119
+ return `${name} finished`;
120
+ }
121
+
122
+ function runDashboards() {
123
+ requireCommand("gcx");
124
+ run("gcx", ["dev", "lint", "run", "./grafana", "-o", "compact"]);
125
+ run("gcx", ["resources", "push", "--path", "./grafana"]);
126
+ return "Dashboards pushed";
127
+ }
128
+
129
+ function repairDns() {
130
+ ensureProductionDomainMapping(config.serviceName);
131
+ return `DNS mapping ready for https://${config.domain.hostname}`;
132
+ }
133
+
134
+ async function runDoctor() {
135
+ const results: Array<{ name: string; status: "pass" | "warn" | "fail"; detail: string }> = [];
136
+ const target = resolveDeploymentTarget("main");
137
+
138
+ await record(results, "bun CLI", "fail", () => checkCommand("bun"));
139
+ await record(results, "gcloud CLI", "fail", () => checkCommand("gcloud"));
140
+ await record(results, "gcloud auth", "fail", () => {
141
+ requireGcloudAuth();
142
+ return "active account available";
143
+ });
144
+ await record(results, "GCP project", "fail", () => {
145
+ gcloud(["projects", "describe", config.project.id, "--format=value(projectId)"]);
146
+ return config.project.id;
147
+ });
148
+ await record(results, "Cloud Run service", "fail", () => {
149
+ const serviceName = gcloud([
150
+ "run",
151
+ "services",
152
+ "describe",
153
+ target.serviceName,
154
+ "--project",
155
+ config.project.id,
156
+ "--region",
157
+ config.region,
158
+ "--format=value(metadata.name)",
159
+ ]).stdout;
160
+ return serviceName || target.serviceName;
161
+ });
162
+ await record(results, "runtime database secret", "fail", () => {
163
+ const value = accessSecretVersion(target.databaseSecretName);
164
+ if (!value.startsWith("postgres://") && !value.startsWith("postgresql://")) {
165
+ throw new Error(`${target.databaseSecretName} does not look like a Postgres URL`);
166
+ }
167
+ return target.databaseSecretName;
168
+ });
169
+ await record(results, "DNS mapping", "fail", () => {
170
+ const mapping = describeProductionDomainMapping();
171
+ const mappedService = mapping?.spec?.routeName;
172
+ if (mappedService !== target.serviceName) {
173
+ throw new Error(`${config.domain.hostname} maps to ${mappedService || "nothing"}, expected ${target.serviceName}`);
174
+ }
175
+ return `${config.domain.hostname} -> ${target.serviceName}`;
176
+ });
177
+ await record(results, "deployment health", "fail", async () => {
178
+ const response = await fetchWithTimeout(`${serviceOrigin(target)}/healthz`, 5_000);
179
+ if (!response.ok) {
180
+ throw new Error(`GET /healthz returned ${response.status}`);
181
+ }
182
+ return "GET /healthz ok";
183
+ });
184
+ await record(results, "migration assets", "fail", async () => {
185
+ if (!(await Bun.file("./migrations/0000_init.sql").exists())) {
186
+ throw new Error("missing migrations/0000_init.sql");
187
+ }
188
+ return "migrations/0000_init.sql";
189
+ });
190
+ if ((config.runtime as string) === "go") {
191
+ await record(results, "Atlas CLI", "fail", () => checkCommand("atlas"));
192
+ await record(results, "Atlas config", "fail", async () => {
193
+ if (!(await Bun.file("./atlas.hcl").exists())) {
194
+ throw new Error("missing atlas.hcl");
195
+ }
196
+ return "atlas.hcl";
197
+ });
198
+ }
199
+ await record(results, "dashboard tooling", "warn", () => {
200
+ if (!Bun.which("gcx")) {
201
+ throw new Error("gcx is not installed");
202
+ }
203
+ return "gcx available";
204
+ });
205
+ await record(results, "dashboard artifacts", "warn", async () => {
206
+ if (!(await Bun.file("./grafana").exists()) && !(await Bun.file("./dashboards").exists())) {
207
+ throw new Error("no grafana/ or dashboards/ directory found");
208
+ }
209
+ return "dashboard directory found";
210
+ });
211
+ await record(results, "authctl", "warn", () => runAuthDoctor().detail);
212
+ await record(results, "Temporal/Cron", "warn", async () => {
213
+ const hasBunTemporal = await Bun.file("./src/temporal/worker.ts").exists();
214
+ const hasGoTemporal = await Bun.file("./internal/temporal/worker.go").exists();
215
+ if (!hasBunTemporal && !hasGoTemporal) {
216
+ throw new Error("Temporal worker config is not present in this scaffold yet");
217
+ }
218
+ return "Temporal worker config present";
219
+ });
220
+
221
+ if ((config.framework as string) === "connectrpc") {
222
+ await record(results, "ConnectRPC proto", "fail", async () => {
223
+ if (!(await Bun.file("./buf.yaml").exists())) {
224
+ throw new Error("missing buf.yaml");
225
+ }
226
+ if (!(await Bun.file("./protos/waitlist/v1/waitlist.proto").exists())) {
227
+ throw new Error("missing waitlist proto");
228
+ }
229
+ return "waitlist proto present";
230
+ });
231
+ await record(results, "Buf CLI", "warn", () => checkCommand("buf"));
232
+ await record(results, "generated SDK artifacts", "warn", async () => {
233
+ const bunGen = await Bun.file("./gen/protos/waitlist/v1/waitlist_pb.ts").exists();
234
+ const goGen = await Bun.file("./gen/waitlist/v1/waitlist.pb.go").exists();
235
+ if (!bunGen && !goGen) {
236
+ throw new Error("generated SDK artifacts are missing; run service sdk build");
237
+ }
238
+ return "local generated artifacts present";
239
+ });
240
+ await record(results, "SDK mode", "warn", async () => {
241
+ const text = await Bun.file(".service/sdk.json").text();
242
+ const state = JSON.parse(text) as { mode?: string; module?: string };
243
+ if (state.mode !== "local" && state.mode !== "remote") {
244
+ throw new Error("SDK mode must be local or remote");
245
+ }
246
+ return `${state.mode}: ${state.module || bufModule()}`;
247
+ });
248
+ }
249
+
250
+ const output = results.map(formatDoctorResult).join("\n");
251
+ const failures = results.filter((result) => result.status === "fail");
252
+ if (failures.length > 0) {
253
+ throw new Error(`Doctor found ${failures.length} failing check(s)\n${output}`);
254
+ }
255
+ return output;
256
+ }
257
+
258
+ async function record(
259
+ results: Array<{ name: string; status: "pass" | "warn" | "fail"; detail: string }>,
260
+ name: string,
261
+ failureStatus: "warn" | "fail",
262
+ check: () => string | Promise<string>
263
+ ) {
264
+ try {
265
+ results.push({ name, status: "pass", detail: await check() });
266
+ } catch (error) {
267
+ results.push({ name, status: failureStatus, detail: formatError(error) });
268
+ }
269
+ }
270
+
271
+ function checkCommand(name: string) {
272
+ const path = Bun.which(name);
273
+ if (!path) {
274
+ throw new Error(`${name} is not installed`);
275
+ }
276
+ return path;
277
+ }
278
+
279
+ async function fetchWithTimeout(url: string, timeoutMs: number) {
280
+ return await fetch(url, { signal: AbortSignal.timeout(timeoutMs) });
281
+ }
282
+
283
+ function formatDoctorResult(result: { name: string; status: "pass" | "warn" | "fail"; detail: string }) {
284
+ const marker = result.status === "pass" ? "PASS" : result.status === "warn" ? "WARN" : "FAIL";
285
+ return `[${marker}] ${result.name}: ${result.detail}`;
286
+ }
287
+
288
+ async function runSdk(args: string[]) {
289
+ if ((config.framework as string) !== "connectrpc") {
290
+ throw new Error("SDK commands are only available for ConnectRPC services");
291
+ }
292
+
293
+ const [subcommand] = args;
294
+ if (subcommand === "publish") {
295
+ requireCommand("buf");
296
+ run("buf", ["push"]);
297
+ return "Schema pushed to Buf Schema Registry";
298
+ }
299
+
300
+ if (subcommand === "build") {
301
+ if (config.runtime === "bun") {
302
+ run("bun", ["run", "gen"]);
303
+ } else {
304
+ run("make", ["gen"]);
305
+ }
306
+ await writeSdkMode("local");
307
+ return "Local SDK artifacts generated and selected";
308
+ }
309
+
310
+ if (subcommand === "use-local") {
311
+ await assertLocalSdkArtifacts();
312
+ await writeSdkMode("local");
313
+ return "Local SDK artifacts selected";
314
+ }
315
+
316
+ if (subcommand === "use-remote") {
317
+ await writeSdkMode("remote");
318
+ return `Remote Buf SDK selected: ${bufModule()}`;
319
+ }
320
+
321
+ throw new Error("Usage: service sdk <build|publish|use-local|use-remote>");
322
+ }
323
+
324
+ async function assertLocalSdkArtifacts() {
325
+ const bunArtifacts = await Bun.file("./gen/protos/waitlist/v1/waitlist_pb.ts").exists();
326
+ const goArtifacts = await Bun.file("./gen/waitlist/v1/waitlist.pb.go").exists();
327
+ if (!bunArtifacts && !goArtifacts) {
328
+ throw new Error("Local SDK artifacts are missing. Run `service sdk build` first.");
329
+ }
330
+ }
331
+
332
+ async function writeSdkMode(mode: "local" | "remote") {
333
+ await mkdir(".service", { recursive: true });
334
+ const localPath = config.runtime === "bun" ? "./gen/protos/waitlist/v1" : "./gen/waitlist/v1";
335
+ await Bun.write(
336
+ ".service/sdk.json",
337
+ `${JSON.stringify(
338
+ {
339
+ mode,
340
+ module: bufModule(),
341
+ localPath,
342
+ updatedAt: new Date().toISOString(),
343
+ },
344
+ null,
345
+ 2
346
+ )}\n`
347
+ );
348
+ }
349
+
350
+ function bufModule() {
351
+ return `buf.build/anmho/${config.serviceName}`;
30
352
  }
31
353
 
32
354
  if (import.meta.main) {
@@ -23,9 +23,17 @@ export const config = {
23
23
  hostname: "{{API_HOSTNAME}}",
24
24
  baseDomain: "{{API_BASE_DOMAIN}}",
25
25
  },
26
- storage: {
27
- attachmentBucket: "{{ATTACHMENT_BUCKET}}",
28
- attachmentPublicBaseUrl: "{{ATTACHMENT_PUBLIC_BASE_URL}}",
26
+ auth: {
27
+ issuer: "https://auth.anmho.com/api/auth",
28
+ audience: "api://{{SERVICE_ID}}",
29
+ jwksUrl: "https://auth.anmho.com/api/auth/jwks",
30
+ },
31
+ temporal: {
32
+ enabled: false,
33
+ address: "localhost:7233",
34
+ namespace: "default",
35
+ taskQueue: "{{SERVICE_ID}}",
36
+ apiKeySecretName: "{{SERVICE_ID}}-temporal-api-key",
29
37
  },
30
38
  neon: {
31
39
  projectId: "{{NEON_PROJECT_ID}}",
@@ -44,7 +52,6 @@ export const config = {
44
52
  "iamcredentials.googleapis.com",
45
53
  "secretmanager.googleapis.com",
46
54
  "serviceusage.googleapis.com",
47
- "storage.googleapis.com",
48
55
  "sts.googleapis.com",
49
56
  ],
50
57
  } as const;
@@ -1,6 +1,5 @@
1
1
  import { config } from "./config";
2
2
  import { bootstrap } from "./bootstrap";
3
- import { publishProviderRuntimeSecrets } from "./integrations";
4
3
  import { deleteBranch, ensureBranch, ensureDatabase, getConnectionUri, listBranches, resolveNeonConfig } from "./neon";
5
4
  import {
6
5
  addSecretVersion,
@@ -63,9 +62,6 @@ export async function deploy(args = Bun.argv.slice(2)) {
63
62
  addSecretVersion(target.databaseSecretName, connectionUri);
64
63
  ensureSecretAccessor(target.databaseSecretName, `serviceAccount:${config.runtimeServiceAccount}`);
65
64
  });
66
-
67
- await runStep("Publishing environment provider secrets", () => publishProviderRuntimeSecrets(target));
68
-
69
65
  const image = imageUrl();
70
66
  await runStep("Building container image", () =>
71
67
  gcloud(["builds", "submit", "--project", config.project.id, "--region", config.region, "--tag", image])