@fonoster/common 0.27.0 → 0.31.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (209) hide show
  1. package/dist/{voice/PlayDtmf.d.ts → agents/index.d.ts} +3 -5
  2. package/dist/{assistants → agents}/index.js +2 -6
  3. package/dist/agents/sessionAgentSchema.d.ts +755 -0
  4. package/dist/agents/sessionAgentSchema.js +117 -0
  5. package/dist/{voice/Dial.d.ts → calls/index.d.ts} +16 -13
  6. package/dist/calls/index.js +44 -0
  7. package/dist/cellsecrets/index.d.ts +91 -0
  8. package/dist/cellsecrets/index.js +299 -0
  9. package/dist/config/cli/check.d.ts +6 -0
  10. package/dist/config/cli/check.js +202 -0
  11. package/dist/config/cli/envFile.d.ts +23 -0
  12. package/dist/config/cli/envFile.js +81 -0
  13. package/dist/config/cli/exposure.d.ts +10 -0
  14. package/dist/config/cli/exposure.js +93 -0
  15. package/dist/config/cli/index.d.ts +2 -0
  16. package/dist/{identity/tokenHasAccessKeyId.js → config/cli/index.js} +7 -8
  17. package/dist/config/cli/info.d.ts +6 -0
  18. package/dist/config/cli/info.js +62 -0
  19. package/dist/config/cli/init.d.ts +9 -0
  20. package/dist/config/cli/init.js +152 -0
  21. package/dist/config/cli/main.d.ts +11 -0
  22. package/dist/config/cli/main.js +155 -0
  23. package/dist/config/cli/render.d.ts +44 -0
  24. package/dist/config/cli/render.js +129 -0
  25. package/dist/config/cli/secrets.d.ts +21 -0
  26. package/dist/config/cli/secrets.js +117 -0
  27. package/dist/config/cli/types.d.ts +24 -0
  28. package/dist/config/cli/types.js +7 -0
  29. package/dist/{assistants/index.d.ts → config/configHints.d.ts} +8 -7
  30. package/dist/config/configHints.js +47 -0
  31. package/dist/{voice → config}/index.d.ts +11 -13
  32. package/dist/{voice → config}/index.js +11 -13
  33. package/dist/config/interpolateEnv.d.ts +34 -0
  34. package/dist/config/interpolateEnv.js +70 -0
  35. package/dist/config/loadServiceConfig.d.ts +28 -0
  36. package/dist/config/loadServiceConfig.js +112 -0
  37. package/dist/config/logsSchema.d.ts +76 -0
  38. package/dist/config/logsSchema.js +49 -0
  39. package/dist/config/natsConnection.d.ts +53 -0
  40. package/dist/config/natsConnection.js +58 -0
  41. package/dist/config/parseConfigFile.d.ts +7 -0
  42. package/dist/config/parseConfigFile.js +60 -0
  43. package/dist/config/resolveConfigFile.d.ts +17 -0
  44. package/dist/config/resolveConfigFile.js +54 -0
  45. package/dist/config/resolveSecretRefs.d.ts +35 -0
  46. package/dist/config/resolveSecretRefs.js +129 -0
  47. package/dist/{assistants/audioFiltersSchema.d.ts → config/strictDeep.d.ts} +9 -12
  48. package/dist/config/strictDeep.js +58 -0
  49. package/dist/config/telemetrySchema.d.ts +39 -0
  50. package/dist/config/telemetrySchema.js +44 -0
  51. package/dist/config/toJsonSchema.d.ts +30 -0
  52. package/dist/config/toJsonSchema.js +161 -0
  53. package/dist/constants.d.ts +1 -6
  54. package/dist/constants.js +4 -7
  55. package/dist/envs.d.ts +2 -1
  56. package/dist/envs.js +13 -11
  57. package/dist/errors/handleZodError.js +3 -1
  58. package/dist/filters/index.d.ts +70 -0
  59. package/dist/filters/index.js +101 -0
  60. package/dist/identity/createAuthInterceptor.d.ts +9 -2
  61. package/dist/identity/createAuthInterceptor.js +36 -30
  62. package/dist/identity/hasAccess.d.ts +9 -3
  63. package/dist/identity/hasAccess.js +32 -8
  64. package/dist/identity/index.d.ts +1 -0
  65. package/dist/identity/index.js +1 -0
  66. package/dist/identity/isValidToken.d.ts +12 -2
  67. package/dist/identity/isValidToken.js +25 -9
  68. package/dist/identity/roles.js +33 -7
  69. package/dist/identity/types.d.ts +6 -1
  70. package/dist/identity/types.js +1 -0
  71. package/dist/{assistants/LanguageModelProvider.js → identity/verifyAccessToken.d.ts} +10 -11
  72. package/dist/identity/verifyAccessToken.js +44 -0
  73. package/dist/index.d.ts +15 -2
  74. package/dist/index.js +18 -3
  75. package/dist/net/assertPublicDestination.d.ts +52 -0
  76. package/dist/net/assertPublicDestination.js +342 -0
  77. package/dist/{voice/StopSay.d.ts → net/index.d.ts} +1 -6
  78. package/dist/net/index.js +35 -0
  79. package/dist/notifications/createSendEmail.js +3 -1
  80. package/dist/protos/applications.proto +138 -3
  81. package/dist/protos/authz.proto +26 -4
  82. package/dist/protos/billing.proto +92 -0
  83. package/dist/protos/calls.proto +4 -0
  84. package/dist/protos/capabilities.proto +102 -0
  85. package/dist/protos/identity.proto +26 -1
  86. package/dist/protos/providers.proto +139 -0
  87. package/dist/protos/roomr.proto +146 -0
  88. package/dist/protos/session.proto +194 -0
  89. package/dist/protos/usage.proto +59 -0
  90. package/dist/protos/webhooks.proto +140 -0
  91. package/dist/{utils/types.d.ts → regions/hub/index.d.ts} +1 -5
  92. package/dist/regions/hub/index.js +35 -0
  93. package/dist/regions/hub/natsJwt.d.ts +63 -0
  94. package/dist/regions/hub/natsJwt.js +195 -0
  95. package/dist/registry/Registry.d.ts +87 -0
  96. package/dist/registry/Registry.js +432 -0
  97. package/dist/registry/cli.d.ts +20 -0
  98. package/dist/registry/cli.js +436 -0
  99. package/dist/registry/features.d.ts +31 -0
  100. package/dist/registry/features.js +38 -0
  101. package/dist/registry/index.d.ts +24 -0
  102. package/dist/registry/index.js +40 -0
  103. package/dist/registry/prisma.d.ts +67 -0
  104. package/dist/registry/prisma.js +399 -0
  105. package/dist/registry/stores.d.ts +21 -0
  106. package/dist/{assistants/languageModelConfigSchema.js → registry/stores.js} +55 -38
  107. package/dist/registry/types.d.ts +110 -0
  108. package/dist/{assistants/LanguageModelProvider.d.ts → registry/types.js} +6 -8
  109. package/dist/registry/workspaceKeys.d.ts +41 -0
  110. package/dist/registry/workspaceKeys.js +64 -0
  111. package/dist/roomr/health.d.ts +9 -0
  112. package/dist/{assistants/eventsHookSchema.js → roomr/health.js} +22 -18
  113. package/dist/roomr/index.d.ts +75 -0
  114. package/dist/roomr/index.js +146 -0
  115. package/dist/roomr/session.d.ts +133 -0
  116. package/dist/roomr/session.js +132 -0
  117. package/dist/session/index.d.ts +19 -0
  118. package/dist/session/index.js +35 -0
  119. package/dist/session/schema.d.ts +2140 -0
  120. package/dist/session/schema.js +448 -0
  121. package/dist/sessions/index.d.ts +96 -0
  122. package/dist/sessions/index.js +197 -0
  123. package/dist/telemetry.d.ts +76 -0
  124. package/dist/telemetry.js +360 -0
  125. package/dist/types.d.ts +3 -5
  126. package/dist/{voice/Gather.d.ts → usage/CallUsage.d.ts} +19 -18
  127. package/dist/usage/CallUsage.js +76 -0
  128. package/dist/usage/index.d.ts +22 -0
  129. package/dist/usage/index.js +38 -0
  130. package/dist/usage/meters.d.ts +113 -0
  131. package/dist/usage/meters.js +90 -0
  132. package/dist/usage/types.d.ts +159 -0
  133. package/dist/usage/types.js +84 -0
  134. package/dist/usage/usageId.d.ts +6 -0
  135. package/dist/usage/usageId.js +45 -0
  136. package/dist/utils/{findIntegrationsCredentials.js → WriteBehind.d.ts} +13 -10
  137. package/dist/utils/WriteBehind.js +64 -0
  138. package/dist/utils/index.d.ts +1 -3
  139. package/dist/utils/index.js +1 -3
  140. package/dist/utils/{createFetchSingleCallByCallId.d.ts → logUnhandledRejections.d.ts} +8 -4
  141. package/dist/{assistants/audioFiltersSchema.js → utils/logUnhandledRejections.js} +17 -12
  142. package/dist/utils/withErrorHandling.js +1 -1
  143. package/dist/validators/calls.d.ts +3 -0
  144. package/dist/validators/calls.js +12 -2
  145. package/dist/validators/identity.d.ts +37 -8
  146. package/dist/validators/identity.js +22 -3
  147. package/dist/validators/index.d.ts +1 -0
  148. package/dist/validators/index.js +1 -0
  149. package/dist/validators/sip.d.ts +40 -0
  150. package/dist/validators/sip.js +99 -0
  151. package/dist/validators/sipnet/credentials.d.ts +2 -2
  152. package/dist/validators/sipnet/domains.js +7 -7
  153. package/dist/validators/sipnet/trunks.d.ts +14 -14
  154. package/dist/validators/sipnet/trunks.js +4 -2
  155. package/dist/webhooks/Dispatcher.d.ts +87 -0
  156. package/dist/webhooks/Dispatcher.js +225 -0
  157. package/dist/webhooks/bus.d.ts +68 -0
  158. package/dist/webhooks/bus.js +178 -0
  159. package/dist/webhooks/events.d.ts +66 -0
  160. package/dist/webhooks/events.js +62 -0
  161. package/dist/webhooks/index.d.ts +23 -0
  162. package/dist/webhooks/index.js +39 -0
  163. package/dist/webhooks/prisma.d.ts +55 -0
  164. package/dist/webhooks/prisma.js +242 -0
  165. package/dist/webhooks/signing.d.ts +8 -0
  166. package/dist/webhooks/signing.js +53 -0
  167. package/package.json +25 -11
  168. package/schema/session.v1.json +871 -0
  169. package/dist/assistants/assistantSchema.d.ts +0 -739
  170. package/dist/assistants/assistantSchema.js +0 -37
  171. package/dist/assistants/conversationSettingsSchema.d.ts +0 -122
  172. package/dist/assistants/conversationSettingsSchema.js +0 -133
  173. package/dist/assistants/eventsHookSchema.d.ts +0 -38
  174. package/dist/assistants/languageModelConfigSchema.d.ts +0 -206
  175. package/dist/identity/tokenHasAccessKeyId.d.ts +0 -2
  176. package/dist/protos/voice.proto +0 -557
  177. package/dist/utils/createFetchSingleCallByCallId.js +0 -33
  178. package/dist/utils/findIntegrationsCredentials.d.ts +0 -23
  179. package/dist/utils/types.js +0 -2
  180. package/dist/voice/Dial.js +0 -20
  181. package/dist/voice/Gather.js +0 -9
  182. package/dist/voice/Mute.d.ts +0 -31
  183. package/dist/voice/Mute.js +0 -9
  184. package/dist/voice/Play.d.ts +0 -30
  185. package/dist/voice/Play.js +0 -2
  186. package/dist/voice/PlayDtmf.js +0 -2
  187. package/dist/voice/PlaybackControl.d.ts +0 -31
  188. package/dist/voice/PlaybackControl.js +0 -11
  189. package/dist/voice/Record.d.ts +0 -36
  190. package/dist/voice/Record.js +0 -7
  191. package/dist/voice/Say.d.ts +0 -28
  192. package/dist/voice/Say.js +0 -2
  193. package/dist/voice/StopSay.js +0 -2
  194. package/dist/voice/Stream.d.ts +0 -53
  195. package/dist/voice/Stream.js +0 -19
  196. package/dist/voice/StreamGather.d.ts +0 -34
  197. package/dist/voice/StreamGather.js +0 -9
  198. package/dist/voice/Verb.d.ts +0 -27
  199. package/dist/voice/Verb.js +0 -2
  200. package/dist/voice/voice.d.ts +0 -171
  201. package/dist/voice/voice.js +0 -65
  202. package/dist/{assistants → agents}/testCasesSchema.d.ts +4 -4
  203. /package/dist/{assistants → agents}/testCasesSchema.js +0 -0
  204. /package/dist/{assistants → agents}/tools/index.d.ts +0 -0
  205. /package/dist/{assistants → agents}/tools/index.js +0 -0
  206. /package/dist/{assistants → agents}/tools/propertySchema.d.ts +0 -0
  207. /package/dist/{assistants → agents}/tools/propertySchema.js +0 -0
  208. /package/dist/{assistants → agents}/tools/toolSchema.d.ts +0 -0
  209. /package/dist/{assistants → agents}/tools/toolSchema.js +0 -0
@@ -0,0 +1,66 @@
1
+ export declare const WEBHOOK_EVENT_TYPES: readonly ["call.started", "call.ended", "call.failed", "call_record.sealed", "provider.deprecated"];
2
+ export type WebhookEventType = (typeof WEBHOOK_EVENT_TYPES)[number];
3
+ /**
4
+ * Event names of the retired Autopilot agent webhook (`eventsHook`) and what replaces them
5
+ * (add-call-record 7.3). The record of a conversation (transcript, turns, recording) is in the call
6
+ * record that `call_record.sealed` links to.
7
+ */
8
+ export declare const RETIRED_EVENT_TYPES: Record<string, WebhookEventType[]>;
9
+ export type WebhookEvent<D = Record<string, unknown>> = {
10
+ id: string;
11
+ type: WebhookEventType;
12
+ createdAt: string;
13
+ workspaceRef: string;
14
+ data: D;
15
+ };
16
+ type CallParties = {
17
+ callRef: string;
18
+ appRef: string;
19
+ from: string;
20
+ to: string;
21
+ direction: "inbound" | "outbound";
22
+ };
23
+ export declare const callStarted: (workspaceRef: string, call: CallParties) => WebhookEvent<CallParties>;
24
+ export declare const callEnded: (workspaceRef: string, call: CallParties & {
25
+ durationSeconds: number;
26
+ endReason: string;
27
+ }) => WebhookEvent<CallParties & {
28
+ durationSeconds: number;
29
+ endReason: string;
30
+ }>;
31
+ /** A call that never reached an app (busy, no answer, failed, app unreachable). */
32
+ export declare const callFailed: (workspaceRef: string, call: {
33
+ callRef: string;
34
+ appRef?: string;
35
+ from: string;
36
+ to: string;
37
+ direction: "inbound" | "outbound";
38
+ reason: string;
39
+ }) => WebhookEvent<{
40
+ callRef: string;
41
+ appRef?: string;
42
+ from: string;
43
+ to: string;
44
+ direction: "inbound" | "outbound";
45
+ reason: string;
46
+ }>;
47
+ /** Points to the record; never carries the transcript, recording or SIP trace. */
48
+ export declare const callRecordSealed: (workspaceRef: string, sealed: {
49
+ callRef: string;
50
+ version: number;
51
+ recordUrl: string;
52
+ }) => WebhookEvent<{
53
+ callRef: string;
54
+ version: number;
55
+ recordUrl: string;
56
+ }>;
57
+ export declare const providerDeprecated: (workspaceRef: string, d: {
58
+ id: string;
59
+ replacement: string;
60
+ retireOn: string;
61
+ }) => WebhookEvent<{
62
+ id: string;
63
+ replacement: string;
64
+ retireOn: string;
65
+ }>;
66
+ export {};
@@ -0,0 +1,62 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.providerDeprecated = exports.callRecordSealed = exports.callFailed = exports.callEnded = exports.callStarted = exports.RETIRED_EVENT_TYPES = exports.WEBHOOK_EVENT_TYPES = void 0;
4
+ /**
5
+ * Copyright (C) 2025 by Fonoster Inc (https://fonoster.com)
6
+ * http://github.com/fonoster/fonoster
7
+ *
8
+ * This file is part of Fonoster
9
+ *
10
+ * Licensed under the MIT License (the "License");
11
+ * you may not use this file except in compliance with
12
+ * the License. You may obtain a copy of the License at
13
+ *
14
+ * https://opensource.org/licenses/MIT
15
+ *
16
+ * Unless required by applicable law or agreed to in writing, software
17
+ * distributed under the License is distributed on an "AS IS" BASIS,
18
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19
+ * See the License for the specific language governing permissions and
20
+ * limitations under the License.
21
+ */
22
+ const crypto_1 = require("crypto");
23
+ // Workspace webhook events, v1 (add-workspace-webhooks W2, W3). Every payload uses the envelope
24
+ // id, type, createdAt, workspaceRef, data; ids stay the same across retries.
25
+ exports.WEBHOOK_EVENT_TYPES = [
26
+ "call.started",
27
+ "call.ended",
28
+ "call.failed",
29
+ "call_record.sealed",
30
+ "provider.deprecated"
31
+ ];
32
+ /**
33
+ * Event names of the retired Autopilot agent webhook (`eventsHook`) and what replaces them
34
+ * (add-call-record 7.3). The record of a conversation (transcript, turns, recording) is in the call
35
+ * record that `call_record.sealed` links to.
36
+ */
37
+ exports.RETIRED_EVENT_TYPES = {
38
+ "conversation.started": ["call.started"],
39
+ "conversation.ended": ["call.ended", "call_record.sealed"],
40
+ all: ["call.started", "call.ended", "call_record.sealed"]
41
+ };
42
+ function envelope(type, workspaceRef, data, now = new Date()) {
43
+ return {
44
+ id: `evt_${(0, crypto_1.randomUUID)()}`,
45
+ type,
46
+ createdAt: now.toISOString(),
47
+ workspaceRef,
48
+ data
49
+ };
50
+ }
51
+ const callStarted = (workspaceRef, call) => envelope("call.started", workspaceRef, call);
52
+ exports.callStarted = callStarted;
53
+ const callEnded = (workspaceRef, call) => envelope("call.ended", workspaceRef, call);
54
+ exports.callEnded = callEnded;
55
+ /** A call that never reached an app (busy, no answer, failed, app unreachable). */
56
+ const callFailed = (workspaceRef, call) => envelope("call.failed", workspaceRef, call);
57
+ exports.callFailed = callFailed;
58
+ /** Points to the record; never carries the transcript, recording or SIP trace. */
59
+ const callRecordSealed = (workspaceRef, sealed) => envelope("call_record.sealed", workspaceRef, sealed);
60
+ exports.callRecordSealed = callRecordSealed;
61
+ const providerDeprecated = (workspaceRef, d) => envelope("provider.deprecated", workspaceRef, d);
62
+ exports.providerDeprecated = providerDeprecated;
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Copyright (C) 2025 by Fonoster Inc (https://fonoster.com)
3
+ * http://github.com/fonoster/fonoster
4
+ *
5
+ * This file is part of Fonoster
6
+ *
7
+ * Licensed under the MIT License (the "License");
8
+ * you may not use this file except in compliance with
9
+ * the License. You may obtain a copy of the License at
10
+ *
11
+ * https://opensource.org/licenses/MIT
12
+ *
13
+ * Unless required by applicable law or agreed to in writing, software
14
+ * distributed under the License is distributed on an "AS IS" BASIS,
15
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ * See the License for the specific language governing permissions and
17
+ * limitations under the License.
18
+ */
19
+ export * from "./Dispatcher";
20
+ export * from "./events";
21
+ export * from "./signing";
22
+ export * from "./prisma";
23
+ export * from "./bus";
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ /**
18
+ * Copyright (C) 2025 by Fonoster Inc (https://fonoster.com)
19
+ * http://github.com/fonoster/fonoster
20
+ *
21
+ * This file is part of Fonoster
22
+ *
23
+ * Licensed under the MIT License (the "License");
24
+ * you may not use this file except in compliance with
25
+ * the License. You may obtain a copy of the License at
26
+ *
27
+ * https://opensource.org/licenses/MIT
28
+ *
29
+ * Unless required by applicable law or agreed to in writing, software
30
+ * distributed under the License is distributed on an "AS IS" BASIS,
31
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
32
+ * See the License for the specific language governing permissions and
33
+ * limitations under the License.
34
+ */
35
+ __exportStar(require("./Dispatcher"), exports);
36
+ __exportStar(require("./events"), exports);
37
+ __exportStar(require("./signing"), exports);
38
+ __exportStar(require("./prisma"), exports);
39
+ __exportStar(require("./bus"), exports);
@@ -0,0 +1,55 @@
1
+ import { Delivery, WebhookEndpoint, WebhookStore } from "./Dispatcher";
2
+ type PrismaLike = any;
3
+ export type PrismaWebhookStoreOptions = {
4
+ /**
5
+ * Take over pending deliveries this long overdue (their owner died or stalled). Off by default;
6
+ * turn it on in processes that run `runDue()` on a timer. Default 60 s when set to true.
7
+ */
8
+ adoptOverdue?: boolean | number;
9
+ now?: () => number;
10
+ };
11
+ export declare class PrismaWebhookStore implements WebhookStore {
12
+ private readonly prisma;
13
+ private readonly options;
14
+ private e;
15
+ private d;
16
+ private readonly writes;
17
+ private constructor();
18
+ static open(prisma: PrismaLike, options?: PrismaWebhookStoreOptions): Promise<PrismaWebhookStore>;
19
+ private now;
20
+ /**
21
+ * Reloads endpoints, forgets settled deliveries (they stay in the log) and, with
22
+ * `adoptOverdue`, claims pending deliveries whose owner stopped retrying them.
23
+ */
24
+ refresh(): Promise<void>;
25
+ /**
26
+ * Reloads one workspace's endpoints now. The fan-out calls it for each event, so an endpoint
27
+ * created, changed or deleted a moment ago through another replica counts at once instead of
28
+ * after the next `refresh()` (ST-2).
29
+ */
30
+ refreshEndpoints(workspaceRef: string): Promise<void>;
31
+ endpoints(workspaceRef?: string): WebhookEndpoint[];
32
+ putEndpoint(x: WebhookEndpoint): void;
33
+ deleteEndpoint(id: string): void;
34
+ /** Deliveries this process knows: the ones it created, adopted or loaded. */
35
+ deliveries(endpointId?: string): Delivery[];
36
+ /**
37
+ * Stores new deliveries before returning (the fan-out acknowledges the bus message only then).
38
+ * Returns the ones it stored; a delivery that already exists (the same event fanned out again)
39
+ * is left as it is, and whoever owns it keeps it (ST-2).
40
+ */
41
+ insertDeliveries(xs: Delivery[]): Promise<Delivery[]>;
42
+ putDelivery(x: Delivery): void;
43
+ /**
44
+ * The delivery log, newest first (W6). Loaded deliveries become known to this store, so
45
+ * `WebhookDispatcher.resend` can find them.
46
+ */
47
+ loadDeliveries(filter?: {
48
+ endpointId?: string;
49
+ workspaceRef?: string;
50
+ id?: string;
51
+ }, limit?: number): Promise<Delivery[]>;
52
+ /** Waits for every queued write; throws the first failure. */
53
+ flush(): Promise<void>;
54
+ }
55
+ export {};
@@ -0,0 +1,242 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.PrismaWebhookStore = void 0;
13
+ /**
14
+ * Copyright (C) 2025 by Fonoster Inc (https://fonoster.com)
15
+ * http://github.com/fonoster/fonoster
16
+ *
17
+ * This file is part of Fonoster
18
+ *
19
+ * Licensed under the MIT License (the "License");
20
+ * you may not use this file except in compliance with
21
+ * the License. You may obtain a copy of the License at
22
+ *
23
+ * https://opensource.org/licenses/MIT
24
+ *
25
+ * Unless required by applicable law or agreed to in writing, software
26
+ * distributed under the License is distributed on an "AS IS" BASIS,
27
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
28
+ * See the License for the specific language governing permissions and
29
+ * limitations under the License.
30
+ */
31
+ const WriteBehind_1 = require("../utils/WriteBehind");
32
+ const ms = (d) => (d ? d.getTime() : undefined);
33
+ const date = (n) => (n === undefined ? null : new Date(n));
34
+ function toEndpoint(r) {
35
+ const e = {
36
+ id: r.ref,
37
+ workspaceRef: r.accessKeyId,
38
+ url: r.url,
39
+ events: r.events,
40
+ secret: r.secret
41
+ };
42
+ if (r.previousSecret) {
43
+ e.previousSecret = r.previousSecret;
44
+ e.previousUntil = ms(r.previousUntil);
45
+ }
46
+ return e;
47
+ }
48
+ function toDelivery(r) {
49
+ const d = {
50
+ id: r.ref,
51
+ endpointId: r.endpointRef,
52
+ event: r.event,
53
+ status: r.status,
54
+ attempts: r.attempts,
55
+ nextAttemptAt: ms(r.nextAttemptAt)
56
+ };
57
+ if (r.firstAttemptAt)
58
+ d.firstAttemptAt = ms(r.firstAttemptAt);
59
+ if (r.lastAttemptAt)
60
+ d.lastAttemptAt = ms(r.lastAttemptAt);
61
+ if (r.lastStatus !== null && r.lastStatus !== undefined)
62
+ d.lastStatus = r.lastStatus;
63
+ return d;
64
+ }
65
+ /** A delivery's columns (without its ref). */
66
+ function deliveryData(x) {
67
+ var _a;
68
+ return {
69
+ endpointRef: x.endpointId,
70
+ accessKeyId: x.event.workspaceRef,
71
+ eventId: x.event.id,
72
+ eventType: x.event.type,
73
+ event: x.event,
74
+ status: x.status,
75
+ attempts: x.attempts,
76
+ firstAttemptAt: date(x.firstAttemptAt),
77
+ lastAttemptAt: date(x.lastAttemptAt),
78
+ lastStatus: (_a = x.lastStatus) !== null && _a !== void 0 ? _a : null,
79
+ nextAttemptAt: new Date(x.nextAttemptAt)
80
+ };
81
+ }
82
+ class PrismaWebhookStore {
83
+ constructor(prisma, options) {
84
+ this.prisma = prisma;
85
+ this.options = options;
86
+ this.e = new Map();
87
+ this.d = new Map();
88
+ this.writes = new WriteBehind_1.WriteBehind();
89
+ }
90
+ static open(prisma_1) {
91
+ return __awaiter(this, arguments, void 0, function* (prisma, options = {}) {
92
+ const store = new PrismaWebhookStore(prisma, options);
93
+ yield store.refresh();
94
+ return store;
95
+ });
96
+ }
97
+ now() {
98
+ var _a, _b, _c;
99
+ return (_c = (_b = (_a = this.options).now) === null || _b === void 0 ? void 0 : _b.call(_a)) !== null && _c !== void 0 ? _c : Date.now();
100
+ }
101
+ /**
102
+ * Reloads endpoints, forgets settled deliveries (they stay in the log) and, with
103
+ * `adoptOverdue`, claims pending deliveries whose owner stopped retrying them.
104
+ */
105
+ refresh() {
106
+ return __awaiter(this, void 0, void 0, function* () {
107
+ yield this.writes.flush();
108
+ const rows = yield this.prisma.webhookEndpoint.findMany();
109
+ this.e = new Map(rows.map((r) => [r.ref, toEndpoint(r)]));
110
+ for (const [id, d] of this.d)
111
+ if (d.status !== "pending")
112
+ this.d.delete(id);
113
+ const adopt = this.options.adoptOverdue;
114
+ if (!adopt)
115
+ return;
116
+ const after = adopt === true ? 60000 : adopt;
117
+ const overdue = yield this.prisma.webhookDelivery.findMany({
118
+ where: {
119
+ status: "pending",
120
+ nextAttemptAt: { lt: new Date(this.now() - after) }
121
+ },
122
+ take: 500
123
+ });
124
+ for (const r of overdue) {
125
+ if (this.d.has(r.ref))
126
+ continue;
127
+ // Claim it: only one process moves nextAttemptAt from the value it read.
128
+ const { count } = yield this.prisma.webhookDelivery.updateMany({
129
+ where: { ref: r.ref, nextAttemptAt: r.nextAttemptAt },
130
+ data: { nextAttemptAt: new Date(this.now()) }
131
+ });
132
+ if (count === 1)
133
+ this.d.set(r.ref, Object.assign(Object.assign({}, toDelivery(r)), { nextAttemptAt: this.now() }));
134
+ }
135
+ });
136
+ }
137
+ /**
138
+ * Reloads one workspace's endpoints now. The fan-out calls it for each event, so an endpoint
139
+ * created, changed or deleted a moment ago through another replica counts at once instead of
140
+ * after the next `refresh()` (ST-2).
141
+ */
142
+ refreshEndpoints(workspaceRef) {
143
+ return __awaiter(this, void 0, void 0, function* () {
144
+ yield this.writes.flush();
145
+ const rows = yield this.prisma.webhookEndpoint.findMany({
146
+ where: { accessKeyId: workspaceRef }
147
+ });
148
+ for (const [id, e] of this.e)
149
+ if (e.workspaceRef === workspaceRef)
150
+ this.e.delete(id);
151
+ for (const r of rows)
152
+ this.e.set(r.ref, toEndpoint(r));
153
+ });
154
+ }
155
+ endpoints(workspaceRef) {
156
+ return [...this.e.values()].filter((x) => !workspaceRef || x.workspaceRef === workspaceRef);
157
+ }
158
+ putEndpoint(x) {
159
+ var _a;
160
+ this.e.set(x.id, x);
161
+ const data = {
162
+ accessKeyId: x.workspaceRef,
163
+ url: x.url,
164
+ events: x.events,
165
+ secret: x.secret,
166
+ previousSecret: (_a = x.previousSecret) !== null && _a !== void 0 ? _a : null,
167
+ previousUntil: date(x.previousUntil)
168
+ };
169
+ this.writes.push(() => this.prisma.webhookEndpoint.upsert({
170
+ where: { ref: x.id },
171
+ create: Object.assign({ ref: x.id }, data),
172
+ update: data
173
+ }));
174
+ }
175
+ deleteEndpoint(id) {
176
+ this.e.delete(id);
177
+ this.writes.push(() => this.prisma.webhookEndpoint.deleteMany({ where: { ref: id } }));
178
+ }
179
+ /** Deliveries this process knows: the ones it created, adopted or loaded. */
180
+ deliveries(endpointId) {
181
+ return [...this.d.values()].filter((x) => !endpointId || x.endpointId === endpointId);
182
+ }
183
+ /**
184
+ * Stores new deliveries before returning (the fan-out acknowledges the bus message only then).
185
+ * Returns the ones it stored; a delivery that already exists (the same event fanned out again)
186
+ * is left as it is, and whoever owns it keeps it (ST-2).
187
+ */
188
+ insertDeliveries(xs) {
189
+ return __awaiter(this, void 0, void 0, function* () {
190
+ yield this.writes.flush();
191
+ const created = [];
192
+ for (const x of xs) {
193
+ try {
194
+ yield this.prisma.webhookDelivery.create({
195
+ data: Object.assign({ ref: x.id }, deliveryData(x))
196
+ });
197
+ }
198
+ catch (e) {
199
+ if (e.code === "P2002")
200
+ continue;
201
+ throw e;
202
+ }
203
+ this.d.set(x.id, x);
204
+ created.push(x);
205
+ }
206
+ return created;
207
+ });
208
+ }
209
+ putDelivery(x) {
210
+ this.d.set(x.id, x);
211
+ const data = deliveryData(x);
212
+ this.writes.push(() => this.prisma.webhookDelivery.upsert({
213
+ where: { ref: x.id },
214
+ create: Object.assign({ ref: x.id }, data),
215
+ update: data
216
+ }));
217
+ }
218
+ /**
219
+ * The delivery log, newest first (W6). Loaded deliveries become known to this store, so
220
+ * `WebhookDispatcher.resend` can find them.
221
+ */
222
+ loadDeliveries() {
223
+ return __awaiter(this, arguments, void 0, function* (filter = {}, limit = 100) {
224
+ yield this.writes.flush();
225
+ const rows = yield this.prisma.webhookDelivery.findMany({
226
+ where: Object.assign(Object.assign(Object.assign({}, (filter.id ? { ref: filter.id } : {})), (filter.endpointId ? { endpointRef: filter.endpointId } : {})), (filter.workspaceRef ? { accessKeyId: filter.workspaceRef } : {})),
227
+ orderBy: { createdAt: "desc" },
228
+ take: limit
229
+ });
230
+ const out = rows.map(toDelivery);
231
+ for (const d of out)
232
+ if (!this.d.has(d.id))
233
+ this.d.set(d.id, d);
234
+ return out;
235
+ });
236
+ }
237
+ /** Waits for every queued write; throws the first failure. */
238
+ flush() {
239
+ return this.writes.flush();
240
+ }
241
+ }
242
+ exports.PrismaWebhookStore = PrismaWebhookStore;
@@ -0,0 +1,8 @@
1
+ export declare const WEBHOOK_SIGNATURE_HEADER = "X-Fonoster-Signature";
2
+ export declare const WEBHOOK_TIMESTAMP_HEADER = "X-Fonoster-Timestamp";
3
+ export declare function webhookHeaders(body: string, secrets: string[], nowSeconds?: number): {
4
+ "X-Fonoster-Timestamp": string;
5
+ "X-Fonoster-Signature": string;
6
+ };
7
+ /** For receivers: true when one of the signatures matches and the timestamp is fresh (±300 s). */
8
+ export declare function verifyWebhook(body: string, headers: Record<string, string | string[] | undefined>, secret: string, nowSeconds?: number): boolean;
@@ -0,0 +1,53 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.WEBHOOK_TIMESTAMP_HEADER = exports.WEBHOOK_SIGNATURE_HEADER = void 0;
4
+ exports.webhookHeaders = webhookHeaders;
5
+ exports.verifyWebhook = verifyWebhook;
6
+ /**
7
+ * Copyright (C) 2025 by Fonoster Inc (https://fonoster.com)
8
+ * http://github.com/fonoster/fonoster
9
+ *
10
+ * This file is part of Fonoster
11
+ *
12
+ * Licensed under the MIT License (the "License");
13
+ * you may not use this file except in compliance with
14
+ * the License. You may obtain a copy of the License at
15
+ *
16
+ * https://opensource.org/licenses/MIT
17
+ *
18
+ * Unless required by applicable law or agreed to in writing, software
19
+ * distributed under the License is distributed on an "AS IS" BASIS,
20
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
21
+ * See the License for the specific language governing permissions and
22
+ * limitations under the License.
23
+ */
24
+ const crypto_1 = require("crypto");
25
+ // add-workspace-webhooks, "Deliveries are signed": X-Fonoster-Timestamp (Unix seconds) and
26
+ // X-Fonoster-Signature = hex HMAC-SHA256 of "<timestamp>.<raw body>", one per valid secret,
27
+ // comma-separated during a rotation window.
28
+ exports.WEBHOOK_SIGNATURE_HEADER = "X-Fonoster-Signature";
29
+ exports.WEBHOOK_TIMESTAMP_HEADER = "X-Fonoster-Timestamp";
30
+ const hmac = (secret, timestamp, body) => (0, crypto_1.createHmac)("sha256", secret).update(`${timestamp}.${body}`).digest("hex");
31
+ function webhookHeaders(body, secrets, nowSeconds = Math.floor(Date.now() / 1000)) {
32
+ return {
33
+ [exports.WEBHOOK_TIMESTAMP_HEADER]: String(nowSeconds),
34
+ [exports.WEBHOOK_SIGNATURE_HEADER]: secrets
35
+ .map((s) => hmac(s, nowSeconds, body))
36
+ .join(",")
37
+ };
38
+ }
39
+ /** For receivers: true when one of the signatures matches and the timestamp is fresh (±300 s). */
40
+ function verifyWebhook(body, headers, secret, nowSeconds = Math.floor(Date.now() / 1000)) {
41
+ const lower = Object.fromEntries(Object.entries(headers).map(([k, v]) => [k.toLowerCase(), v]));
42
+ const timestamp = Number(lower[exports.WEBHOOK_TIMESTAMP_HEADER.toLowerCase()]);
43
+ const header = lower[exports.WEBHOOK_SIGNATURE_HEADER.toLowerCase()];
44
+ if (!Number.isFinite(timestamp) || typeof header !== "string")
45
+ return false;
46
+ if (Math.abs(nowSeconds - timestamp) > 300)
47
+ return false;
48
+ const expected = Buffer.from(hmac(secret, timestamp, body), "hex");
49
+ return header.split(",").some((sig) => {
50
+ const given = Buffer.from(sig.trim(), "hex");
51
+ return given.length === expected.length && (0, crypto_1.timingSafeEqual)(given, expected);
52
+ });
53
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fonoster/common",
3
- "version": "0.27.0",
3
+ "version": "0.31.0",
4
4
  "description": "Common library for Fonoster projects",
5
5
  "author": "Pedro Sanders <psanders@fonoster.com>",
6
6
  "homepage": "https://github.com/fonoster/fonoster#readme",
@@ -15,27 +15,37 @@
15
15
  "prebuild": "rimraf ./dist tsconfig.tsbuildinfo",
16
16
  "build": "tsc -b tsconfig.json",
17
17
  "postbuild": "cp -a src/protos dist",
18
- "clean": "rimraf ./dist node_modules tsconfig.tsbuildinfo"
18
+ "generate:session-schema": "tsx scripts/export-session-schema.ts",
19
+ "generate:config-schemas": "tsx scripts/export-config-schemas.ts"
19
20
  },
20
21
  "dependencies": {
21
- "@fonoster/logger": "^0.27.0",
22
- "@fonoster/types": "^0.27.0",
23
- "@grpc/grpc-js": "~1.10.11",
22
+ "@fonoster/logger": "^0.31.0",
23
+ "@fonoster/types": "^0.31.0",
24
+ "@grpc/grpc-js": "~1.14.6",
24
25
  "@grpc/proto-loader": "^0.7.15",
25
- "@influxdata/influxdb-client": "^1.35.0",
26
- "dotenv": "^16.6.1",
26
+ "@opentelemetry/api": "^1.9.0",
27
+ "@opentelemetry/resources": "^2.12.0",
28
+ "@opentelemetry/sdk-trace-base": "^2.12.0",
29
+ "@opentelemetry/sdk-trace-node": "^2.12.0",
30
+ "ajv": "^8.17.1",
27
31
  "grpc-health-check": "^2.0.2",
28
32
  "handlebars": "^4.7.8",
33
+ "json-schema-to-ts": "^3.1.1",
34
+ "jsonc-parser": "^3.2.0",
29
35
  "jsonwebtoken": "^9.0.2",
30
36
  "jwt-decode": "^4.0.0",
31
- "nodemailer": "^6.10.1",
37
+ "nkeys.js": "^1.1.0",
38
+ "nodemailer": "^10.1.2",
32
39
  "pb-util": "^1.0.3",
33
40
  "twilio": "^5.11.1",
34
41
  "validator": "^13.12.0",
42
+ "yaml": "^2.9.0",
43
+ "zod": "^3.25.76",
35
44
  "zod-validation-error": "^3.5.4"
36
45
  },
37
46
  "files": [
38
- "dist"
47
+ "dist",
48
+ "schema"
39
49
  ],
40
50
  "publishConfig": {
41
51
  "access": "public"
@@ -48,7 +58,11 @@
48
58
  "url": "https://github.com/fonoster/fonoster/issues"
49
59
  },
50
60
  "devDependencies": {
51
- "@types/nodemailer": "^6.4.21"
61
+ "@fonoster/roomr": "0.2.5"
52
62
  },
53
- "gitHead": "1191b34677b51a36000177887ff82303ef2693e1"
63
+ "gitHead": "f0a5102dddeea0f70f659234108805bc10690f2e",
64
+ "bin": {
65
+ "fonoster-registry": "dist/registry/cli.js",
66
+ "fonoster-config": "dist/config/cli/index.js"
67
+ }
54
68
  }