@teamscala/service-boot 0.23.409 → 0.23.411

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -6,7 +6,7 @@
6
6
  },
7
7
  "name": "@teamscala/service-boot",
8
8
  "type": "module",
9
- "version": "0.23.409",
9
+ "version": "0.23.411",
10
10
  "sideEffects": false,
11
11
  "imports": {
12
12
  "#service-boot/*": "./src/*",
@@ -19,8 +19,8 @@ const SEND_CALLS: Array<{ url: string; headers: Record<string, string>; body: un
19
19
 
20
20
  mock.module("@teamscala/db/registry/load-config", () => ({
21
21
  loadRegistryConfig: async (type: string, slug: string) => {
22
- if (type === "service_install" && slug === "scala-messaging-service") {
23
- return { deploy_target: "railway", port: 8080 };
22
+ if (type === "config" && slug === "messaging-service-internal-url") {
23
+ return { internalBaseUrl: "http://scala-messaging-rust.railway.internal:8080" };
24
24
  }
25
25
  if (type === "secret" && slug === "railway-scala-messaging-whatsapp-webhook-secret") {
26
26
  return { token: "test-token" };
@@ -54,7 +54,7 @@ const originalFetch = globalThis.fetch;
54
54
  if (body.phone === "0-0-1-refuse") {
55
55
  return new Response("no", { status: 401 });
56
56
  }
57
- return Response.json({ message_id: "3EB07EE8TESTID" });
57
+ return Response.json({ ok: true, sent: true, messageId: "3EB07EE8TESTID" });
58
58
  };
59
59
 
60
60
  const { messagingWhatsappRouter } = await import("./messaging-whatsapp-router.ts");
@@ -73,7 +73,7 @@ const client = createRouterClient(
73
73
  ) as { messagingWhatsappSend: (a: unknown) => Promise<unknown> };
74
74
 
75
75
  describe("fn.messagingWhatsappSend", () => {
76
- test("derives the URL from service_install, sends with the decrypted header value, returns the message_id", async () => {
76
+ test("derives the URL from the config row, sends with the decrypted header value, returns the message_id", async () => {
77
77
  SEND_CALLS.length = 0;
78
78
  const result = await client.messagingWhatsappSend({
79
79
  phone: "61428193498",
@@ -82,7 +82,7 @@ describe("fn.messagingWhatsappSend", () => {
82
82
 
83
83
  expect(SEND_CALLS).toHaveLength(1);
84
84
  expect(SEND_CALLS[0]?.url).toBe(
85
- "http://scala-messaging-service.railway.internal:8080/api/messaging/whatsapp/send",
85
+ "http://scala-messaging-rust.railway.internal:8080/api/messaging/whatsapp/send",
86
86
  );
87
87
  expect(SEND_CALLS[0]?.headers["x-webhook-secret"]).toBe("test-token");
88
88
  expect(SEND_CALLS[0]?.body).toEqual({ phone: "61428193498", message: "migration probe" });
@@ -61,29 +61,34 @@ const authed = os.$context<Context>().use(async (opt) => {
61
61
  return await opt.next({ context: { ...context, user } });
62
62
  });
63
63
 
64
- /** service_install shape for deriving the messaging service's internal URL. */
65
- const MessagingInstallSchema = v.looseObject({
66
- port: v.optional(v.number(), 8080),
67
- deploy_target: v.string(),
64
+ /**
65
+ * Derive the messaging service base URL. The single source is the
66
+ * config/messaging-service-internal-url row (the Railway private domain is a
67
+ * Railway fact — the registry slug and the Railway service name differ, e.g.
68
+ * scala-messaging-service vs scala-messaging-rust — so the resolved URL lives
69
+ * in ONE registry row instead of being spelled per-caller).
70
+ */
71
+ const MessagingUrlSchema = v.looseObject({
72
+ internalBaseUrl: v.string(),
68
73
  });
69
74
 
70
75
  /** The sealed webhook-secret row shape (value is decryptSecretConfig input). */
71
76
  const MessagingSecretSchema = v.looseObject({});
72
77
 
73
- /** Derive the messaging service base URL from its service_install row. */
74
78
  async function resolveMessagingBaseUrl(): Promise<string> {
75
- const install = await loadRegistryConfig(
76
- "service_install",
77
- "scala-messaging-service",
78
- MessagingInstallSchema,
79
+ const row = await loadRegistryConfig(
80
+ "config",
81
+ "messaging-service-internal-url",
82
+ MessagingUrlSchema,
79
83
  );
80
- if (install.deploy_target !== "railway") {
84
+ const url = row.internalBaseUrl.trim().replace(/\/+$/, "");
85
+ if (!url.startsWith("http")) {
81
86
  throw new Error(
82
- `scala-messaging-service deploy_target is "${install.deploy_target}", not "railway" — ` +
83
- "the private-DNS URL derivation only holds on Railway. Fix the service_install row.",
87
+ "config/messaging-service-internal-url.internalBaseUrl must be an http(s) URL — " +
88
+ "the fn router refuses to guess a messaging service address.",
84
89
  );
85
90
  }
86
- return `http://scala-messaging-service.railway.internal:${install.port}`;
91
+ return url;
87
92
  }
88
93
 
89
94
  const sendSchema = v.object({
@@ -149,7 +154,8 @@ export const messagingWhatsappRouter = {
149
154
  };
150
155
  }
151
156
  const body = (await response.json().catch(() => null)) as {
152
- message_id?: string;
157
+ // The Rust handler answers { ok, sent, messageId } (camelCase).
158
+ messageId?: string;
153
159
  error?: string;
154
160
  } | null;
155
161
  if (!response.ok) {
@@ -165,11 +171,11 @@ export const messagingWhatsappRouter = {
165
171
  // parse_message_id contract).
166
172
  log.info("[fn.messagingWhatsappSend] sent", {
167
173
  phone,
168
- message_id: body?.message_id ?? null,
174
+ message_id: body?.messageId ?? null,
169
175
  });
170
176
  return {
171
177
  ok: true as const,
172
- message_id: body?.message_id ?? null,
178
+ message_id: body?.messageId ?? null,
173
179
  };
174
180
  } catch (err) {
175
181
  const errorMessage = err instanceof Error ? err.message : String(err);