@memberjunction/server 5.44.0 → 5.45.1
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/agentSessions/ReturningVisitorRecap.d.ts +39 -0
- package/dist/agentSessions/ReturningVisitorRecap.d.ts.map +1 -0
- package/dist/agentSessions/ReturningVisitorRecap.js +198 -0
- package/dist/agentSessions/ReturningVisitorRecap.js.map +1 -0
- package/dist/agentSessions/SessionJanitor.d.ts +13 -0
- package/dist/agentSessions/SessionJanitor.d.ts.map +1 -1
- package/dist/agentSessions/SessionJanitor.js +65 -7
- package/dist/agentSessions/SessionJanitor.js.map +1 -1
- package/dist/agentSessions/SessionManager.d.ts.map +1 -1
- package/dist/agentSessions/SessionManager.js +37 -0
- package/dist/agentSessions/SessionManager.js.map +1 -1
- package/dist/auth/magicLink/MagicLinkRouter.d.ts +10 -0
- package/dist/auth/magicLink/MagicLinkRouter.d.ts.map +1 -1
- package/dist/auth/magicLink/MagicLinkRouter.js +16 -0
- package/dist/auth/magicLink/MagicLinkRouter.js.map +1 -1
- package/dist/auth/magicLink/MagicLinkService.d.ts +7 -0
- package/dist/auth/magicLink/MagicLinkService.d.ts.map +1 -1
- package/dist/auth/magicLink/MagicLinkService.js +13 -3
- package/dist/auth/magicLink/MagicLinkService.js.map +1 -1
- package/dist/auth/magicLink/index.d.ts +1 -1
- package/dist/auth/magicLink/index.d.ts.map +1 -1
- package/dist/auth/magicLink/index.js +1 -1
- package/dist/auth/magicLink/index.js.map +1 -1
- package/dist/auth/magicLink/magicLinkCore.d.ts +38 -1
- package/dist/auth/magicLink/magicLinkCore.d.ts.map +1 -1
- package/dist/auth/magicLink/magicLinkCore.js +42 -19
- package/dist/auth/magicLink/magicLinkCore.js.map +1 -1
- package/dist/auth/magicLink/types.d.ts +26 -0
- package/dist/auth/magicLink/types.d.ts.map +1 -1
- package/dist/config.d.ts +1883 -144
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +160 -36
- package/dist/config.js.map +1 -1
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +45 -2
- package/dist/context.js.map +1 -1
- package/dist/generated/generated.d.ts +522 -0
- package/dist/generated/generated.d.ts.map +1 -1
- package/dist/generated/generated.js +2860 -0
- package/dist/generated/generated.js.map +1 -1
- package/dist/generic/ResolverBase.d.ts +13 -0
- package/dist/generic/ResolverBase.d.ts.map +1 -1
- package/dist/generic/ResolverBase.js +22 -0
- package/dist/generic/ResolverBase.js.map +1 -1
- package/dist/generic/RunViewResolver.d.ts.map +1 -1
- package/dist/generic/RunViewResolver.js +4 -0
- package/dist/generic/RunViewResolver.js.map +1 -1
- package/dist/index.d.ts +0 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +101 -3
- package/dist/index.js.map +1 -1
- package/dist/integration/CustomColumnPromoter.d.ts.map +1 -1
- package/dist/integration/CustomColumnPromoter.js +6 -0
- package/dist/integration/CustomColumnPromoter.js.map +1 -1
- package/dist/realtimeWidget/WidgetRouter.d.ts +26 -0
- package/dist/realtimeWidget/WidgetRouter.d.ts.map +1 -0
- package/dist/realtimeWidget/WidgetRouter.js +182 -0
- package/dist/realtimeWidget/WidgetRouter.js.map +1 -0
- package/dist/realtimeWidget/WidgetSessionService.d.ts +265 -0
- package/dist/realtimeWidget/WidgetSessionService.d.ts.map +1 -0
- package/dist/realtimeWidget/WidgetSessionService.js +477 -0
- package/dist/realtimeWidget/WidgetSessionService.js.map +1 -0
- package/dist/realtimeWidget/host-identity.d.ts +40 -0
- package/dist/realtimeWidget/host-identity.d.ts.map +1 -0
- package/dist/realtimeWidget/host-identity.js +58 -0
- package/dist/realtimeWidget/host-identity.js.map +1 -0
- package/dist/realtimeWidget/index.d.ts +10 -0
- package/dist/realtimeWidget/index.d.ts.map +1 -0
- package/dist/realtimeWidget/index.js +9 -0
- package/dist/realtimeWidget/index.js.map +1 -0
- package/dist/realtimeWidget/visitorIdentity.d.ts +76 -0
- package/dist/realtimeWidget/visitorIdentity.d.ts.map +1 -0
- package/dist/realtimeWidget/visitorIdentity.js +202 -0
- package/dist/realtimeWidget/visitorIdentity.js.map +1 -0
- package/dist/realtimeWidget/widgetCore.d.ts +106 -0
- package/dist/realtimeWidget/widgetCore.d.ts.map +1 -0
- package/dist/realtimeWidget/widgetCore.js +173 -0
- package/dist/realtimeWidget/widgetCore.js.map +1 -0
- package/dist/realtimeWidget/widgetGuestElevation.d.ts +51 -0
- package/dist/realtimeWidget/widgetGuestElevation.d.ts.map +1 -0
- package/dist/realtimeWidget/widgetGuestElevation.js +79 -0
- package/dist/realtimeWidget/widgetGuestElevation.js.map +1 -0
- package/dist/resolvers/ComponentRegistryResolver.d.ts +7 -0
- package/dist/resolvers/ComponentRegistryResolver.d.ts.map +1 -1
- package/dist/resolvers/ComponentRegistryResolver.js +31 -8
- package/dist/resolvers/ComponentRegistryResolver.js.map +1 -1
- package/dist/resolvers/EntityPermissionResolver.d.ts.map +1 -1
- package/dist/resolvers/EntityPermissionResolver.js +1 -2
- package/dist/resolvers/EntityPermissionResolver.js.map +1 -1
- package/dist/resolvers/QuerySystemUserResolver.d.ts +9 -6
- package/dist/resolvers/QuerySystemUserResolver.d.ts.map +1 -1
- package/dist/resolvers/QuerySystemUserResolver.js +15 -13
- package/dist/resolvers/QuerySystemUserResolver.js.map +1 -1
- package/dist/resolvers/RealtimeClientSessionResolver.d.ts +10 -0
- package/dist/resolvers/RealtimeClientSessionResolver.d.ts.map +1 -1
- package/dist/resolvers/RealtimeClientSessionResolver.js +47 -3
- package/dist/resolvers/RealtimeClientSessionResolver.js.map +1 -1
- package/dist/resolvers/RingCentralTelephonyResolver.d.ts +27 -0
- package/dist/resolvers/RingCentralTelephonyResolver.d.ts.map +1 -0
- package/dist/resolvers/RingCentralTelephonyResolver.js +87 -0
- package/dist/resolvers/RingCentralTelephonyResolver.js.map +1 -0
- package/dist/resolvers/RunAIAgentResolver.d.ts +9 -0
- package/dist/resolvers/RunAIAgentResolver.d.ts.map +1 -1
- package/dist/resolvers/RunAIAgentResolver.js +20 -4
- package/dist/resolvers/RunAIAgentResolver.js.map +1 -1
- package/dist/resolvers/SearchKnowledgeResolver.d.ts.map +1 -1
- package/dist/resolvers/SearchKnowledgeResolver.js +2 -0
- package/dist/resolvers/SearchKnowledgeResolver.js.map +1 -1
- package/dist/resolvers/TeamsMeetingsResolver.d.ts +28 -0
- package/dist/resolvers/TeamsMeetingsResolver.d.ts.map +1 -0
- package/dist/resolvers/TeamsMeetingsResolver.js +91 -0
- package/dist/resolvers/TeamsMeetingsResolver.js.map +1 -0
- package/dist/resolvers/TelephonyResolver.d.ts +26 -0
- package/dist/resolvers/TelephonyResolver.d.ts.map +1 -0
- package/dist/resolvers/TelephonyResolver.js +86 -0
- package/dist/resolvers/TelephonyResolver.js.map +1 -0
- package/dist/resolvers/TestQuerySQLResolver.d.ts +1 -1
- package/dist/resolvers/TestQuerySQLResolver.d.ts.map +1 -1
- package/dist/resolvers/TestQuerySQLResolver.js +2 -3
- package/dist/resolvers/TestQuerySQLResolver.js.map +1 -1
- package/dist/resolvers/VonageTelephonyResolver.d.ts +26 -0
- package/dist/resolvers/VonageTelephonyResolver.d.ts.map +1 -0
- package/dist/resolvers/VonageTelephonyResolver.js +86 -0
- package/dist/resolvers/VonageTelephonyResolver.js.map +1 -0
- package/dist/rest/MediaStreamHandler.js +4 -1
- package/dist/rest/MediaStreamHandler.js.map +1 -1
- package/dist/telephony/RingCentralTelephonyService.d.ts +115 -0
- package/dist/telephony/RingCentralTelephonyService.d.ts.map +1 -0
- package/dist/telephony/RingCentralTelephonyService.js +262 -0
- package/dist/telephony/RingCentralTelephonyService.js.map +1 -0
- package/dist/telephony/TeamsMeetingsRouter.d.ts +40 -0
- package/dist/telephony/TeamsMeetingsRouter.d.ts.map +1 -0
- package/dist/telephony/TeamsMeetingsRouter.js +96 -0
- package/dist/telephony/TeamsMeetingsRouter.js.map +1 -0
- package/dist/telephony/TeamsMeetingsService.d.ts +113 -0
- package/dist/telephony/TeamsMeetingsService.d.ts.map +1 -0
- package/dist/telephony/TeamsMeetingsService.js +196 -0
- package/dist/telephony/TeamsMeetingsService.js.map +1 -0
- package/dist/telephony/TwilioTelephonyRouter.d.ts +36 -0
- package/dist/telephony/TwilioTelephonyRouter.d.ts.map +1 -0
- package/dist/telephony/TwilioTelephonyRouter.js +143 -0
- package/dist/telephony/TwilioTelephonyRouter.js.map +1 -0
- package/dist/telephony/TwilioTelephonyService.d.ts +95 -0
- package/dist/telephony/TwilioTelephonyService.d.ts.map +1 -0
- package/dist/telephony/TwilioTelephonyService.js +193 -0
- package/dist/telephony/TwilioTelephonyService.js.map +1 -0
- package/dist/telephony/VonageTelephonyRouter.d.ts +41 -0
- package/dist/telephony/VonageTelephonyRouter.d.ts.map +1 -0
- package/dist/telephony/VonageTelephonyRouter.js +201 -0
- package/dist/telephony/VonageTelephonyRouter.js.map +1 -0
- package/dist/telephony/VonageTelephonyService.d.ts +90 -0
- package/dist/telephony/VonageTelephonyService.d.ts.map +1 -0
- package/dist/telephony/VonageTelephonyService.js +191 -0
- package/dist/telephony/VonageTelephonyService.js.map +1 -0
- package/dist/telephony/calendar-scheduler.d.ts +67 -0
- package/dist/telephony/calendar-scheduler.d.ts.map +1 -0
- package/dist/telephony/calendar-scheduler.js +133 -0
- package/dist/telephony/calendar-scheduler.js.map +1 -0
- package/dist/telephony/index.d.ts +29 -0
- package/dist/telephony/index.d.ts.map +1 -0
- package/dist/telephony/index.js +38 -0
- package/dist/telephony/index.js.map +1 -0
- package/dist/telephony/media-upgrade-router.d.ts +33 -0
- package/dist/telephony/media-upgrade-router.d.ts.map +1 -0
- package/dist/telephony/media-upgrade-router.js +56 -0
- package/dist/telephony/media-upgrade-router.js.map +1 -0
- package/dist/telephony/ringcentral-runtime.d.ts +14 -0
- package/dist/telephony/ringcentral-runtime.d.ts.map +1 -0
- package/dist/telephony/ringcentral-runtime.js +18 -0
- package/dist/telephony/ringcentral-runtime.js.map +1 -0
- package/dist/telephony/teams-meetings-runtime.d.ts +16 -0
- package/dist/telephony/teams-meetings-runtime.d.ts.map +1 -0
- package/dist/telephony/teams-meetings-runtime.js +20 -0
- package/dist/telephony/teams-meetings-runtime.js.map +1 -0
- package/dist/telephony/teamsAcsMediaRegistry.d.ts +69 -0
- package/dist/telephony/teamsAcsMediaRegistry.d.ts.map +1 -0
- package/dist/telephony/teamsAcsMediaRegistry.js +118 -0
- package/dist/telephony/teamsAcsMediaRegistry.js.map +1 -0
- package/dist/telephony/telephony-runtime.d.ts +14 -0
- package/dist/telephony/telephony-runtime.d.ts.map +1 -0
- package/dist/telephony/telephony-runtime.js +18 -0
- package/dist/telephony/telephony-runtime.js.map +1 -0
- package/dist/telephony/twilioMediaRegistry.d.ts +60 -0
- package/dist/telephony/twilioMediaRegistry.d.ts.map +1 -0
- package/dist/telephony/twilioMediaRegistry.js +106 -0
- package/dist/telephony/twilioMediaRegistry.js.map +1 -0
- package/dist/telephony/vonage-runtime.d.ts +14 -0
- package/dist/telephony/vonage-runtime.d.ts.map +1 -0
- package/dist/telephony/vonage-runtime.js +18 -0
- package/dist/telephony/vonage-runtime.js.map +1 -0
- package/dist/telephony/vonageMediaRegistry.d.ts +78 -0
- package/dist/telephony/vonageMediaRegistry.d.ts.map +1 -0
- package/dist/telephony/vonageMediaRegistry.js +158 -0
- package/dist/telephony/vonageMediaRegistry.js.map +1 -0
- package/package.json +88 -83
- package/src/__tests__/RunAIAgentResolver.streaming.test.ts +128 -0
- package/src/__tests__/magicLink.test.ts +53 -3
- package/src/__tests__/search-knowledge-tags.test.ts +9 -9
- package/src/__tests__/teams-meetings-service.test.ts +65 -0
- package/src/__tests__/teamsAcsMediaRegistry.test.ts +82 -0
- package/src/__tests__/twilio-telephony-service.test.ts +23 -0
- package/src/__tests__/twilioMediaRegistry.test.ts +103 -0
- package/src/__tests__/vonageMediaRegistry.test.ts +180 -0
- package/src/__tests__/widget.test.ts +204 -0
- package/src/__tests__/widgetGuestElevation.test.ts +57 -0
- package/src/__tests__/widgetHostIdentity.test.ts +117 -0
- package/src/agentSessions/ReturningVisitorRecap.ts +242 -0
- package/src/agentSessions/SessionJanitor.ts +66 -6
- package/src/agentSessions/SessionManager.ts +38 -0
- package/src/auth/magicLink/MagicLinkRouter.ts +17 -0
- package/src/auth/magicLink/MagicLinkService.ts +13 -3
- package/src/auth/magicLink/index.ts +1 -0
- package/src/auth/magicLink/magicLinkCore.ts +48 -20
- package/src/auth/magicLink/types.ts +26 -0
- package/src/config.ts +172 -38
- package/src/context.ts +50 -3
- package/src/generated/generated.ts +1991 -1
- package/src/generic/ResolverBase.ts +28 -0
- package/src/generic/RunViewResolver.ts +4 -0
- package/src/index.ts +109 -3
- package/src/integration/CustomColumnPromoter.ts +6 -0
- package/src/realtimeWidget/WidgetRouter.ts +204 -0
- package/src/realtimeWidget/WidgetSessionService.ts +714 -0
- package/src/realtimeWidget/host-identity.ts +84 -0
- package/src/realtimeWidget/index.ts +15 -0
- package/src/realtimeWidget/visitorIdentity.ts +258 -0
- package/src/realtimeWidget/widgetCore.ts +231 -0
- package/src/realtimeWidget/widgetGuestElevation.ts +115 -0
- package/src/resolvers/ComponentRegistryResolver.ts +36 -10
- package/src/resolvers/EntityPermissionResolver.ts +1 -2
- package/src/resolvers/QuerySystemUserResolver.ts +14 -10
- package/src/resolvers/RealtimeClientSessionResolver.ts +59 -2
- package/src/resolvers/RingCentralTelephonyResolver.ts +64 -0
- package/src/resolvers/RunAIAgentResolver.ts +32 -8
- package/src/resolvers/SearchKnowledgeResolver.ts +2 -0
- package/src/resolvers/TeamsMeetingsResolver.ts +68 -0
- package/src/resolvers/TelephonyResolver.ts +63 -0
- package/src/resolvers/TestQuerySQLResolver.ts +2 -3
- package/src/resolvers/VonageTelephonyResolver.ts +63 -0
- package/src/rest/MediaStreamHandler.ts +4 -1
- package/src/telephony/RingCentralTelephonyService.ts +342 -0
- package/src/telephony/TeamsMeetingsRouter.ts +124 -0
- package/src/telephony/TeamsMeetingsService.ts +268 -0
- package/src/telephony/TwilioTelephonyRouter.ts +184 -0
- package/src/telephony/TwilioTelephonyService.ts +261 -0
- package/src/telephony/VonageTelephonyRouter.ts +239 -0
- package/src/telephony/VonageTelephonyService.ts +259 -0
- package/src/telephony/calendar-scheduler.ts +176 -0
- package/src/telephony/index.ts +43 -0
- package/src/telephony/media-upgrade-router.ts +62 -0
- package/src/telephony/ringcentral-runtime.ts +22 -0
- package/src/telephony/teams-meetings-runtime.ts +24 -0
- package/src/telephony/teamsAcsMediaRegistry.ts +152 -0
- package/src/telephony/telephony-runtime.ts +22 -0
- package/src/telephony/twilioMediaRegistry.ts +137 -0
- package/src/telephony/vonage-runtime.ts +22 -0
- package/src/telephony/vonageMediaRegistry.ts +200 -0
- package/dist/agents/skip-agent.d.ts +0 -111
- package/dist/agents/skip-agent.d.ts.map +0 -1
- package/dist/agents/skip-agent.js +0 -1487
- package/dist/agents/skip-agent.js.map +0 -1
- package/dist/agents/skip-sdk.d.ts +0 -261
- package/dist/agents/skip-sdk.d.ts.map +0 -1
- package/dist/agents/skip-sdk.js +0 -909
- package/dist/agents/skip-sdk.js.map +0 -1
- package/src/agents/skip-agent.ts +0 -1594
- package/src/agents/skip-sdk.ts +0 -1210
|
@@ -106,35 +106,63 @@ export function evaluateInvite(invite: InviteEvaluationInput, nowMs: number): {
|
|
|
106
106
|
return { ok: true };
|
|
107
107
|
}
|
|
108
108
|
|
|
109
|
+
/** SQL dialect for {@link buildConsumeInviteSQL}. */
|
|
110
|
+
export type ConsumeInviteDialect = 'sqlserver' | 'postgresql';
|
|
111
|
+
|
|
112
|
+
/** SQL Server table identifier — bracket-quoted `[schema].[table]` (word chars only). */
|
|
113
|
+
const QUALIFIED_TABLE_PATTERN = /^\[\w+\]\.\[\w+\]$/;
|
|
114
|
+
/** PostgreSQL table identifier — `schema.table` (word chars only); auto-quoted downstream. */
|
|
115
|
+
const QUALIFIED_TABLE_PATTERN_PG = /^\w+\.\w+$/;
|
|
116
|
+
|
|
109
117
|
/**
|
|
110
|
-
* Builds the atomic compare-and-swap UPDATE that consumes one use of an invite
|
|
118
|
+
* Builds the atomic compare-and-swap UPDATE that consumes one use of an invite,
|
|
119
|
+
* in the given SQL dialect.
|
|
111
120
|
*
|
|
112
121
|
* The WHERE clause re-checks every eligibility condition at the DB level, so the
|
|
113
122
|
* increment and the guard are a single atomic operation: concurrent redemptions
|
|
114
|
-
* of a single-use link race on the row and exactly one matches
|
|
115
|
-
*
|
|
116
|
-
*
|
|
123
|
+
* of a single-use link race on the row and exactly one matches. This is what
|
|
124
|
+
* actually enforces single-use — the JS-side `evaluateInvite` is only a friendly
|
|
125
|
+
* pre-check. The matched row's ID is returned to the caller (via `OUTPUT` on SQL
|
|
126
|
+
* Server, `RETURNING` on PostgreSQL) so it can detect a win (exactly one row).
|
|
127
|
+
*
|
|
128
|
+
* The invite ID MUST be bound as a parameter by the caller (`@p0` on SQL Server,
|
|
129
|
+
* `$1` on PostgreSQL) — it is never interpolated into the string, so this builder
|
|
130
|
+
* is injection-safe regardless of the ID's contents.
|
|
117
131
|
*
|
|
118
|
-
*
|
|
119
|
-
*
|
|
120
|
-
*
|
|
132
|
+
* **SQL Server** — `OUTPUT` goes `INTO` a table variable (not a bare OUTPUT):
|
|
133
|
+
* SQL Server forbids a bare OUTPUT clause on a table that has enabled triggers,
|
|
134
|
+
* and CodeGen adds an `__mj_UpdatedAt` trigger to every MJ table. The trailing
|
|
135
|
+
* SELECT returns the matched row(s) regardless of trigger behavior.
|
|
121
136
|
*
|
|
122
|
-
*
|
|
123
|
-
*
|
|
124
|
-
*
|
|
125
|
-
*
|
|
126
|
-
*
|
|
137
|
+
* **PostgreSQL** — the `UPDATE … WHERE` guard IS itself the atomic single-use
|
|
138
|
+
* gate: the row is locked for the UPDATE's duration, so concurrent redemptions
|
|
139
|
+
* serialize and only the first matching `Status='Active' AND UseCount < MaxUses`
|
|
140
|
+
* wins; `RETURNING` yields the row iff the guard matched (equivalent to the SS
|
|
141
|
+
* `OUTPUT`-into-table win-detect). PascalCase identifiers are auto-quoted by
|
|
142
|
+
* `PostgreSQLDataProvider.ExecuteSQL`, so the table is passed unquoted (`schema.table`).
|
|
127
143
|
*
|
|
128
|
-
* `qualifiedTable` is asserted to
|
|
129
|
-
*
|
|
130
|
-
*
|
|
131
|
-
*
|
|
144
|
+
* `qualifiedTable` is asserted to match the dialect's whitelist pattern before
|
|
145
|
+
* interpolation. The caller derives it from `EntityInfo` (never user input), so
|
|
146
|
+
* this is defense-in-depth: even a future careless caller cannot turn this into
|
|
147
|
+
* an injection vector — a non-conforming table throws.
|
|
132
148
|
*
|
|
133
|
-
* @param qualifiedTable
|
|
149
|
+
* @param qualifiedTable `[schema].[table]` (sqlserver) or `schema.table` (postgresql) for MagicLinkInvite
|
|
150
|
+
* @param dialect target SQL dialect (defaults to `'sqlserver'`)
|
|
134
151
|
*/
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
152
|
+
export function buildConsumeInviteSQL(qualifiedTable: string, dialect: ConsumeInviteDialect = 'sqlserver'): string {
|
|
153
|
+
if (dialect === 'postgresql') {
|
|
154
|
+
if (!QUALIFIED_TABLE_PATTERN_PG.test(qualifiedTable)) {
|
|
155
|
+
throw new Error(`buildConsumeInviteSQL: refusing to build SQL for non-whitelisted table identifier '${qualifiedTable}'.`);
|
|
156
|
+
}
|
|
157
|
+
return (
|
|
158
|
+
`UPDATE ${qualifiedTable} ` +
|
|
159
|
+
`SET UseCount = UseCount + 1, ` +
|
|
160
|
+
`ConsumedAt = COALESCE(ConsumedAt, (now() AT TIME ZONE 'utc')), ` +
|
|
161
|
+
`Status = CASE WHEN UseCount + 1 >= MaxUses THEN 'Consumed' ELSE Status END ` +
|
|
162
|
+
`WHERE ID = $1 AND Status = 'Active' AND UseCount < MaxUses AND ExpiresAt > (now() AT TIME ZONE 'utc') ` +
|
|
163
|
+
`RETURNING ID;`
|
|
164
|
+
);
|
|
165
|
+
}
|
|
138
166
|
if (!QUALIFIED_TABLE_PATTERN.test(qualifiedTable)) {
|
|
139
167
|
throw new Error(`buildConsumeInviteSQL: refusing to build SQL for non-whitelisted table identifier '${qualifiedTable}'.`);
|
|
140
168
|
}
|
|
@@ -58,6 +58,32 @@ export interface MagicLinkJWTClaims {
|
|
|
58
58
|
mj_anon?: boolean;
|
|
59
59
|
/** Opaque per-session id — correlates one anonymous session's activity across audit rows without a real user. */
|
|
60
60
|
mj_sid?: string;
|
|
61
|
+
/**
|
|
62
|
+
* Public web widget instance id (additive — set only for widget guest sessions
|
|
63
|
+
* minted by WidgetSessionService). Binds the synthesized guest principal to one
|
|
64
|
+
* widget instance so its pinned agent / guest role can be locked down. Absent on
|
|
65
|
+
* ordinary magic-link sessions.
|
|
66
|
+
*/
|
|
67
|
+
mj_widget_id?: string;
|
|
68
|
+
/**
|
|
69
|
+
* Host-asserted visitor email for a widget `host-identity` session (additive). The
|
|
70
|
+
* authoritative `email`/`sub` still resolve the constrained shared Anonymous principal;
|
|
71
|
+
* this carries WHO the host says the visitor is, for the agent to look up their account —
|
|
72
|
+
* without granting that account's permissions. Absent on anonymous/ordinary sessions.
|
|
73
|
+
*/
|
|
74
|
+
mj_host_email?: string;
|
|
75
|
+
/**
|
|
76
|
+
* Returning-visitor anchor for a widget guest session (additive, RV1). Carried so the VOICE path —
|
|
77
|
+
* whose conversation is created server-side — can stamp the same Conversation.VisitorKey the text
|
|
78
|
+
* path stamps client-side. Present only when the widget remembers returning visitors.
|
|
79
|
+
*/
|
|
80
|
+
mj_visitor_key?: string;
|
|
81
|
+
/** The prior conversation this visit chains from (RV2), for the voice path to stamp Conversation.LastConversationID. */
|
|
82
|
+
mj_last_conversation_id?: string;
|
|
83
|
+
/** Resolved polymorphic identity entity id (RV4), for the voice path to stamp the existing Conversation.LinkedEntityID / AIAgentSession.LinkedEntityID. */
|
|
84
|
+
mj_linked_entity_id?: string;
|
|
85
|
+
/** Resolved polymorphic identity record id (RV4), for the voice path to stamp the existing Conversation.LinkedRecordID / AIAgentSession.LinkedRecordID. */
|
|
86
|
+
mj_linked_record_id?: string;
|
|
61
87
|
/** Marks the session as magic-link so the Explorer can confine the UI. */
|
|
62
88
|
mj_magic_link: true;
|
|
63
89
|
}
|
package/src/config.ts
CHANGED
|
@@ -68,23 +68,6 @@ const zodBooleanWithTransforms = () => {
|
|
|
68
68
|
})
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
-
const askSkipInfoSchema = z.object({
|
|
72
|
-
url: z.string().optional(), // Base URL for Skip API
|
|
73
|
-
apiKey: z.string().optional(),
|
|
74
|
-
orgID: z.string().optional(),
|
|
75
|
-
organizationInfo: z.string().optional(),
|
|
76
|
-
entitiesToSend: z
|
|
77
|
-
.object({
|
|
78
|
-
excludeSchemas: z.array(z.string()).optional(),
|
|
79
|
-
includeEntitiesFromExcludedSchemas: z.array(z.string()).optional(),
|
|
80
|
-
})
|
|
81
|
-
.optional(),
|
|
82
|
-
chatURL: z.string().optional(),
|
|
83
|
-
learningCycleRunUponStartup: zodBooleanWithTransforms(),
|
|
84
|
-
learningCycleEnabled: zodBooleanWithTransforms(),
|
|
85
|
-
learningCycleURL: z.string().optional(),
|
|
86
|
-
learningCycleIntervalInMinutes: z.coerce.number().optional(),
|
|
87
|
-
});
|
|
88
71
|
|
|
89
72
|
const sqlLoggingOptionsSchema = z.object({
|
|
90
73
|
formatAsMigration: z.boolean().optional().default(false),
|
|
@@ -330,13 +313,178 @@ const magicLinkSchema = z.object({
|
|
|
330
313
|
explorerUrl: z.string().optional(),
|
|
331
314
|
}).passthrough();
|
|
332
315
|
|
|
316
|
+
/**
|
|
317
|
+
* Public web widget config (plans/realtime/bridges-and-widget/public-web-widget.md).
|
|
318
|
+
* The widget REUSES the magic-link RS256 key + `anonymous-embed` synthesis, so it
|
|
319
|
+
* shares the magic-link `audience`/issuer/JWKS by default — the same auth provider
|
|
320
|
+
* validates both. When `enabled`, the server ensures the magic-link key manager is
|
|
321
|
+
* initialized and the provider registered even if `magicLink.enabled` is false.
|
|
322
|
+
*/
|
|
323
|
+
const widgetSchema = z.object({
|
|
324
|
+
/** Master switch. When false, the /widget routes are not mounted. */
|
|
325
|
+
enabled: zodBooleanWithTransforms().default(false),
|
|
326
|
+
/**
|
|
327
|
+
* Which signing mechanism the widget reuses. Only `magic-link` is supported today
|
|
328
|
+
* (shares MagicLinkKeyManager + the magic-link auth provider). Recorded so a future
|
|
329
|
+
* dedicated widget key is a config flip, not a code change.
|
|
330
|
+
*/
|
|
331
|
+
signingReuse: z.enum(['magic-link']).optional().default('magic-link'),
|
|
332
|
+
/**
|
|
333
|
+
* Audience claim for minted guest JWTs. MUST equal the magic-link audience so the
|
|
334
|
+
* auto-registered `magic-link` auth provider validates widget tokens.
|
|
335
|
+
*/
|
|
336
|
+
audience: z.string().optional().default('mj-magic-link'),
|
|
337
|
+
/** Email of the shared Anonymous principal used in guest claims (resolves the synthesized guest user). */
|
|
338
|
+
anonymousEmail: z.string().optional().default('anonymous@magic-link.local'),
|
|
339
|
+
/** Fallback session-token TTL (minutes) when a widget instance does not set its own. */
|
|
340
|
+
defaultSessionTtlMinutes: z.coerce.number().optional().default(15),
|
|
341
|
+
/** Fallback mint rate-limit (per IP per window) when a widget instance does not set its own. */
|
|
342
|
+
defaultRateLimitPerMinute: z.coerce.number().optional().default(30),
|
|
343
|
+
/** Rate-limit window (ms) for the public /widget/session endpoints. Default 60s. */
|
|
344
|
+
rateLimitWindowMs: z.coerce.number().optional().default(60_000),
|
|
345
|
+
/** Server-wide default hard ceiling (minutes) on a voice session when an instance omits one (W4). */
|
|
346
|
+
voiceDefaultMaxSessionMinutes: z.coerce.number().optional().default(10),
|
|
347
|
+
/** Email/name of the internal user whose context READS widget config at mint time (falls back to system/Owner). */
|
|
348
|
+
contextUserForLookup: z.string().optional(),
|
|
349
|
+
/**
|
|
350
|
+
* Host-identity public keys (PEM), keyed by widget PublicKey, for the `host-identity` auth
|
|
351
|
+
* strategy (D1). The host signs a short-lived RS256 assertion; the mint verifies it against
|
|
352
|
+
* the key here. INTERIM location — production should store the host key per widget instance
|
|
353
|
+
* (a HostPublicKey column on WidgetInstance, pending a migration). When a widget's strategy is
|
|
354
|
+
* HostIdentity and no key is configured here, host mints fail closed.
|
|
355
|
+
*/
|
|
356
|
+
hostPublicKeys: z.record(z.string(), z.string()).optional().default({}),
|
|
357
|
+
/**
|
|
358
|
+
* Returning-visitor identity-resolution target (RV4). When a visitor verifies (magic-link upgrade)
|
|
359
|
+
* or a host asserts their identity, their email is resolved to a record `(entityName, recordId)`
|
|
360
|
+
* and prior anonymous memory is merged onto that polymorphic pair. Deployment-configurable so the
|
|
361
|
+
* resolved record need NOT be an MJ User — point it at any entity that carries the visitor's email
|
|
362
|
+
* (e.g. a CRM `Persons` table). Defaults to the core `Users` entity keyed by `Email`.
|
|
363
|
+
*/
|
|
364
|
+
identityResolution: z.object({
|
|
365
|
+
entityName: z.string().optional().default('Users'),
|
|
366
|
+
emailField: z.string().optional().default('Email'),
|
|
367
|
+
}).optional(),
|
|
368
|
+
}).passthrough();
|
|
369
|
+
|
|
370
|
+
/**
|
|
371
|
+
* Telephony bridge config (plans/realtime/bridges-and-widget/telephony-vendor-bindings.md).
|
|
372
|
+
* Holds the per-vendor credentials + public stream URL the MJAPI telephony ingress wires onto
|
|
373
|
+
* the already-stubbed native SDK seams. Credentials resolve here from `mj.config.cjs` / env —
|
|
374
|
+
* never inlined in code. When `twilio` is omitted, the Twilio telephony routes are not mounted.
|
|
375
|
+
*/
|
|
376
|
+
const twilioTelephonySchema = z.object({
|
|
377
|
+
/** Twilio Account SID (`AC…`). */
|
|
378
|
+
accountSid: z.string(),
|
|
379
|
+
/** Account auth token — REST auth (when no API key pair) AND the HMAC key for X-Twilio-Signature verification. */
|
|
380
|
+
authToken: z.string().optional(),
|
|
381
|
+
/** API Key SID (`SK…`) — preferred over the auth token for REST auth when paired with apiKeySecret. */
|
|
382
|
+
apiKeySid: z.string().optional(),
|
|
383
|
+
/** API Key secret — paired with apiKeySid. */
|
|
384
|
+
apiKeySecret: z.string().optional(),
|
|
385
|
+
/** The publicly reachable `wss://…/telephony/twilio/media` URL Twilio's <Connect><Stream> connects to. */
|
|
386
|
+
streamPublicUrl: z.string(),
|
|
387
|
+
/** Optional shared secret gating the public webhook/WSS endpoints (defense-in-depth beyond signature verification). */
|
|
388
|
+
webhookSigningSecret: z.string().optional(),
|
|
389
|
+
/** Optional status-callback URL Twilio posts call lifecycle events to. */
|
|
390
|
+
statusCallbackUrl: z.string().optional(),
|
|
391
|
+
}).passthrough();
|
|
392
|
+
|
|
393
|
+
/**
|
|
394
|
+
* Vonage Voice + WebSocket-media telephony binding. Voice-API auth is application-scoped
|
|
395
|
+
* (application id + RSA private key) with an optional account API key pair for key-scoped ops;
|
|
396
|
+
* signatureSecret is the HMAC key for signed-request `sig` AND HS256 webhook-JWT verification.
|
|
397
|
+
* When `vonage` is omitted, the Vonage telephony routes are not mounted.
|
|
398
|
+
*/
|
|
399
|
+
const vonageTelephonySchema = z.object({
|
|
400
|
+
/** Vonage Application ID (UUID) — the JWT-auth identity for the Voice API. */
|
|
401
|
+
applicationId: z.string().optional(),
|
|
402
|
+
/** The application's RSA private key (PEM) used to sign Voice-API JWTs. */
|
|
403
|
+
privateKey: z.string().optional(),
|
|
404
|
+
/** Account API key — used for key-scoped operations when no application credential pair is supplied. */
|
|
405
|
+
apiKey: z.string().optional(),
|
|
406
|
+
/** Account API secret — paired with apiKey. */
|
|
407
|
+
apiSecret: z.string().optional(),
|
|
408
|
+
/** The publicly reachable `wss://…/telephony/vonage/media` URL the call's connect NCCO opens. */
|
|
409
|
+
mediaPublicUrl: z.string(),
|
|
410
|
+
/** Vonage account signature secret — HMAC key for signed-request `sig` AND HS256 webhook-JWT verification. */
|
|
411
|
+
signatureSecret: z.string().optional(),
|
|
412
|
+
/** Optional event-webhook URL Vonage posts call lifecycle events to (passed on outbound createCall). */
|
|
413
|
+
eventUrl: z.string().optional(),
|
|
414
|
+
}).passthrough();
|
|
415
|
+
|
|
416
|
+
/**
|
|
417
|
+
* RingCentral telephony binding: **SIP device credentials** for the headless softphone (the
|
|
418
|
+
* `ringcentral-softphone` SIP/RTP path — the only RingCentral product that carries bidirectional call
|
|
419
|
+
* audio; the WebSocket "Call Streaming" product is receive-only). These come from a RingCentral
|
|
420
|
+
* "Other Phone" (BYO-device) registration's SIP info, resolved here from `mj.config.cjs` / env — never
|
|
421
|
+
* inlined. When `ringcentral` is omitted, the RingCentral softphone is not started.
|
|
422
|
+
*
|
|
423
|
+
* Unlike Twilio/Vonage, there is NO public HTTP webhook or media WSS: the softphone is an outbound SIP
|
|
424
|
+
* registration that receives inbound INVITEs directly over its own SIP/TLS connection. So this block has
|
|
425
|
+
* no signing secret or public URL — just the SIP device creds + codec.
|
|
426
|
+
*/
|
|
427
|
+
const ringcentralTelephonySchema = z.object({
|
|
428
|
+
/** SIP domain (e.g. `sip.ringcentral.com`). */
|
|
429
|
+
sipDomain: z.string(),
|
|
430
|
+
/** SIP outbound proxy (`host:port`, e.g. `sip10.ringcentral.com:5096`). */
|
|
431
|
+
sipOutboundProxy: z.string(),
|
|
432
|
+
/** SIP auth username (the device's phone number / extension). */
|
|
433
|
+
sipUsername: z.string(),
|
|
434
|
+
/** SIP auth password (resolved upstream — never inlined). */
|
|
435
|
+
sipPassword: z.string(),
|
|
436
|
+
/** SIP authorization id (the device's RingCentral authorization id). */
|
|
437
|
+
sipAuthorizationId: z.string(),
|
|
438
|
+
/** Codec to negotiate. Defaults to `OPUS/16000` (clean wideband PCM16 — the least-friction realtime path). */
|
|
439
|
+
codec: z.enum(['OPUS/16000', 'OPUS/48000/2', 'PCMU/8000']).optional(),
|
|
440
|
+
/** Skip TLS cert validation (sandbox/test only — never in production). */
|
|
441
|
+
ignoreTlsCertErrors: zodBooleanWithTransforms().optional().default(false),
|
|
442
|
+
}).passthrough();
|
|
443
|
+
|
|
444
|
+
/**
|
|
445
|
+
* Teams meetings bridge config (plans/realtime/bridges-and-widget/meeting-vendor-bindings-teams-slack.md M1).
|
|
446
|
+
* Holds the bot credentials + Graph webhook shared secret + ACS audio rate the MJAPI meetings ingress wires
|
|
447
|
+
* onto the Teams binding seams. Credentials resolve here from `mj.config.cjs` / env — never inlined in code.
|
|
448
|
+
* When `teams` is omitted (or disabled), the Teams meetings routes are not mounted.
|
|
449
|
+
*/
|
|
450
|
+
const teamsMeetingsSchema = z.object({
|
|
451
|
+
/** Master switch for the Teams meetings ingress. */
|
|
452
|
+
enabled: zodBooleanWithTransforms().default(false),
|
|
453
|
+
/** The bot's Azure AD application (client) id (resolved upstream; carried for diagnostics). */
|
|
454
|
+
appId: z.string().optional(),
|
|
455
|
+
/** The Azure tenant id the bot operates in. */
|
|
456
|
+
tenantId: z.string().optional(),
|
|
457
|
+
/** A pre-resolved OAuth bearer / application token for the bot's Graph calls (resolved upstream — never inline). */
|
|
458
|
+
botAccessToken: z.string().optional(),
|
|
459
|
+
/** The shared secret set as `clientState` on the Graph subscription; gates the change-notification webhook. */
|
|
460
|
+
notificationClientState: z.string().optional(),
|
|
461
|
+
/** The ACS application-hosted-media PCM sample rate (Hz) the audio plane negotiates. Defaults to 16000. */
|
|
462
|
+
acsSampleRate: z.coerce.number().optional().default(16000),
|
|
463
|
+
/** The realtime model's PCM16 sample rate (Hz) the binding resamples to/from. Defaults to 16000. */
|
|
464
|
+
modelSampleRate: z.coerce.number().optional().default(16000),
|
|
465
|
+
}).passthrough();
|
|
466
|
+
|
|
467
|
+
const telephonySchema = z.object({
|
|
468
|
+
/** Master switch. When false (or when no vendor block is present), telephony routes are not mounted. */
|
|
469
|
+
enabled: zodBooleanWithTransforms().default(false),
|
|
470
|
+
/** Twilio Programmable Voice + Media Streams binding. */
|
|
471
|
+
twilio: twilioTelephonySchema.optional(),
|
|
472
|
+
/** Vonage Voice + WebSocket-media binding. */
|
|
473
|
+
vonage: vonageTelephonySchema.optional(),
|
|
474
|
+
/** RingCentral Call Control + media-stream binding. */
|
|
475
|
+
ringcentral: ringcentralTelephonySchema.optional(),
|
|
476
|
+
/** Microsoft Teams meetings (Graph cloud-communications + ACS application-hosted media) binding. */
|
|
477
|
+
teams: teamsMeetingsSchema.optional(),
|
|
478
|
+
}).passthrough();
|
|
479
|
+
|
|
333
480
|
const configInfoSchema = z.object({
|
|
334
481
|
userHandling: userHandlingInfoSchema,
|
|
335
482
|
magicLink: magicLinkSchema.optional().default({}),
|
|
483
|
+
widget: widgetSchema.optional().default({}),
|
|
484
|
+
telephony: telephonySchema.optional().default({}),
|
|
336
485
|
databaseSettings: databaseSettingsInfoSchema,
|
|
337
486
|
viewingSystem: viewingSystemInfoSchema.optional(),
|
|
338
487
|
restApiOptions: restApiOptionsSchema.optional().default({}),
|
|
339
|
-
askSkip: askSkipInfoSchema.optional(),
|
|
340
488
|
sqlLogging: sqlLoggingSchema.optional(),
|
|
341
489
|
authProviders: z.array(authProviderSchema).optional(),
|
|
342
490
|
componentRegistries: z.array(componentRegistrySchema).optional(),
|
|
@@ -384,10 +532,15 @@ const configInfoSchema = z.object({
|
|
|
384
532
|
|
|
385
533
|
export type UserHandlingInfo = z.infer<typeof userHandlingInfoSchema>;
|
|
386
534
|
export type MagicLinkConfig = z.infer<typeof magicLinkSchema>;
|
|
535
|
+
export type WidgetConfig = z.infer<typeof widgetSchema>;
|
|
536
|
+
export type TelephonyConfig = z.infer<typeof telephonySchema>;
|
|
537
|
+
export type TwilioTelephonyConfig = z.infer<typeof twilioTelephonySchema>;
|
|
538
|
+
export type VonageTelephonyConfig = z.infer<typeof vonageTelephonySchema>;
|
|
539
|
+
export type RingCentralTelephonyConfig = z.infer<typeof ringcentralTelephonySchema>;
|
|
540
|
+
export type TeamsMeetingsConfig = z.infer<typeof teamsMeetingsSchema>;
|
|
387
541
|
export type DatabaseSettingsInfo = z.infer<typeof databaseSettingsInfoSchema>;
|
|
388
542
|
export type ViewingSystemSettingsInfo = z.infer<typeof viewingSystemInfoSchema>;
|
|
389
543
|
export type RESTApiOptions = z.infer<typeof restApiOptionsSchema>;
|
|
390
|
-
export type AskSkipInfo = z.infer<typeof askSkipInfoSchema>;
|
|
391
544
|
export type SqlLoggingOptions = z.infer<typeof sqlLoggingOptionsSchema>;
|
|
392
545
|
export type SqlLoggingInfo = z.infer<typeof sqlLoggingSchema>;
|
|
393
546
|
export type AuthProviderConfig = z.infer<typeof authProviderSchema>;
|
|
@@ -477,25 +630,6 @@ export const DEFAULT_SERVER_CONFIG: Partial<ConfigInfo> = {
|
|
|
477
630
|
enabled: false,
|
|
478
631
|
},
|
|
479
632
|
|
|
480
|
-
// Ask Skip configuration (environment-driven)
|
|
481
|
-
askSkip: {
|
|
482
|
-
url: process.env.ASK_SKIP_URL,
|
|
483
|
-
chatURL: process.env.ASK_SKIP_CHAT_URL,
|
|
484
|
-
learningCycleURL: process.env.ASK_SKIP_LEARNING_URL,
|
|
485
|
-
learningCycleIntervalInMinutes: process.env.ASK_SKIP_LEARNING_CYCLE_INTERVAL_IN_MINUTES
|
|
486
|
-
? parseInt(process.env.ASK_SKIP_LEARNING_CYCLE_INTERVAL_IN_MINUTES, 10)
|
|
487
|
-
: undefined,
|
|
488
|
-
learningCycleEnabled: process.env.ASK_SKIP_RUN_LEARNING_CYCLES === 'true',
|
|
489
|
-
learningCycleRunUponStartup: process.env.ASK_SKIP_RUN_LEARNING_CYCLES_UPON_STARTUP === 'true',
|
|
490
|
-
orgID: process.env.ASK_SKIP_ORGANIZATION_ID,
|
|
491
|
-
apiKey: process.env.ASK_SKIP_API_KEY,
|
|
492
|
-
organizationInfo: process.env.ASK_SKIP_ORGANIZATION_INFO,
|
|
493
|
-
entitiesToSend: {
|
|
494
|
-
excludeSchemas: [],
|
|
495
|
-
includeEntitiesFromExcludedSchemas: [],
|
|
496
|
-
},
|
|
497
|
-
},
|
|
498
|
-
|
|
499
633
|
// SQL logging defaults
|
|
500
634
|
sqlLogging: {
|
|
501
635
|
enabled: true,
|
package/src/context.ts
CHANGED
|
@@ -15,7 +15,7 @@ import { GetReadOnlyDataSource, GetReadWriteDataSource } from './util.js';
|
|
|
15
15
|
import { v4 as uuidv4 } from 'uuid';
|
|
16
16
|
import e from 'express';
|
|
17
17
|
import type { RequestHandler, Request, Response, NextFunction } from 'express';
|
|
18
|
-
import { DatabaseProviderBase, UserInfo, type MagicLinkScope } from '@memberjunction/core';
|
|
18
|
+
import { DatabaseProviderBase, UserInfo, type MagicLinkScope, type ReturningVisitorContext, type WidgetGuestContext } from '@memberjunction/core';
|
|
19
19
|
import { SQLServerDataProvider, SQLServerProviderConfigData, UserCache } from '@memberjunction/sqlserver-dataprovider';
|
|
20
20
|
import { Metadata } from '@memberjunction/core';
|
|
21
21
|
import { UUIDsEqual } from '@memberjunction/global';
|
|
@@ -242,8 +242,17 @@ function buildMagicLinkSessionUser(userRecord: UserInfo, payload: jwt.JwtPayload
|
|
|
242
242
|
}
|
|
243
243
|
}
|
|
244
244
|
|
|
245
|
-
//
|
|
246
|
-
|
|
245
|
+
// Returning-visitor context (RV1/RV2/RV4): carried on a widget guest token so the voice path
|
|
246
|
+
// (server-created conversation) stamps the same returning-visitor anchor + resolved identity.
|
|
247
|
+
const visitorContext = extractReturningVisitorContext(payload);
|
|
248
|
+
|
|
249
|
+
// Widget-instance identity (mj_widget_id): present on every public web-widget guest token. The
|
|
250
|
+
// privileged agent-dispatch path reads it to resolve the AUTHORITATIVE pinned agent for the guest
|
|
251
|
+
// (so a guest can never run an arbitrary agent under the elevated server principal).
|
|
252
|
+
const widgetGuestContext = extractWidgetGuestContext(payload);
|
|
253
|
+
|
|
254
|
+
// Named session with no resource scope and no widget/visitor context → no per-request state needed.
|
|
255
|
+
if (!isAnon && !scope && !visitorContext && !widgetGuestContext) {
|
|
247
256
|
return userRecord;
|
|
248
257
|
}
|
|
249
258
|
|
|
@@ -255,6 +264,12 @@ function buildMagicLinkSessionUser(userRecord: UserInfo, payload: jwt.JwtPayload
|
|
|
255
264
|
if (scope) {
|
|
256
265
|
sessionUser.MagicLinkScope = scope;
|
|
257
266
|
}
|
|
267
|
+
if (visitorContext) {
|
|
268
|
+
sessionUser.ReturningVisitorContext = visitorContext;
|
|
269
|
+
}
|
|
270
|
+
if (widgetGuestContext) {
|
|
271
|
+
sessionUser.WidgetGuestContext = widgetGuestContext;
|
|
272
|
+
}
|
|
258
273
|
if (isAnon) {
|
|
259
274
|
// Mark the session so the CurrentUser field resolver serves these synthesized roles
|
|
260
275
|
// (the shared Anonymous principal holds none in the DB). See UserInfo.IsMagicLinkAnonymous.
|
|
@@ -263,6 +278,38 @@ function buildMagicLinkSessionUser(userRecord: UserInfo, payload: jwt.JwtPayload
|
|
|
263
278
|
return sessionUser;
|
|
264
279
|
}
|
|
265
280
|
|
|
281
|
+
/**
|
|
282
|
+
* Extracts the returning-visitor context (RV1/RV2/RV4) from a widget guest token's claims, or
|
|
283
|
+
* undefined when the token carries no VisitorKey (the default, remembering-off case). Used so the
|
|
284
|
+
* voice path can stamp the conversation with the same anchor/identity the text path stamps client-side.
|
|
285
|
+
*/
|
|
286
|
+
function extractReturningVisitorContext(payload: jwt.JwtPayload): ReturningVisitorContext | undefined {
|
|
287
|
+
const visitorKey = payload['mj_visitor_key'];
|
|
288
|
+
if (typeof visitorKey !== 'string' || !visitorKey) {
|
|
289
|
+
return undefined;
|
|
290
|
+
}
|
|
291
|
+
const str = (v: unknown): string | undefined => (typeof v === 'string' && v ? v : undefined);
|
|
292
|
+
return {
|
|
293
|
+
VisitorKey: visitorKey,
|
|
294
|
+
LastConversationID: str(payload['mj_last_conversation_id']),
|
|
295
|
+
LinkedEntityID: str(payload['mj_linked_entity_id']),
|
|
296
|
+
LinkedRecordID: str(payload['mj_linked_record_id']),
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* Extracts the widget-instance identity (`mj_widget_id`) from a public web-widget guest token, or
|
|
302
|
+
* undefined when the token carries none (every non-widget session). Carried so the privileged
|
|
303
|
+
* agent-dispatch path can resolve the authoritative pinned agent for the guest from the trusted token.
|
|
304
|
+
*/
|
|
305
|
+
function extractWidgetGuestContext(payload: jwt.JwtPayload): WidgetGuestContext | undefined {
|
|
306
|
+
const widgetId = payload['mj_widget_id'];
|
|
307
|
+
if (typeof widgetId !== 'string' || !widgetId) {
|
|
308
|
+
return undefined;
|
|
309
|
+
}
|
|
310
|
+
return { WidgetID: widgetId };
|
|
311
|
+
}
|
|
312
|
+
|
|
266
313
|
const verifyAsync = async (issuer: string, token: string): Promise<jwt.JwtPayload> =>
|
|
267
314
|
new Promise((resolve, reject) => {
|
|
268
315
|
const options = getValidationOptions(issuer);
|