@fonoster/common 0.27.0 → 0.30.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 (176) hide show
  1. package/dist/assistants/index.d.ts +2 -6
  2. package/dist/assistants/index.js +2 -6
  3. package/dist/assistants/sessionAssistantSchema.d.ts +741 -0
  4. package/dist/assistants/sessionAssistantSchema.js +112 -0
  5. package/dist/assistants/testCasesSchema.d.ts +4 -4
  6. package/dist/{voice/Dial.d.ts → calls/index.d.ts} +16 -13
  7. package/dist/calls/index.js +44 -0
  8. package/dist/cellsecrets/index.d.ts +91 -0
  9. package/dist/cellsecrets/index.js +299 -0
  10. package/dist/config/cli/check.d.ts +6 -0
  11. package/dist/config/cli/check.js +202 -0
  12. package/dist/config/cli/envFile.d.ts +23 -0
  13. package/dist/config/cli/envFile.js +81 -0
  14. package/dist/config/cli/exposure.d.ts +10 -0
  15. package/dist/config/cli/exposure.js +93 -0
  16. package/dist/config/cli/index.d.ts +2 -0
  17. package/dist/{identity/tokenHasAccessKeyId.js → config/cli/index.js} +7 -8
  18. package/dist/config/cli/info.d.ts +6 -0
  19. package/dist/config/cli/info.js +62 -0
  20. package/dist/config/cli/init.d.ts +9 -0
  21. package/dist/config/cli/init.js +152 -0
  22. package/dist/config/cli/main.d.ts +11 -0
  23. package/dist/config/cli/main.js +155 -0
  24. package/dist/config/cli/render.d.ts +44 -0
  25. package/dist/config/cli/render.js +129 -0
  26. package/dist/config/cli/secrets.d.ts +21 -0
  27. package/dist/config/cli/secrets.js +117 -0
  28. package/dist/{utils/findIntegrationsCredentials.d.ts → config/cli/types.d.ts} +6 -5
  29. package/dist/config/cli/types.js +7 -0
  30. package/dist/config/configHints.d.ts +39 -0
  31. package/dist/config/configHints.js +61 -0
  32. package/dist/{voice → config}/index.d.ts +12 -13
  33. package/dist/config/index.js +46 -0
  34. package/dist/config/integrationsSchema.d.ts +32 -0
  35. package/dist/config/integrationsSchema.js +56 -0
  36. package/dist/config/interpolateEnv.d.ts +34 -0
  37. package/dist/config/interpolateEnv.js +70 -0
  38. package/dist/config/loadServiceConfig.d.ts +33 -0
  39. package/dist/config/loadServiceConfig.js +121 -0
  40. package/dist/config/logsSchema.d.ts +76 -0
  41. package/dist/config/logsSchema.js +49 -0
  42. package/dist/config/natsConnection.d.ts +53 -0
  43. package/dist/config/natsConnection.js +58 -0
  44. package/dist/config/parseConfigFile.d.ts +7 -0
  45. package/dist/config/parseConfigFile.js +60 -0
  46. package/dist/config/resolveConfigFile.d.ts +17 -0
  47. package/dist/config/resolveConfigFile.js +54 -0
  48. package/dist/config/resolveSecretRefs.d.ts +35 -0
  49. package/dist/config/resolveSecretRefs.js +129 -0
  50. package/dist/{assistants/audioFiltersSchema.d.ts → config/strictDeep.d.ts} +9 -12
  51. package/dist/config/strictDeep.js +58 -0
  52. package/dist/config/telemetrySchema.d.ts +39 -0
  53. package/dist/config/telemetrySchema.js +44 -0
  54. package/dist/config/toJsonSchema.d.ts +30 -0
  55. package/dist/config/toJsonSchema.js +161 -0
  56. package/dist/constants.d.ts +1 -4
  57. package/dist/constants.js +4 -5
  58. package/dist/envs.d.ts +2 -1
  59. package/dist/envs.js +13 -11
  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 +22 -6
  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 +11 -1
  74. package/dist/index.js +14 -2
  75. package/dist/net/assertPublicDestination.d.ts +52 -0
  76. package/dist/net/assertPublicDestination.js +342 -0
  77. package/dist/{voice/PlayDtmf.d.ts → net/index.d.ts} +1 -5
  78. package/dist/{voice → net}/index.js +1 -13
  79. package/dist/notifications/createSendEmail.js +3 -1
  80. package/dist/protos/applications.proto +124 -3
  81. package/dist/protos/authz.proto +23 -4
  82. package/dist/protos/capabilities.proto +94 -0
  83. package/dist/protos/identity.proto +26 -1
  84. package/dist/protos/providers.proto +135 -0
  85. package/dist/protos/webhooks.proto +140 -0
  86. package/dist/{voice/StopSay.d.ts → regions/hub/index.d.ts} +1 -6
  87. package/dist/regions/hub/index.js +35 -0
  88. package/dist/regions/hub/natsJwt.d.ts +63 -0
  89. package/dist/regions/hub/natsJwt.js +195 -0
  90. package/dist/registry/Registry.d.ts +79 -0
  91. package/dist/registry/Registry.js +418 -0
  92. package/dist/registry/cli.d.ts +20 -0
  93. package/dist/registry/cli.js +424 -0
  94. package/dist/{assistants/audioFiltersSchema.js → registry/features.d.ts} +13 -14
  95. package/dist/registry/features.js +36 -0
  96. package/dist/{assistants/LanguageModelProvider.d.ts → registry/index.d.ts} +6 -8
  97. package/dist/registry/index.js +40 -0
  98. package/dist/registry/prisma.d.ts +67 -0
  99. package/dist/registry/prisma.js +391 -0
  100. package/dist/registry/stores.d.ts +21 -0
  101. package/dist/{assistants/eventsHookSchema.js → registry/stores.js} +55 -18
  102. package/dist/registry/types.d.ts +98 -0
  103. package/dist/{voice/Say.d.ts → registry/types.js} +4 -10
  104. package/dist/registry/workspaceKeys.d.ts +41 -0
  105. package/dist/registry/workspaceKeys.js +64 -0
  106. package/dist/session/index.d.ts +19 -0
  107. package/dist/session/index.js +35 -0
  108. package/dist/session/schema.d.ts +1951 -0
  109. package/dist/session/schema.js +397 -0
  110. package/dist/sessions/index.d.ts +93 -0
  111. package/dist/sessions/index.js +194 -0
  112. package/dist/telemetry.d.ts +76 -0
  113. package/dist/telemetry.js +360 -0
  114. package/dist/types.d.ts +3 -5
  115. package/dist/utils/{findIntegrationsCredentials.js → WriteBehind.d.ts} +13 -10
  116. package/dist/utils/WriteBehind.js +64 -0
  117. package/dist/utils/index.d.ts +1 -1
  118. package/dist/utils/index.js +1 -1
  119. package/dist/{voice/Play.d.ts → utils/logUnhandledRejections.d.ts} +8 -12
  120. package/dist/utils/logUnhandledRejections.js +37 -0
  121. package/dist/validators/calls.d.ts +2 -2
  122. package/dist/validators/calls.js +5 -2
  123. package/dist/validators/identity.d.ts +37 -8
  124. package/dist/validators/identity.js +22 -3
  125. package/dist/validators/index.d.ts +1 -0
  126. package/dist/validators/index.js +1 -0
  127. package/dist/validators/sip.d.ts +40 -0
  128. package/dist/validators/sip.js +99 -0
  129. package/dist/validators/sipnet/credentials.d.ts +2 -2
  130. package/dist/validators/sipnet/domains.js +7 -7
  131. package/dist/validators/sipnet/trunks.d.ts +14 -14
  132. package/dist/validators/sipnet/trunks.js +4 -2
  133. package/dist/webhooks/Dispatcher.d.ts +87 -0
  134. package/dist/webhooks/Dispatcher.js +225 -0
  135. package/dist/webhooks/bus.d.ts +49 -0
  136. package/dist/webhooks/bus.js +170 -0
  137. package/dist/webhooks/events.d.ts +66 -0
  138. package/dist/webhooks/events.js +62 -0
  139. package/dist/webhooks/index.d.ts +23 -0
  140. package/dist/webhooks/index.js +39 -0
  141. package/dist/webhooks/prisma.d.ts +55 -0
  142. package/dist/webhooks/prisma.js +242 -0
  143. package/dist/webhooks/signing.d.ts +8 -0
  144. package/dist/webhooks/signing.js +53 -0
  145. package/package.json +25 -12
  146. package/schema/session.v1.json +791 -0
  147. package/dist/assistants/assistantSchema.d.ts +0 -739
  148. package/dist/assistants/assistantSchema.js +0 -37
  149. package/dist/assistants/conversationSettingsSchema.d.ts +0 -122
  150. package/dist/assistants/conversationSettingsSchema.js +0 -133
  151. package/dist/assistants/eventsHookSchema.d.ts +0 -38
  152. package/dist/assistants/languageModelConfigSchema.d.ts +0 -206
  153. package/dist/assistants/languageModelConfigSchema.js +0 -91
  154. package/dist/identity/tokenHasAccessKeyId.d.ts +0 -2
  155. package/dist/protos/voice.proto +0 -557
  156. package/dist/voice/Dial.js +0 -20
  157. package/dist/voice/Gather.d.ts +0 -37
  158. package/dist/voice/Gather.js +0 -9
  159. package/dist/voice/Mute.d.ts +0 -31
  160. package/dist/voice/Mute.js +0 -9
  161. package/dist/voice/Play.js +0 -2
  162. package/dist/voice/PlayDtmf.js +0 -2
  163. package/dist/voice/PlaybackControl.d.ts +0 -31
  164. package/dist/voice/PlaybackControl.js +0 -11
  165. package/dist/voice/Record.d.ts +0 -36
  166. package/dist/voice/Record.js +0 -7
  167. package/dist/voice/Say.js +0 -2
  168. package/dist/voice/StopSay.js +0 -2
  169. package/dist/voice/Stream.d.ts +0 -53
  170. package/dist/voice/Stream.js +0 -19
  171. package/dist/voice/StreamGather.d.ts +0 -34
  172. package/dist/voice/StreamGather.js +0 -9
  173. package/dist/voice/Verb.d.ts +0 -27
  174. package/dist/voice/Verb.js +0 -2
  175. package/dist/voice/voice.d.ts +0 -171
  176. package/dist/voice/voice.js +0 -65
@@ -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.30.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,38 @@
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
+ "clean": "rimraf ./dist node_modules tsconfig.tsbuildinfo",
19
+ "generate:session-schema": "tsx scripts/export-session-schema.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.30.0",
23
+ "@fonoster/types": "^0.30.0",
24
+ "@grpc/grpc-js": "~1.14.6",
24
25
  "@grpc/proto-loader": "^0.7.15",
25
26
  "@influxdata/influxdb-client": "^1.35.0",
26
- "dotenv": "^16.6.1",
27
+ "@opentelemetry/api": "^1.9.0",
28
+ "@opentelemetry/resources": "^2.12.0",
29
+ "@opentelemetry/sdk-trace-base": "^2.12.0",
30
+ "@opentelemetry/sdk-trace-node": "^2.12.0",
31
+ "ajv": "^8.17.1",
27
32
  "grpc-health-check": "^2.0.2",
28
33
  "handlebars": "^4.7.8",
34
+ "json-schema-to-ts": "^3.1.1",
35
+ "jsonc-parser": "^3.2.0",
29
36
  "jsonwebtoken": "^9.0.2",
30
37
  "jwt-decode": "^4.0.0",
31
- "nodemailer": "^6.10.1",
38
+ "nkeys.js": "^1.1.0",
39
+ "nodemailer": "^10.1.2",
32
40
  "pb-util": "^1.0.3",
33
41
  "twilio": "^5.11.1",
34
42
  "validator": "^13.12.0",
43
+ "yaml": "^2.9.0",
44
+ "zod": "^3.25.76",
35
45
  "zod-validation-error": "^3.5.4"
36
46
  },
37
47
  "files": [
38
- "dist"
48
+ "dist",
49
+ "schema"
39
50
  ],
40
51
  "publishConfig": {
41
52
  "access": "public"
@@ -47,8 +58,10 @@
47
58
  "bugs": {
48
59
  "url": "https://github.com/fonoster/fonoster/issues"
49
60
  },
50
- "devDependencies": {
51
- "@types/nodemailer": "^6.4.21"
52
- },
53
- "gitHead": "1191b34677b51a36000177887ff82303ef2693e1"
61
+ "devDependencies": {},
62
+ "gitHead": "08c9cfd7932675b6b16111daa4d03f14b4e287d1",
63
+ "bin": {
64
+ "fonoster-registry": "dist/registry/cli.js",
65
+ "fonoster-config": "dist/config/cli/index.js"
66
+ }
54
67
  }