@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.
- package/dist/assistants/index.d.ts +2 -6
- package/dist/assistants/index.js +2 -6
- package/dist/assistants/sessionAssistantSchema.d.ts +741 -0
- package/dist/assistants/sessionAssistantSchema.js +112 -0
- package/dist/assistants/testCasesSchema.d.ts +4 -4
- package/dist/{voice/Dial.d.ts → calls/index.d.ts} +16 -13
- package/dist/calls/index.js +44 -0
- package/dist/cellsecrets/index.d.ts +91 -0
- package/dist/cellsecrets/index.js +299 -0
- package/dist/config/cli/check.d.ts +6 -0
- package/dist/config/cli/check.js +202 -0
- package/dist/config/cli/envFile.d.ts +23 -0
- package/dist/config/cli/envFile.js +81 -0
- package/dist/config/cli/exposure.d.ts +10 -0
- package/dist/config/cli/exposure.js +93 -0
- package/dist/config/cli/index.d.ts +2 -0
- package/dist/{identity/tokenHasAccessKeyId.js → config/cli/index.js} +7 -8
- package/dist/config/cli/info.d.ts +6 -0
- package/dist/config/cli/info.js +62 -0
- package/dist/config/cli/init.d.ts +9 -0
- package/dist/config/cli/init.js +152 -0
- package/dist/config/cli/main.d.ts +11 -0
- package/dist/config/cli/main.js +155 -0
- package/dist/config/cli/render.d.ts +44 -0
- package/dist/config/cli/render.js +129 -0
- package/dist/config/cli/secrets.d.ts +21 -0
- package/dist/config/cli/secrets.js +117 -0
- package/dist/{utils/findIntegrationsCredentials.d.ts → config/cli/types.d.ts} +6 -5
- package/dist/config/cli/types.js +7 -0
- package/dist/config/configHints.d.ts +39 -0
- package/dist/config/configHints.js +61 -0
- package/dist/{voice → config}/index.d.ts +12 -13
- package/dist/config/index.js +46 -0
- package/dist/config/integrationsSchema.d.ts +32 -0
- package/dist/config/integrationsSchema.js +56 -0
- package/dist/config/interpolateEnv.d.ts +34 -0
- package/dist/config/interpolateEnv.js +70 -0
- package/dist/config/loadServiceConfig.d.ts +33 -0
- package/dist/config/loadServiceConfig.js +121 -0
- package/dist/config/logsSchema.d.ts +76 -0
- package/dist/config/logsSchema.js +49 -0
- package/dist/config/natsConnection.d.ts +53 -0
- package/dist/config/natsConnection.js +58 -0
- package/dist/config/parseConfigFile.d.ts +7 -0
- package/dist/config/parseConfigFile.js +60 -0
- package/dist/config/resolveConfigFile.d.ts +17 -0
- package/dist/config/resolveConfigFile.js +54 -0
- package/dist/config/resolveSecretRefs.d.ts +35 -0
- package/dist/config/resolveSecretRefs.js +129 -0
- package/dist/{assistants/audioFiltersSchema.d.ts → config/strictDeep.d.ts} +9 -12
- package/dist/config/strictDeep.js +58 -0
- package/dist/config/telemetrySchema.d.ts +39 -0
- package/dist/config/telemetrySchema.js +44 -0
- package/dist/config/toJsonSchema.d.ts +30 -0
- package/dist/config/toJsonSchema.js +161 -0
- package/dist/constants.d.ts +1 -4
- package/dist/constants.js +4 -5
- package/dist/envs.d.ts +2 -1
- package/dist/envs.js +13 -11
- package/dist/identity/createAuthInterceptor.d.ts +9 -2
- package/dist/identity/createAuthInterceptor.js +36 -30
- package/dist/identity/hasAccess.d.ts +9 -3
- package/dist/identity/hasAccess.js +32 -8
- package/dist/identity/index.d.ts +1 -0
- package/dist/identity/index.js +1 -0
- package/dist/identity/isValidToken.d.ts +12 -2
- package/dist/identity/isValidToken.js +25 -9
- package/dist/identity/roles.js +22 -6
- package/dist/identity/types.d.ts +6 -1
- package/dist/identity/types.js +1 -0
- package/dist/{assistants/LanguageModelProvider.js → identity/verifyAccessToken.d.ts} +10 -11
- package/dist/identity/verifyAccessToken.js +44 -0
- package/dist/index.d.ts +11 -1
- package/dist/index.js +14 -2
- package/dist/net/assertPublicDestination.d.ts +52 -0
- package/dist/net/assertPublicDestination.js +342 -0
- package/dist/{voice/PlayDtmf.d.ts → net/index.d.ts} +1 -5
- package/dist/{voice → net}/index.js +1 -13
- package/dist/notifications/createSendEmail.js +3 -1
- package/dist/protos/applications.proto +124 -3
- package/dist/protos/authz.proto +23 -4
- package/dist/protos/capabilities.proto +94 -0
- package/dist/protos/identity.proto +26 -1
- package/dist/protos/providers.proto +135 -0
- package/dist/protos/webhooks.proto +140 -0
- package/dist/{voice/StopSay.d.ts → regions/hub/index.d.ts} +1 -6
- package/dist/regions/hub/index.js +35 -0
- package/dist/regions/hub/natsJwt.d.ts +63 -0
- package/dist/regions/hub/natsJwt.js +195 -0
- package/dist/registry/Registry.d.ts +79 -0
- package/dist/registry/Registry.js +418 -0
- package/dist/registry/cli.d.ts +20 -0
- package/dist/registry/cli.js +424 -0
- package/dist/{assistants/audioFiltersSchema.js → registry/features.d.ts} +13 -14
- package/dist/registry/features.js +36 -0
- package/dist/{assistants/LanguageModelProvider.d.ts → registry/index.d.ts} +6 -8
- package/dist/registry/index.js +40 -0
- package/dist/registry/prisma.d.ts +67 -0
- package/dist/registry/prisma.js +391 -0
- package/dist/registry/stores.d.ts +21 -0
- package/dist/{assistants/eventsHookSchema.js → registry/stores.js} +55 -18
- package/dist/registry/types.d.ts +98 -0
- package/dist/{voice/Say.d.ts → registry/types.js} +4 -10
- package/dist/registry/workspaceKeys.d.ts +41 -0
- package/dist/registry/workspaceKeys.js +64 -0
- package/dist/session/index.d.ts +19 -0
- package/dist/session/index.js +35 -0
- package/dist/session/schema.d.ts +1951 -0
- package/dist/session/schema.js +397 -0
- package/dist/sessions/index.d.ts +93 -0
- package/dist/sessions/index.js +194 -0
- package/dist/telemetry.d.ts +76 -0
- package/dist/telemetry.js +360 -0
- package/dist/types.d.ts +3 -5
- package/dist/utils/{findIntegrationsCredentials.js → WriteBehind.d.ts} +13 -10
- package/dist/utils/WriteBehind.js +64 -0
- package/dist/utils/index.d.ts +1 -1
- package/dist/utils/index.js +1 -1
- package/dist/{voice/Play.d.ts → utils/logUnhandledRejections.d.ts} +8 -12
- package/dist/utils/logUnhandledRejections.js +37 -0
- package/dist/validators/calls.d.ts +2 -2
- package/dist/validators/calls.js +5 -2
- package/dist/validators/identity.d.ts +37 -8
- package/dist/validators/identity.js +22 -3
- package/dist/validators/index.d.ts +1 -0
- package/dist/validators/index.js +1 -0
- package/dist/validators/sip.d.ts +40 -0
- package/dist/validators/sip.js +99 -0
- package/dist/validators/sipnet/credentials.d.ts +2 -2
- package/dist/validators/sipnet/domains.js +7 -7
- package/dist/validators/sipnet/trunks.d.ts +14 -14
- package/dist/validators/sipnet/trunks.js +4 -2
- package/dist/webhooks/Dispatcher.d.ts +87 -0
- package/dist/webhooks/Dispatcher.js +225 -0
- package/dist/webhooks/bus.d.ts +49 -0
- package/dist/webhooks/bus.js +170 -0
- package/dist/webhooks/events.d.ts +66 -0
- package/dist/webhooks/events.js +62 -0
- package/dist/webhooks/index.d.ts +23 -0
- package/dist/webhooks/index.js +39 -0
- package/dist/webhooks/prisma.d.ts +55 -0
- package/dist/webhooks/prisma.js +242 -0
- package/dist/webhooks/signing.d.ts +8 -0
- package/dist/webhooks/signing.js +53 -0
- package/package.json +25 -12
- package/schema/session.v1.json +791 -0
- package/dist/assistants/assistantSchema.d.ts +0 -739
- package/dist/assistants/assistantSchema.js +0 -37
- package/dist/assistants/conversationSettingsSchema.d.ts +0 -122
- package/dist/assistants/conversationSettingsSchema.js +0 -133
- package/dist/assistants/eventsHookSchema.d.ts +0 -38
- package/dist/assistants/languageModelConfigSchema.d.ts +0 -206
- package/dist/assistants/languageModelConfigSchema.js +0 -91
- package/dist/identity/tokenHasAccessKeyId.d.ts +0 -2
- package/dist/protos/voice.proto +0 -557
- package/dist/voice/Dial.js +0 -20
- package/dist/voice/Gather.d.ts +0 -37
- package/dist/voice/Gather.js +0 -9
- package/dist/voice/Mute.d.ts +0 -31
- package/dist/voice/Mute.js +0 -9
- package/dist/voice/Play.js +0 -2
- package/dist/voice/PlayDtmf.js +0 -2
- package/dist/voice/PlaybackControl.d.ts +0 -31
- package/dist/voice/PlaybackControl.js +0 -11
- package/dist/voice/Record.d.ts +0 -36
- package/dist/voice/Record.js +0 -7
- package/dist/voice/Say.js +0 -2
- package/dist/voice/StopSay.js +0 -2
- package/dist/voice/Stream.d.ts +0 -53
- package/dist/voice/Stream.js +0 -19
- package/dist/voice/StreamGather.d.ts +0 -34
- package/dist/voice/StreamGather.js +0 -9
- package/dist/voice/Verb.d.ts +0 -27
- package/dist/voice/Verb.js +0 -2
- package/dist/voice/voice.d.ts +0 -171
- package/dist/voice/voice.js +0 -65
|
@@ -0,0 +1,397 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.APP_COMMAND_SCHEMAS = exports.GATEWAY_MESSAGE_SCHEMAS = exports.recordNoteSchema = exports.handoffSchema = exports.resumeSchema = exports.holdSchema = exports.transferCompleteSchema = exports.transferSchema = exports.TRANSFER_MODES = exports.dtmfSendSchema = exports.hangupSchema = exports.clearSchema = exports.markCommandSchema = exports.subscribeSchema = exports.SUBSCRIBABLE_EVENTS = exports.listenUpdateSchema = exports.playSchema = exports.speakSchema = exports.amdResultSchema = exports.dtmfEventSchema = exports.markEventSchema = exports.outputInterruptedSchema = exports.outputDoneSchema = exports.outputStartedSchema = exports.transcriptPartialSchema = exports.turnEstimateSchema = exports.turnBackchannelSchema = exports.turnResumedSchema = exports.turnEndedSchema = exports.turnLikelyEndedSchema = exports.turnUserStartedSchema = exports.errorSchema = exports.ERROR_CODES = exports.sessionEndSchema = exports.SESSION_END_REASONS = exports.sessionStartSchema = exports.SESSION_PROTOCOL = void 0;
|
|
4
|
+
exports.sessionProtocolDocument = sessionProtocolDocument;
|
|
5
|
+
// The fonoster.session.v1 messages (fonoster protocol), as JSON Schema. These schemas are the source
|
|
6
|
+
// of truth: TypeScript types are derived from them below, and schema/session.v1.json is exported
|
|
7
|
+
// from them for other languages (see scripts/export-session-schema.ts).
|
|
8
|
+
//
|
|
9
|
+
// Every schema allows extra fields: both sides ignore unknown fields (voice-session, "Message
|
|
10
|
+
// envelope and versioning"), so the protocol only grows.
|
|
11
|
+
exports.SESSION_PROTOCOL = "fonoster.session.v1";
|
|
12
|
+
const id = { type: "string", minLength: 1, maxLength: 128 };
|
|
13
|
+
const catalogId = { type: "string", minLength: 1 };
|
|
14
|
+
// ---- gateway → app ---------------------------------------------------------------------
|
|
15
|
+
exports.sessionStartSchema = {
|
|
16
|
+
type: "object",
|
|
17
|
+
required: [
|
|
18
|
+
"type",
|
|
19
|
+
"protocol",
|
|
20
|
+
"sessionRef",
|
|
21
|
+
"callRef",
|
|
22
|
+
"appRef",
|
|
23
|
+
"direction",
|
|
24
|
+
"from",
|
|
25
|
+
"to",
|
|
26
|
+
"metadata",
|
|
27
|
+
"handoff",
|
|
28
|
+
"audio",
|
|
29
|
+
"tracks",
|
|
30
|
+
"speech"
|
|
31
|
+
],
|
|
32
|
+
properties: {
|
|
33
|
+
type: { const: "session.start" },
|
|
34
|
+
protocol: { const: exports.SESSION_PROTOCOL },
|
|
35
|
+
sessionRef: { type: "string" },
|
|
36
|
+
callRef: { type: "string" },
|
|
37
|
+
appRef: { type: "string" },
|
|
38
|
+
direction: { enum: ["inbound", "outbound"] },
|
|
39
|
+
from: { type: "string" },
|
|
40
|
+
to: { type: "string" },
|
|
41
|
+
metadata: { type: "object" },
|
|
42
|
+
handoff: { oneOf: [{ type: "null" }, { type: "object" }] },
|
|
43
|
+
audio: {
|
|
44
|
+
type: "object",
|
|
45
|
+
required: ["codec", "rate"],
|
|
46
|
+
properties: {
|
|
47
|
+
// The native format menu (voice-session OQ8, AO-B4); Opus is not offered over WebSocket.
|
|
48
|
+
codec: { enum: ["pcm16", "pcmu"] },
|
|
49
|
+
rate: { enum: [8000, 16000, 24000] }
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
tracks: {
|
|
53
|
+
type: "array",
|
|
54
|
+
items: {
|
|
55
|
+
type: "object",
|
|
56
|
+
required: ["id", "side", "kind", "codec"],
|
|
57
|
+
properties: {
|
|
58
|
+
id: { type: "integer", minimum: 0, maximum: 255 },
|
|
59
|
+
side: { enum: ["inbound", "outbound"] },
|
|
60
|
+
kind: { enum: ["audio"] },
|
|
61
|
+
codec: { type: "string" }
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
speech: {
|
|
66
|
+
type: "object",
|
|
67
|
+
required: ["voice", "language"],
|
|
68
|
+
properties: { voice: { type: "string" }, language: { type: "string" } }
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
exports.SESSION_END_REASONS = [
|
|
73
|
+
"caller_hangup",
|
|
74
|
+
"app_hangup",
|
|
75
|
+
"transfer",
|
|
76
|
+
"handoff",
|
|
77
|
+
"error",
|
|
78
|
+
"timeout"
|
|
79
|
+
];
|
|
80
|
+
exports.sessionEndSchema = {
|
|
81
|
+
type: "object",
|
|
82
|
+
required: ["type", "reason"],
|
|
83
|
+
properties: {
|
|
84
|
+
type: { const: "session.end" },
|
|
85
|
+
reason: { enum: exports.SESSION_END_REASONS }
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
exports.ERROR_CODES = [
|
|
89
|
+
"bad_message",
|
|
90
|
+
"unknown_command",
|
|
91
|
+
"read_only",
|
|
92
|
+
"not_supported_by_protocol",
|
|
93
|
+
"provider_unavailable",
|
|
94
|
+
"provider_timeout",
|
|
95
|
+
"invalid_catalog_id",
|
|
96
|
+
"feature_not_supported",
|
|
97
|
+
"transfer_failed",
|
|
98
|
+
"on_hold",
|
|
99
|
+
"queue_full",
|
|
100
|
+
"play_fetch_failed",
|
|
101
|
+
"internal"
|
|
102
|
+
];
|
|
103
|
+
exports.errorSchema = {
|
|
104
|
+
type: "object",
|
|
105
|
+
required: ["type", "code", "message", "fatal"],
|
|
106
|
+
properties: {
|
|
107
|
+
type: { const: "error" },
|
|
108
|
+
code: { type: "string" },
|
|
109
|
+
message: { type: "string" },
|
|
110
|
+
fatal: { type: "boolean" },
|
|
111
|
+
relatesTo: id
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
exports.turnUserStartedSchema = {
|
|
115
|
+
type: "object",
|
|
116
|
+
required: ["type"],
|
|
117
|
+
properties: { type: { const: "turn.user_started" } }
|
|
118
|
+
};
|
|
119
|
+
exports.turnLikelyEndedSchema = {
|
|
120
|
+
type: "object",
|
|
121
|
+
required: ["type", "text"],
|
|
122
|
+
properties: { type: { const: "turn.likely_ended" }, text: { type: "string" } }
|
|
123
|
+
};
|
|
124
|
+
exports.turnEndedSchema = {
|
|
125
|
+
type: "object",
|
|
126
|
+
required: ["type", "text", "language", "confidence", "durationMs"],
|
|
127
|
+
properties: {
|
|
128
|
+
type: { const: "turn.ended" },
|
|
129
|
+
text: { type: "string" },
|
|
130
|
+
language: { type: "string" },
|
|
131
|
+
confidence: { type: "number", minimum: 0, maximum: 1 },
|
|
132
|
+
durationMs: { type: "number", minimum: 0 },
|
|
133
|
+
/** No speech followed turn.user_started for 10 s (noise, a dropped line). */
|
|
134
|
+
abandoned: { type: "boolean" }
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
exports.turnResumedSchema = {
|
|
138
|
+
type: "object",
|
|
139
|
+
required: ["type"],
|
|
140
|
+
properties: { type: { const: "turn.resumed" } }
|
|
141
|
+
};
|
|
142
|
+
exports.turnBackchannelSchema = {
|
|
143
|
+
type: "object",
|
|
144
|
+
required: ["type", "text"],
|
|
145
|
+
properties: { type: { const: "turn.backchannel" }, text: { type: "string" } }
|
|
146
|
+
};
|
|
147
|
+
exports.turnEstimateSchema = {
|
|
148
|
+
type: "object",
|
|
149
|
+
required: ["type", "eot", "speaking", "kind"],
|
|
150
|
+
properties: {
|
|
151
|
+
type: { const: "turn.estimate" },
|
|
152
|
+
eot: { type: "number", minimum: 0, maximum: 1 },
|
|
153
|
+
speaking: { type: "boolean" },
|
|
154
|
+
kind: { enum: ["speech", "backchannel", "interruption", "none"] }
|
|
155
|
+
}
|
|
156
|
+
};
|
|
157
|
+
exports.transcriptPartialSchema = {
|
|
158
|
+
type: "object",
|
|
159
|
+
required: ["type", "text"],
|
|
160
|
+
properties: {
|
|
161
|
+
type: { const: "transcript.partial" },
|
|
162
|
+
text: { type: "string" },
|
|
163
|
+
language: { type: "string" }
|
|
164
|
+
}
|
|
165
|
+
};
|
|
166
|
+
exports.outputStartedSchema = {
|
|
167
|
+
type: "object",
|
|
168
|
+
required: ["type", "id"],
|
|
169
|
+
properties: { type: { const: "output.started" }, id }
|
|
170
|
+
};
|
|
171
|
+
exports.outputDoneSchema = {
|
|
172
|
+
type: "object",
|
|
173
|
+
required: ["type", "id"],
|
|
174
|
+
properties: {
|
|
175
|
+
type: { const: "output.done" },
|
|
176
|
+
id,
|
|
177
|
+
heardMs: { type: "number", minimum: 0 }
|
|
178
|
+
}
|
|
179
|
+
};
|
|
180
|
+
exports.outputInterruptedSchema = {
|
|
181
|
+
type: "object",
|
|
182
|
+
required: ["type", "id", "heardText", "heardMs"],
|
|
183
|
+
properties: {
|
|
184
|
+
type: { const: "output.interrupted" },
|
|
185
|
+
id,
|
|
186
|
+
heardText: { type: "string" },
|
|
187
|
+
heardMs: { type: "number", minimum: 0 }
|
|
188
|
+
}
|
|
189
|
+
};
|
|
190
|
+
exports.markEventSchema = {
|
|
191
|
+
type: "object",
|
|
192
|
+
required: ["type", "name", "cleared"],
|
|
193
|
+
properties: {
|
|
194
|
+
type: { const: "mark" },
|
|
195
|
+
name: { type: "string" },
|
|
196
|
+
cleared: { type: "boolean" }
|
|
197
|
+
}
|
|
198
|
+
};
|
|
199
|
+
exports.dtmfEventSchema = {
|
|
200
|
+
type: "object",
|
|
201
|
+
required: ["type", "digit"],
|
|
202
|
+
properties: {
|
|
203
|
+
type: { const: "dtmf" },
|
|
204
|
+
digit: { type: "string", pattern: "^[0-9A-D*#]$" }
|
|
205
|
+
}
|
|
206
|
+
};
|
|
207
|
+
/** Answering-machine detection, once per call, for apps with `amd` (add-media-node 4.3). */
|
|
208
|
+
exports.amdResultSchema = {
|
|
209
|
+
type: "object",
|
|
210
|
+
required: ["type", "status", "confidence", "detector", "audioMs"],
|
|
211
|
+
properties: {
|
|
212
|
+
type: { const: "amd.result" },
|
|
213
|
+
status: { enum: ["human", "voicemail", "ivr", "machine", "unknown"] },
|
|
214
|
+
confidence: { type: "number", minimum: 0, maximum: 1 },
|
|
215
|
+
detector: { type: "string" },
|
|
216
|
+
/** How much caller audio the verdict is based on, from when the caller started speaking. */
|
|
217
|
+
audioMs: { type: "number", minimum: 0 },
|
|
218
|
+
/** Caller audio received before speech began (unset when the caller never spoke). */
|
|
219
|
+
speechOnsetMs: { type: "number", minimum: 0 },
|
|
220
|
+
/** Why detection could not run (status unknown): no_audio, no_speech, no_classifier, call_ended, … */
|
|
221
|
+
reason: { type: "string" }
|
|
222
|
+
}
|
|
223
|
+
};
|
|
224
|
+
// ---- app → gateway ---------------------------------------------------------------------
|
|
225
|
+
exports.speakSchema = {
|
|
226
|
+
type: "object",
|
|
227
|
+
required: ["type", "text"],
|
|
228
|
+
properties: {
|
|
229
|
+
type: { const: "speak" },
|
|
230
|
+
id,
|
|
231
|
+
text: { type: "string" },
|
|
232
|
+
last: { type: "boolean" },
|
|
233
|
+
voice: catalogId,
|
|
234
|
+
language: catalogId,
|
|
235
|
+
interruptible: { type: "boolean" },
|
|
236
|
+
preempt: { type: "boolean" }
|
|
237
|
+
}
|
|
238
|
+
};
|
|
239
|
+
exports.playSchema = {
|
|
240
|
+
type: "object",
|
|
241
|
+
required: ["type", "url"],
|
|
242
|
+
properties: {
|
|
243
|
+
type: { const: "play" },
|
|
244
|
+
id,
|
|
245
|
+
url: { type: "string", pattern: "^https?://" },
|
|
246
|
+
loop: { type: "boolean" },
|
|
247
|
+
interruptible: { type: "boolean" },
|
|
248
|
+
preempt: { type: "boolean" }
|
|
249
|
+
}
|
|
250
|
+
};
|
|
251
|
+
exports.listenUpdateSchema = {
|
|
252
|
+
type: "object",
|
|
253
|
+
required: ["type"],
|
|
254
|
+
properties: {
|
|
255
|
+
type: { const: "listen.update" },
|
|
256
|
+
id,
|
|
257
|
+
language: catalogId,
|
|
258
|
+
hints: { type: "array", items: { type: "string" } },
|
|
259
|
+
/** How long the Turn Detector waits before turn.ended. */
|
|
260
|
+
patience: { enum: ["low", "normal", "high"] }
|
|
261
|
+
}
|
|
262
|
+
};
|
|
263
|
+
exports.SUBSCRIBABLE_EVENTS = [
|
|
264
|
+
"turn.estimate",
|
|
265
|
+
"transcript.partial"
|
|
266
|
+
];
|
|
267
|
+
exports.subscribeSchema = {
|
|
268
|
+
type: "object",
|
|
269
|
+
required: ["type", "events"],
|
|
270
|
+
properties: {
|
|
271
|
+
type: { const: "subscribe" },
|
|
272
|
+
id,
|
|
273
|
+
events: { type: "array", items: { type: "string" } }
|
|
274
|
+
}
|
|
275
|
+
};
|
|
276
|
+
exports.markCommandSchema = {
|
|
277
|
+
type: "object",
|
|
278
|
+
required: ["type", "name"],
|
|
279
|
+
properties: {
|
|
280
|
+
type: { const: "mark" },
|
|
281
|
+
name: { type: "string", minLength: 1 }
|
|
282
|
+
}
|
|
283
|
+
};
|
|
284
|
+
exports.clearSchema = {
|
|
285
|
+
type: "object",
|
|
286
|
+
required: ["type"],
|
|
287
|
+
properties: { type: { const: "clear" }, id }
|
|
288
|
+
};
|
|
289
|
+
exports.hangupSchema = {
|
|
290
|
+
type: "object",
|
|
291
|
+
required: ["type"],
|
|
292
|
+
properties: { type: { const: "hangup" }, id }
|
|
293
|
+
};
|
|
294
|
+
exports.dtmfSendSchema = {
|
|
295
|
+
type: "object",
|
|
296
|
+
required: ["type", "digits"],
|
|
297
|
+
properties: {
|
|
298
|
+
type: { const: "dtmf.send" },
|
|
299
|
+
id,
|
|
300
|
+
digits: { type: "string", pattern: "^[0-9A-D*#w]+$" }
|
|
301
|
+
}
|
|
302
|
+
};
|
|
303
|
+
exports.TRANSFER_MODES = ["cold", "warm", "refer"];
|
|
304
|
+
exports.transferSchema = {
|
|
305
|
+
type: "object",
|
|
306
|
+
required: ["type", "to"],
|
|
307
|
+
properties: {
|
|
308
|
+
type: { const: "transfer" },
|
|
309
|
+
id,
|
|
310
|
+
to: { type: "string", minLength: 1 },
|
|
311
|
+
headers: { type: "object", additionalProperties: { type: "string" } },
|
|
312
|
+
mode: { enum: exports.TRANSFER_MODES },
|
|
313
|
+
/** Warm transfers: what the caller hears while waiting. */
|
|
314
|
+
music: { type: "string" }
|
|
315
|
+
}
|
|
316
|
+
};
|
|
317
|
+
exports.transferCompleteSchema = {
|
|
318
|
+
type: "object",
|
|
319
|
+
required: ["type", "id"],
|
|
320
|
+
properties: { type: { const: "transfer.complete" }, id }
|
|
321
|
+
};
|
|
322
|
+
exports.holdSchema = {
|
|
323
|
+
type: "object",
|
|
324
|
+
required: ["type"],
|
|
325
|
+
properties: { type: { const: "hold" }, id, music: { type: "string" } }
|
|
326
|
+
};
|
|
327
|
+
exports.resumeSchema = {
|
|
328
|
+
type: "object",
|
|
329
|
+
required: ["type"],
|
|
330
|
+
properties: { type: { const: "resume" }, id }
|
|
331
|
+
};
|
|
332
|
+
exports.handoffSchema = {
|
|
333
|
+
type: "object",
|
|
334
|
+
required: ["type", "app"],
|
|
335
|
+
properties: {
|
|
336
|
+
type: { const: "handoff" },
|
|
337
|
+
id,
|
|
338
|
+
app: { type: "string", minLength: 1 },
|
|
339
|
+
payload: { type: "object" }
|
|
340
|
+
}
|
|
341
|
+
};
|
|
342
|
+
exports.recordNoteSchema = {
|
|
343
|
+
type: "object",
|
|
344
|
+
required: ["type", "data"],
|
|
345
|
+
properties: {
|
|
346
|
+
type: { const: "record.note" },
|
|
347
|
+
id,
|
|
348
|
+
data: { type: "object" }
|
|
349
|
+
}
|
|
350
|
+
};
|
|
351
|
+
/** Schemas by message type, per direction. "mark" exists in both directions. */
|
|
352
|
+
exports.GATEWAY_MESSAGE_SCHEMAS = {
|
|
353
|
+
"session.start": exports.sessionStartSchema,
|
|
354
|
+
"session.end": exports.sessionEndSchema,
|
|
355
|
+
error: exports.errorSchema,
|
|
356
|
+
"turn.user_started": exports.turnUserStartedSchema,
|
|
357
|
+
"turn.likely_ended": exports.turnLikelyEndedSchema,
|
|
358
|
+
"turn.ended": exports.turnEndedSchema,
|
|
359
|
+
"turn.resumed": exports.turnResumedSchema,
|
|
360
|
+
"turn.backchannel": exports.turnBackchannelSchema,
|
|
361
|
+
"turn.estimate": exports.turnEstimateSchema,
|
|
362
|
+
"transcript.partial": exports.transcriptPartialSchema,
|
|
363
|
+
"output.started": exports.outputStartedSchema,
|
|
364
|
+
"output.done": exports.outputDoneSchema,
|
|
365
|
+
"output.interrupted": exports.outputInterruptedSchema,
|
|
366
|
+
mark: exports.markEventSchema,
|
|
367
|
+
dtmf: exports.dtmfEventSchema,
|
|
368
|
+
"amd.result": exports.amdResultSchema
|
|
369
|
+
};
|
|
370
|
+
exports.APP_COMMAND_SCHEMAS = {
|
|
371
|
+
speak: exports.speakSchema,
|
|
372
|
+
play: exports.playSchema,
|
|
373
|
+
"listen.update": exports.listenUpdateSchema,
|
|
374
|
+
subscribe: exports.subscribeSchema,
|
|
375
|
+
mark: exports.markCommandSchema,
|
|
376
|
+
clear: exports.clearSchema,
|
|
377
|
+
hangup: exports.hangupSchema,
|
|
378
|
+
"dtmf.send": exports.dtmfSendSchema,
|
|
379
|
+
transfer: exports.transferSchema,
|
|
380
|
+
"transfer.complete": exports.transferCompleteSchema,
|
|
381
|
+
hold: exports.holdSchema,
|
|
382
|
+
resume: exports.resumeSchema,
|
|
383
|
+
handoff: exports.handoffSchema,
|
|
384
|
+
"record.note": exports.recordNoteSchema
|
|
385
|
+
};
|
|
386
|
+
/** The whole protocol as one JSON Schema document, as exported to schema/session.v1.json. */
|
|
387
|
+
function sessionProtocolDocument() {
|
|
388
|
+
return {
|
|
389
|
+
$schema: "http://json-schema.org/draft-07/schema#",
|
|
390
|
+
$id: "https://fonoster.com/schemas/session/v1.json",
|
|
391
|
+
title: exports.SESSION_PROTOCOL,
|
|
392
|
+
description: "Messages of the Fonoster session protocol (a stream with protocol fonoster). Unknown types and fields must be ignored.",
|
|
393
|
+
protocol: exports.SESSION_PROTOCOL,
|
|
394
|
+
gatewayToApp: exports.GATEWAY_MESSAGE_SCHEMAS,
|
|
395
|
+
appToGateway: exports.APP_COMMAND_SCHEMAS
|
|
396
|
+
};
|
|
397
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
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
|
+
import type { KV, NatsConnection } from "nats";
|
|
20
|
+
/**
|
|
21
|
+
* The session registry (add-platform 2.3, add-media-node 3.2, decision D3): one NATS JetStream KV
|
|
22
|
+
* bucket shared by every service. Keys are `<kind>.<id>`:
|
|
23
|
+
*
|
|
24
|
+
* nodes.<nodeId> media node: { nodeId, rooms, draining, at } (written by the node)
|
|
25
|
+
* rooms.<roomRef> media node: { roomRef, nodeId, at } (written by the node)
|
|
26
|
+
* calls.<callRef> gateway: { callRef, gatewayRef, nodeId, roomRef, direction, at }
|
|
27
|
+
* + once answered: { appRef, from, to, startedAt }
|
|
28
|
+
* gateways.<gatewayRef> gateway: { gatewayRef, calls, at }
|
|
29
|
+
*
|
|
30
|
+
* The control plane reads `calls` and `gateways`: a call whose entry and whose gateway's entry
|
|
31
|
+
* both stop being rewritten lost its gateway mid-call, and gets `call.ended` (`appbridge-lost`).
|
|
32
|
+
*
|
|
33
|
+
* Entries are leases: the bucket keeps a value for SESSIONS_TTL_MS after its last write, owners
|
|
34
|
+
* rewrite theirs every SESSIONS_REFRESH_MS, and a crashed owner's entries expire on their own. The
|
|
35
|
+
* Go node uses the same bucket, TTL and key rules (go/medianode/internal/registry).
|
|
36
|
+
*/
|
|
37
|
+
export declare const SESSIONS_BUCKET = "fonoster_sessions";
|
|
38
|
+
export declare const SESSIONS_TTL_MS = 15000;
|
|
39
|
+
export declare const SESSIONS_REFRESH_MS = 5000;
|
|
40
|
+
export type SessionKind = "nodes" | "rooms" | "calls" | "gateways";
|
|
41
|
+
/** `<kind>.<id>` with characters KV keys do not allow replaced by `_`. */
|
|
42
|
+
export declare const sessionKey: (kind: SessionKind, id: string) => string;
|
|
43
|
+
/** Opens the bucket, creating it when it does not exist yet. */
|
|
44
|
+
export declare function openSessions(nc: NatsConnection, options?: {
|
|
45
|
+
bucket?: string;
|
|
46
|
+
ttlMs?: number;
|
|
47
|
+
}): Promise<KV>;
|
|
48
|
+
type Logger = {
|
|
49
|
+
warn(message: string): void;
|
|
50
|
+
};
|
|
51
|
+
/**
|
|
52
|
+
* An owner's entries, kept alive until deleted (the gateway's calls and its own gateway entry).
|
|
53
|
+
* Writes never throw: the registry is best effort and entries are rewritten on the next refresh.
|
|
54
|
+
*/
|
|
55
|
+
export declare class SessionLeases {
|
|
56
|
+
private readonly kv;
|
|
57
|
+
private readonly options;
|
|
58
|
+
private readonly entries;
|
|
59
|
+
private readonly timer;
|
|
60
|
+
private chain;
|
|
61
|
+
private closed;
|
|
62
|
+
constructor(kv: KV, options?: {
|
|
63
|
+
refreshMs?: number;
|
|
64
|
+
logger?: Logger;
|
|
65
|
+
});
|
|
66
|
+
private write;
|
|
67
|
+
/** Writes the entry now and keeps it alive. */
|
|
68
|
+
set(kind: SessionKind, id: string, value: Record<string, unknown>): Promise<unknown>;
|
|
69
|
+
/** Removes the entry. */
|
|
70
|
+
delete(kind: SessionKind, id: string): Promise<unknown>;
|
|
71
|
+
/** Reads any owner's entry from the bucket (undefined when absent or expired). */
|
|
72
|
+
get(kind: SessionKind, id: string): Promise<Record<string, unknown>>;
|
|
73
|
+
/** Rewrites every live entry (called on a timer). */
|
|
74
|
+
refresh(): Promise<unknown>;
|
|
75
|
+
/** Stops refreshing; with `remove`, deletes every entry first (clean shutdown). */
|
|
76
|
+
close(remove?: boolean): Promise<void>;
|
|
77
|
+
}
|
|
78
|
+
export type SessionEntry = {
|
|
79
|
+
kind: SessionKind;
|
|
80
|
+
id: string;
|
|
81
|
+
/** Undefined when the entry was deleted or purged. */
|
|
82
|
+
value?: Record<string, unknown>;
|
|
83
|
+
/** When the entry was last written (the server's timestamp). */
|
|
84
|
+
writtenAt: Date;
|
|
85
|
+
};
|
|
86
|
+
/**
|
|
87
|
+
* Follows one kind of entries: the current ones first (so a restarted replica knows the registry
|
|
88
|
+
* at once), then every change. Returns a stop function.
|
|
89
|
+
*/
|
|
90
|
+
export declare function watchSessions(kv: KV, kind: SessionKind, onEntry: (entry: SessionEntry) => void): Promise<() => void>;
|
|
91
|
+
/** Reads one entry (undefined when absent or expired). */
|
|
92
|
+
export declare function getSession(kv: KV, kind: SessionKind, id: string): Promise<Record<string, unknown> | undefined>;
|
|
93
|
+
export {};
|
|
@@ -0,0 +1,194 @@
|
|
|
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
|
+
var __asyncValues = (this && this.__asyncValues) || function (o) {
|
|
12
|
+
if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
|
|
13
|
+
var m = o[Symbol.asyncIterator], i;
|
|
14
|
+
return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i);
|
|
15
|
+
function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }
|
|
16
|
+
function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
|
|
17
|
+
};
|
|
18
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
19
|
+
exports.SessionLeases = exports.sessionKey = exports.SESSIONS_REFRESH_MS = exports.SESSIONS_TTL_MS = exports.SESSIONS_BUCKET = void 0;
|
|
20
|
+
exports.openSessions = openSessions;
|
|
21
|
+
exports.watchSessions = watchSessions;
|
|
22
|
+
exports.getSession = getSession;
|
|
23
|
+
/**
|
|
24
|
+
* The session registry (add-platform 2.3, add-media-node 3.2, decision D3): one NATS JetStream KV
|
|
25
|
+
* bucket shared by every service. Keys are `<kind>.<id>`:
|
|
26
|
+
*
|
|
27
|
+
* nodes.<nodeId> media node: { nodeId, rooms, draining, at } (written by the node)
|
|
28
|
+
* rooms.<roomRef> media node: { roomRef, nodeId, at } (written by the node)
|
|
29
|
+
* calls.<callRef> gateway: { callRef, gatewayRef, nodeId, roomRef, direction, at }
|
|
30
|
+
* + once answered: { appRef, from, to, startedAt }
|
|
31
|
+
* gateways.<gatewayRef> gateway: { gatewayRef, calls, at }
|
|
32
|
+
*
|
|
33
|
+
* The control plane reads `calls` and `gateways`: a call whose entry and whose gateway's entry
|
|
34
|
+
* both stop being rewritten lost its gateway mid-call, and gets `call.ended` (`appbridge-lost`).
|
|
35
|
+
*
|
|
36
|
+
* Entries are leases: the bucket keeps a value for SESSIONS_TTL_MS after its last write, owners
|
|
37
|
+
* rewrite theirs every SESSIONS_REFRESH_MS, and a crashed owner's entries expire on their own. The
|
|
38
|
+
* Go node uses the same bucket, TTL and key rules (go/medianode/internal/registry).
|
|
39
|
+
*/
|
|
40
|
+
exports.SESSIONS_BUCKET = "fonoster_sessions";
|
|
41
|
+
exports.SESSIONS_TTL_MS = 15000;
|
|
42
|
+
exports.SESSIONS_REFRESH_MS = 5000;
|
|
43
|
+
/** `<kind>.<id>` with characters KV keys do not allow replaced by `_`. */
|
|
44
|
+
const sessionKey = (kind, id) => `${kind}.${id.replace(/[^-_=/.a-zA-Z0-9]/g, "_")}`;
|
|
45
|
+
exports.sessionKey = sessionKey;
|
|
46
|
+
/** Opens the bucket, creating it when it does not exist yet. */
|
|
47
|
+
function openSessions(nc_1) {
|
|
48
|
+
return __awaiter(this, arguments, void 0, function* (nc, options = {}) {
|
|
49
|
+
var _a;
|
|
50
|
+
const bucket = (_a = options.bucket) !== null && _a !== void 0 ? _a : exports.SESSIONS_BUCKET;
|
|
51
|
+
const open = () => {
|
|
52
|
+
var _a;
|
|
53
|
+
return nc.jetstream().views.kv(bucket, {
|
|
54
|
+
history: 1,
|
|
55
|
+
ttl: (_a = options.ttlMs) !== null && _a !== void 0 ? _a : exports.SESSIONS_TTL_MS,
|
|
56
|
+
description: "Fonoster session registry: nodes, rooms, calls, gateways (leases)"
|
|
57
|
+
});
|
|
58
|
+
};
|
|
59
|
+
try {
|
|
60
|
+
return yield open();
|
|
61
|
+
}
|
|
62
|
+
catch (e) {
|
|
63
|
+
// Another service created it at the same moment with a slightly different config: bind.
|
|
64
|
+
if (/already in use/i.test(e.message))
|
|
65
|
+
return nc.jetstream().views.kv(bucket, { bindOnly: true });
|
|
66
|
+
throw e;
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* An owner's entries, kept alive until deleted (the gateway's calls and its own gateway entry).
|
|
72
|
+
* Writes never throw: the registry is best effort and entries are rewritten on the next refresh.
|
|
73
|
+
*/
|
|
74
|
+
class SessionLeases {
|
|
75
|
+
constructor(kv, options = {}) {
|
|
76
|
+
var _a, _b, _c;
|
|
77
|
+
this.kv = kv;
|
|
78
|
+
this.options = options;
|
|
79
|
+
this.entries = new Map();
|
|
80
|
+
this.chain = Promise.resolve();
|
|
81
|
+
this.closed = false;
|
|
82
|
+
this.timer = setInterval(() => this.refresh(), (_a = options.refreshMs) !== null && _a !== void 0 ? _a : exports.SESSIONS_REFRESH_MS);
|
|
83
|
+
(_c = (_b = this.timer).unref) === null || _c === void 0 ? void 0 : _c.call(_b);
|
|
84
|
+
}
|
|
85
|
+
write(key, value) {
|
|
86
|
+
// One write at a time, in order (a delete never overtakes the put before it).
|
|
87
|
+
this.chain = this.chain.then(() => __awaiter(this, void 0, void 0, function* () {
|
|
88
|
+
var _a;
|
|
89
|
+
try {
|
|
90
|
+
if (value)
|
|
91
|
+
yield this.kv.put(key, JSON.stringify(Object.assign(Object.assign({}, value), { at: new Date().toISOString() })));
|
|
92
|
+
else
|
|
93
|
+
yield this.kv.delete(key);
|
|
94
|
+
}
|
|
95
|
+
catch (e) {
|
|
96
|
+
(_a = this.options.logger) === null || _a === void 0 ? void 0 : _a.warn(`session registry write ${key} failed: ${e.message}`);
|
|
97
|
+
}
|
|
98
|
+
}));
|
|
99
|
+
return this.chain;
|
|
100
|
+
}
|
|
101
|
+
/** Writes the entry now and keeps it alive. */
|
|
102
|
+
set(kind, id, value) {
|
|
103
|
+
if (this.closed)
|
|
104
|
+
return Promise.resolve();
|
|
105
|
+
const key = (0, exports.sessionKey)(kind, id);
|
|
106
|
+
this.entries.set(key, value);
|
|
107
|
+
return this.write(key, value);
|
|
108
|
+
}
|
|
109
|
+
/** Removes the entry. */
|
|
110
|
+
delete(kind, id) {
|
|
111
|
+
if (this.closed)
|
|
112
|
+
return Promise.resolve();
|
|
113
|
+
const key = (0, exports.sessionKey)(kind, id);
|
|
114
|
+
this.entries.delete(key);
|
|
115
|
+
return this.write(key);
|
|
116
|
+
}
|
|
117
|
+
/** Reads any owner's entry from the bucket (undefined when absent or expired). */
|
|
118
|
+
get(kind, id) {
|
|
119
|
+
return getSession(this.kv, kind, id);
|
|
120
|
+
}
|
|
121
|
+
/** Rewrites every live entry (called on a timer). */
|
|
122
|
+
refresh() {
|
|
123
|
+
for (const [key, value] of this.entries)
|
|
124
|
+
void this.write(key, value);
|
|
125
|
+
return this.chain;
|
|
126
|
+
}
|
|
127
|
+
/** Stops refreshing; with `remove`, deletes every entry first (clean shutdown). */
|
|
128
|
+
close() {
|
|
129
|
+
return __awaiter(this, arguments, void 0, function* (remove = true) {
|
|
130
|
+
if (this.closed)
|
|
131
|
+
return;
|
|
132
|
+
clearInterval(this.timer);
|
|
133
|
+
if (remove)
|
|
134
|
+
for (const key of this.entries.keys())
|
|
135
|
+
void this.write(key);
|
|
136
|
+
this.closed = true;
|
|
137
|
+
this.entries.clear();
|
|
138
|
+
yield this.chain;
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
exports.SessionLeases = SessionLeases;
|
|
143
|
+
/**
|
|
144
|
+
* Follows one kind of entries: the current ones first (so a restarted replica knows the registry
|
|
145
|
+
* at once), then every change. Returns a stop function.
|
|
146
|
+
*/
|
|
147
|
+
function watchSessions(kv, kind, onEntry) {
|
|
148
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
149
|
+
const iter = yield kv.watch({ key: `${kind}.>` });
|
|
150
|
+
void (() => __awaiter(this, void 0, void 0, function* () {
|
|
151
|
+
var _a, e_1, _b, _c;
|
|
152
|
+
try {
|
|
153
|
+
for (var _d = true, _e = __asyncValues(iter), _f; _f = yield _e.next(), _a = _f.done, !_a; _d = true) {
|
|
154
|
+
_c = _f.value;
|
|
155
|
+
_d = false;
|
|
156
|
+
const e = _c;
|
|
157
|
+
const id = e.key.slice(kind.length + 1);
|
|
158
|
+
let value;
|
|
159
|
+
if (e.operation === "PUT") {
|
|
160
|
+
try {
|
|
161
|
+
value = JSON.parse(e.string());
|
|
162
|
+
}
|
|
163
|
+
catch (_g) {
|
|
164
|
+
continue;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
onEntry({ kind, id, value, writtenAt: e.created });
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
|
171
|
+
finally {
|
|
172
|
+
try {
|
|
173
|
+
if (!_d && !_a && (_b = _e.return)) yield _b.call(_e);
|
|
174
|
+
}
|
|
175
|
+
finally { if (e_1) throw e_1.error; }
|
|
176
|
+
}
|
|
177
|
+
}))();
|
|
178
|
+
return () => iter.stop();
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
/** Reads one entry (undefined when absent or expired). */
|
|
182
|
+
function getSession(kv, kind, id) {
|
|
183
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
184
|
+
const e = yield kv.get((0, exports.sessionKey)(kind, id));
|
|
185
|
+
if (!e || e.operation !== "PUT")
|
|
186
|
+
return undefined;
|
|
187
|
+
try {
|
|
188
|
+
return JSON.parse(e.string());
|
|
189
|
+
}
|
|
190
|
+
catch (_a) {
|
|
191
|
+
return undefined;
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
}
|