@stackframe/react 2.8.69 → 2.8.70
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/esm/lib/stack-app/apps/implementations/client-app-impl.js +170 -22
- package/dist/esm/lib/stack-app/apps/implementations/client-app-impl.js.map +1 -1
- package/dist/esm/lib/stack-app/apps/implementations/common.js +1 -1
- package/dist/esm/lib/stack-app/apps/implementations/common.js.map +1 -1
- package/dist/esm/lib/stack-app/apps/implementations/event-tracker.js +4 -12
- package/dist/esm/lib/stack-app/apps/implementations/event-tracker.js.map +1 -1
- package/dist/esm/lib/stack-app/apps/implementations/server-app-impl.js +108 -10
- package/dist/esm/lib/stack-app/apps/implementations/server-app-impl.js.map +1 -1
- package/dist/esm/lib/stack-app/apps/implementations/session-replay.js +37 -24
- package/dist/esm/lib/stack-app/apps/implementations/session-replay.js.map +1 -1
- package/dist/esm/lib/stack-app/users/index.js.map +1 -1
- package/dist/index.d.mts +73 -2
- package/dist/index.d.ts +73 -2
- package/dist/lib/stack-app/apps/implementations/client-app-impl.js +170 -22
- package/dist/lib/stack-app/apps/implementations/client-app-impl.js.map +1 -1
- package/dist/lib/stack-app/apps/implementations/common.js +1 -1
- package/dist/lib/stack-app/apps/implementations/common.js.map +1 -1
- package/dist/lib/stack-app/apps/implementations/event-tracker.js +4 -12
- package/dist/lib/stack-app/apps/implementations/event-tracker.js.map +1 -1
- package/dist/lib/stack-app/apps/implementations/server-app-impl.js +108 -10
- package/dist/lib/stack-app/apps/implementations/server-app-impl.js.map +1 -1
- package/dist/lib/stack-app/apps/implementations/session-replay.js +37 -24
- package/dist/lib/stack-app/apps/implementations/session-replay.js.map +1 -1
- package/dist/lib/stack-app/connected-accounts/index.js.map +1 -1
- package/dist/lib/stack-app/users/index.js.map +1 -1
- package/package.json +3 -3
|
@@ -64,6 +64,7 @@ var __StackClientAppImplIncomplete = class __StackClientAppImplIncomplete {
|
|
|
64
64
|
this._currentUserTeamsCache = createCacheBySession(async (session) => {
|
|
65
65
|
return await this._interface.listCurrentUserTeams(session);
|
|
66
66
|
});
|
|
67
|
+
/** @deprecated Used by legacy getConnectedAccount(providerId) — uses old per-provider access token endpoint */
|
|
67
68
|
this._currentUserOAuthConnectionAccessTokensCache = createCacheBySession(
|
|
68
69
|
async (session, [providerId, scope]) => {
|
|
69
70
|
try {
|
|
@@ -77,6 +78,7 @@ var __StackClientAppImplIncomplete = class __StackClientAppImplIncomplete {
|
|
|
77
78
|
return null;
|
|
78
79
|
}
|
|
79
80
|
);
|
|
81
|
+
/** @deprecated Used by legacy getConnectedAccount(providerId) — combines token check + redirect */
|
|
80
82
|
this._currentUserOAuthConnectionCache = createCacheBySession(
|
|
81
83
|
async (session, [providerId, scope, redirect]) => {
|
|
82
84
|
return await this._getUserOAuthConnectionCacheFn({
|
|
@@ -90,6 +92,49 @@ var __StackClientAppImplIncomplete = class __StackClientAppImplIncomplete {
|
|
|
90
92
|
});
|
|
91
93
|
}
|
|
92
94
|
);
|
|
95
|
+
this._currentUserConnectedAccountsCache = createCacheBySession(
|
|
96
|
+
async (session) => {
|
|
97
|
+
const result = await this._interface.listConnectedAccounts(session);
|
|
98
|
+
return result.items.map((item) => this._createOAuthConnectionFromCrudItem(item, session));
|
|
99
|
+
}
|
|
100
|
+
);
|
|
101
|
+
this._currentUserOAuthConnectionAccessTokensByAccountCache = createCacheBySession(
|
|
102
|
+
async (session, [providerId, providerAccountId, scope]) => {
|
|
103
|
+
try {
|
|
104
|
+
const result = await this._interface.createProviderAccessTokenByAccount(providerId, providerAccountId, scope, session);
|
|
105
|
+
return { accessToken: result.access_token };
|
|
106
|
+
} catch (err) {
|
|
107
|
+
if (KnownErrors.OAuthConnectionDoesNotHaveRequiredScope.isInstance(err) || KnownErrors.OAuthConnectionNotConnectedToUser.isInstance(err)) {
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
throw err;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
);
|
|
114
|
+
this._currentUserValidConnectedAccountForProviderCache = createCacheBySession(
|
|
115
|
+
async (session, [provider, scopeString]) => {
|
|
116
|
+
const connectedAccounts = Result.orThrow(await this._currentUserConnectedAccountsCache.getOrWait([session], "write-only"));
|
|
117
|
+
const matchingAccounts = connectedAccounts.filter((a) => a.provider === provider);
|
|
118
|
+
const scopes = scopeString ? scopeString.split(" ") : void 0;
|
|
119
|
+
for (const account of matchingAccounts) {
|
|
120
|
+
const tokenResult = await account.getAccessToken({ scopes });
|
|
121
|
+
if (tokenResult.status === "ok") {
|
|
122
|
+
return account;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
await addNewOAuthProviderOrScope(
|
|
126
|
+
this._interface,
|
|
127
|
+
{
|
|
128
|
+
provider,
|
|
129
|
+
redirectUrl: this.urls.oauthCallback,
|
|
130
|
+
errorRedirectUrl: this.urls.error,
|
|
131
|
+
providerScope: mergeScopeStrings(scopeString, (this._oauthScopesOnSignIn[provider] ?? []).join(" "))
|
|
132
|
+
},
|
|
133
|
+
session
|
|
134
|
+
);
|
|
135
|
+
return await neverResolve();
|
|
136
|
+
}
|
|
137
|
+
);
|
|
93
138
|
this._teamMemberProfilesCache = createCacheBySession(
|
|
94
139
|
async (session, [teamId]) => {
|
|
95
140
|
return await this._interface.listTeamMemberProfiles({ teamId }, session);
|
|
@@ -260,14 +305,14 @@ var __StackClientAppImplIncomplete = class __StackClientAppImplIncomplete {
|
|
|
260
305
|
this._initUniqueIdentifier();
|
|
261
306
|
}
|
|
262
307
|
this._analyticsOptions = resolvedOptions.analytics;
|
|
308
|
+
const getAnalyticsAccessToken = async () => {
|
|
309
|
+
this._ensurePersistentTokenStore();
|
|
310
|
+
return await (await this.getUser({ or: "anonymous" })).getAccessToken();
|
|
311
|
+
};
|
|
263
312
|
if (isBrowserLike() && this._analyticsOptions?.replays?.enabled === true) {
|
|
264
313
|
this._sessionRecorder = new SessionRecorder({
|
|
265
314
|
projectId: this.projectId,
|
|
266
|
-
getAccessToken:
|
|
267
|
-
const session = await this._getSession();
|
|
268
|
-
const tokens = await session.getOrFetchLikelyValidTokens(2e4, 75e3);
|
|
269
|
-
return tokens?.accessToken.token ?? null;
|
|
270
|
-
},
|
|
315
|
+
getAccessToken: getAnalyticsAccessToken,
|
|
271
316
|
sendBatch: async (body, opts) => {
|
|
272
317
|
return await this._interface.sendSessionReplayBatch(body, await this._getSession(), opts);
|
|
273
318
|
}
|
|
@@ -277,11 +322,7 @@ var __StackClientAppImplIncomplete = class __StackClientAppImplIncomplete {
|
|
|
277
322
|
if (isBrowserLike() && this.projectId === "internal") {
|
|
278
323
|
this._eventTracker = new EventTracker({
|
|
279
324
|
projectId: this.projectId,
|
|
280
|
-
getAccessToken:
|
|
281
|
-
const session = await this._getSession();
|
|
282
|
-
const tokens = await session.getOrFetchLikelyValidTokens(2e4, 75e3);
|
|
283
|
-
return tokens?.accessToken.token ?? null;
|
|
284
|
-
},
|
|
325
|
+
getAccessToken: getAnalyticsAccessToken,
|
|
285
326
|
sendBatch: async (body, opts) => {
|
|
286
327
|
return await this._interface.sendAnalyticsEventBatch(body, await this._getSession(), opts);
|
|
287
328
|
}
|
|
@@ -297,6 +338,7 @@ var __StackClientAppImplIncomplete = class __StackClientAppImplIncomplete {
|
|
|
297
338
|
return await createPlaceholderCookieHelper();
|
|
298
339
|
}
|
|
299
340
|
}
|
|
341
|
+
/** @deprecated Used by legacy getConnectedAccount(providerId) — combines user check + token check + redirect into one cache */
|
|
300
342
|
async _getUserOAuthConnectionCacheFn(options) {
|
|
301
343
|
const user = await options.getUser();
|
|
302
344
|
let hasConnection = true;
|
|
@@ -329,24 +371,58 @@ var __StackClientAppImplIncomplete = class __StackClientAppImplIncomplete {
|
|
|
329
371
|
} else if (!hasConnection) {
|
|
330
372
|
return null;
|
|
331
373
|
}
|
|
374
|
+
const matchingProvider = user.oauth_providers.find((p) => p.id === options.providerId);
|
|
375
|
+
const providerAccountId = matchingProvider?.account_id ?? "";
|
|
332
376
|
return {
|
|
333
377
|
id: options.providerId,
|
|
378
|
+
// deprecated, for backward compat
|
|
379
|
+
provider: options.providerId,
|
|
380
|
+
providerAccountId,
|
|
334
381
|
async getAccessToken() {
|
|
335
382
|
const result = await options.getOrWaitOAuthToken();
|
|
336
383
|
if (!result) {
|
|
337
|
-
throw new StackAssertionError(
|
|
384
|
+
throw new StackAssertionError(`Failed to retrieve an access token for this connected account (provider: ${options.providerId}). This usually means the OAuth refresh token has been revoked or expired. The user needs to re-authorize by calling \`linkConnectedAccount\` or using \`getOrLinkConnectedAccount\`.`);
|
|
338
385
|
}
|
|
339
386
|
return result;
|
|
340
387
|
},
|
|
341
388
|
useAccessToken() {
|
|
342
389
|
const result = options.useOAuthToken();
|
|
343
390
|
if (!result) {
|
|
344
|
-
throw new StackAssertionError(
|
|
391
|
+
throw new StackAssertionError(`Failed to retrieve an access token for this connected account (provider: ${options.providerId}). This usually means the OAuth refresh token has been revoked or expired. The user needs to re-authorize by calling \`linkConnectedAccount\` or using \`getOrLinkConnectedAccount\`.`);
|
|
345
392
|
}
|
|
346
393
|
return result;
|
|
347
394
|
}
|
|
348
395
|
};
|
|
349
396
|
}
|
|
397
|
+
_createOAuthConnectionFromCrudItem(item, session) {
|
|
398
|
+
const app = this;
|
|
399
|
+
const providerId = item.provider;
|
|
400
|
+
const providerAccountId = item.provider_account_id;
|
|
401
|
+
return {
|
|
402
|
+
id: providerId,
|
|
403
|
+
// deprecated, for backward compat
|
|
404
|
+
provider: providerId,
|
|
405
|
+
providerAccountId,
|
|
406
|
+
async getAccessToken(options) {
|
|
407
|
+
const scopeString = options?.scopes?.join(" ") ?? "";
|
|
408
|
+
const result = Result.orThrow(await app._currentUserOAuthConnectionAccessTokensByAccountCache.getOrWait([session, providerId, providerAccountId, scopeString], "write-only"));
|
|
409
|
+
if (!result) {
|
|
410
|
+
const scopeDetail = scopeString ? `The requested scopes [${scopeString}] are not available on the existing token.` : "The OAuth refresh token has likely been revoked or expired.";
|
|
411
|
+
return Result.error(new KnownErrors.OAuthAccessTokenNotAvailable(providerId, `${scopeDetail} The user needs to re-authorize by calling \`linkConnectedAccount\` or using \`getOrLinkConnectedAccount\`.`));
|
|
412
|
+
}
|
|
413
|
+
return Result.ok(result);
|
|
414
|
+
},
|
|
415
|
+
useAccessToken(options) {
|
|
416
|
+
const scopeString = options?.scopes?.join(" ") ?? "";
|
|
417
|
+
const result = useAsyncCache(app._currentUserOAuthConnectionAccessTokensByAccountCache, [session, providerId, providerAccountId, scopeString], "connection.useAccessToken()");
|
|
418
|
+
if (!result) {
|
|
419
|
+
const scopeDetail = scopeString ? `The requested scopes [${scopeString}] are not available on the existing token.` : "The OAuth refresh token has likely been revoked or expired.";
|
|
420
|
+
return Result.error(new KnownErrors.OAuthAccessTokenNotAvailable(providerId, `${scopeDetail} The user needs to re-authorize by calling \`linkConnectedAccount\` or using \`getOrLinkConnectedAccount\`.`));
|
|
421
|
+
}
|
|
422
|
+
return Result.ok(result);
|
|
423
|
+
}
|
|
424
|
+
};
|
|
425
|
+
}
|
|
350
426
|
_initUniqueIdentifier() {
|
|
351
427
|
if (!this._uniqueIdentifier) {
|
|
352
428
|
throw new StackAssertionError("Unique identifier not initialized");
|
|
@@ -978,7 +1054,10 @@ var __StackClientAppImplIncomplete = class __StackClientAppImplIncomplete {
|
|
|
978
1054
|
},
|
|
979
1055
|
session
|
|
980
1056
|
);
|
|
981
|
-
await
|
|
1057
|
+
await Promise.all([
|
|
1058
|
+
app._currentUserOAuthProvidersCache.refresh([session]),
|
|
1059
|
+
app._currentUserConnectedAccountsCache.refresh([session])
|
|
1060
|
+
]);
|
|
982
1061
|
return Result.ok(void 0);
|
|
983
1062
|
} catch (error) {
|
|
984
1063
|
if (KnownErrors.OAuthProviderAccountIdAlreadyUsedForSignIn.isInstance(error)) {
|
|
@@ -989,7 +1068,10 @@ var __StackClientAppImplIncomplete = class __StackClientAppImplIncomplete {
|
|
|
989
1068
|
},
|
|
990
1069
|
async delete() {
|
|
991
1070
|
await app._interface.deleteOAuthProvider(crud.user_id, crud.id, session);
|
|
992
|
-
await
|
|
1071
|
+
await Promise.all([
|
|
1072
|
+
app._currentUserOAuthProvidersCache.refresh([session]),
|
|
1073
|
+
app._currentUserConnectedAccountsCache.refresh([session])
|
|
1074
|
+
]);
|
|
993
1075
|
}
|
|
994
1076
|
};
|
|
995
1077
|
}
|
|
@@ -1153,13 +1235,36 @@ var __StackClientAppImplIncomplete = class __StackClientAppImplIncomplete {
|
|
|
1153
1235
|
}
|
|
1154
1236
|
_createUserExtraFromCurrent(crud, session) {
|
|
1155
1237
|
const app = this;
|
|
1156
|
-
async function getConnectedAccount(
|
|
1157
|
-
const scopeString = options?.scopes?.join(" ");
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1238
|
+
async function getConnectedAccount(idOrAccount, options) {
|
|
1239
|
+
const scopeString = options?.scopes?.join(" ") ?? "";
|
|
1240
|
+
if (typeof idOrAccount === "object" && "provider" in idOrAccount && "providerAccountId" in idOrAccount) {
|
|
1241
|
+
const { provider, providerAccountId } = idOrAccount;
|
|
1242
|
+
const connectedAccounts = Result.orThrow(await app._currentUserConnectedAccountsCache.getOrWait([session], "write-only"));
|
|
1243
|
+
const found = connectedAccounts.find(
|
|
1244
|
+
(a) => a.provider === provider && a.providerAccountId === providerAccountId
|
|
1245
|
+
);
|
|
1246
|
+
if (!found) {
|
|
1247
|
+
return null;
|
|
1248
|
+
}
|
|
1249
|
+
return found;
|
|
1250
|
+
}
|
|
1251
|
+
return Result.orThrow(await app._currentUserOAuthConnectionCache.getOrWait([session, idOrAccount, scopeString, options?.or === "redirect"], "write-only"));
|
|
1252
|
+
}
|
|
1253
|
+
function useConnectedAccount(idOrAccount, options) {
|
|
1254
|
+
const scopeString = options?.scopes?.join(" ") ?? "";
|
|
1255
|
+
if (typeof idOrAccount === "object" && "provider" in idOrAccount && "providerAccountId" in idOrAccount) {
|
|
1256
|
+
const { provider, providerAccountId } = idOrAccount;
|
|
1257
|
+
const connectedAccounts = useAsyncCache(
|
|
1258
|
+
app._currentUserConnectedAccountsCache,
|
|
1259
|
+
[session],
|
|
1260
|
+
"user.useConnectedAccount()"
|
|
1261
|
+
);
|
|
1262
|
+
const found = connectedAccounts.find(
|
|
1263
|
+
(a) => a.provider === provider && a.providerAccountId === providerAccountId
|
|
1264
|
+
);
|
|
1265
|
+
return found ?? null;
|
|
1266
|
+
}
|
|
1267
|
+
return useAsyncCache(app._currentUserOAuthConnectionCache, [session, idOrAccount, scopeString, options?.or === "redirect"], "user.useConnectedAccount()");
|
|
1163
1268
|
}
|
|
1164
1269
|
return {
|
|
1165
1270
|
async getActiveSessions() {
|
|
@@ -1181,6 +1286,42 @@ var __StackClientAppImplIncomplete = class __StackClientAppImplIncomplete {
|
|
|
1181
1286
|
getConnectedAccount,
|
|
1182
1287
|
useConnectedAccount,
|
|
1183
1288
|
// THIS_LINE_PLATFORM react-like
|
|
1289
|
+
async listConnectedAccounts() {
|
|
1290
|
+
return Result.orThrow(await app._currentUserConnectedAccountsCache.getOrWait([session], "write-only"));
|
|
1291
|
+
},
|
|
1292
|
+
useConnectedAccounts() {
|
|
1293
|
+
return useAsyncCache(app._currentUserConnectedAccountsCache, [session], "user.useConnectedAccounts()");
|
|
1294
|
+
},
|
|
1295
|
+
async linkConnectedAccount(provider, options) {
|
|
1296
|
+
const scopeString = options?.scopes?.join(" ") ?? "";
|
|
1297
|
+
await addNewOAuthProviderOrScope(
|
|
1298
|
+
app._interface,
|
|
1299
|
+
{
|
|
1300
|
+
provider,
|
|
1301
|
+
redirectUrl: app.urls.oauthCallback,
|
|
1302
|
+
errorRedirectUrl: app.urls.error,
|
|
1303
|
+
providerScope: mergeScopeStrings(scopeString, (app._oauthScopesOnSignIn[provider] ?? []).join(" "))
|
|
1304
|
+
},
|
|
1305
|
+
session
|
|
1306
|
+
);
|
|
1307
|
+
await neverResolve();
|
|
1308
|
+
},
|
|
1309
|
+
async getOrLinkConnectedAccount(provider, options) {
|
|
1310
|
+
const connectedAccounts = Result.orThrow(await app._currentUserConnectedAccountsCache.getOrWait([session], "write-only"));
|
|
1311
|
+
const matchingAccounts = connectedAccounts.filter((a) => a.provider === provider);
|
|
1312
|
+
for (const account of matchingAccounts) {
|
|
1313
|
+
const tokenResult = await account.getAccessToken({ scopes: options?.scopes });
|
|
1314
|
+
if (tokenResult.status === "ok") {
|
|
1315
|
+
return account;
|
|
1316
|
+
}
|
|
1317
|
+
}
|
|
1318
|
+
await this.linkConnectedAccount(provider, options);
|
|
1319
|
+
return await neverResolve();
|
|
1320
|
+
},
|
|
1321
|
+
useOrLinkConnectedAccount(provider, options) {
|
|
1322
|
+
const scopeString = options?.scopes?.join(" ") ?? "";
|
|
1323
|
+
return useAsyncCache(app._currentUserValidConnectedAccountForProviderCache, [session, provider, scopeString], "user.useOrLinkConnectedAccount()");
|
|
1324
|
+
},
|
|
1184
1325
|
async getTeam(teamId) {
|
|
1185
1326
|
const teams = await this.listTeams();
|
|
1186
1327
|
return teams.find((t) => t.id === teamId) ?? null;
|
|
@@ -1833,6 +1974,8 @@ var __StackClientAppImplIncomplete = class __StackClientAppImplIncomplete {
|
|
|
1833
1974
|
case void 0:
|
|
1834
1975
|
case "anonymous-if-exists[deprecated]":
|
|
1835
1976
|
case "return-null": {
|
|
1977
|
+
crud = null;
|
|
1978
|
+
break;
|
|
1836
1979
|
}
|
|
1837
1980
|
}
|
|
1838
1981
|
}
|
|
@@ -2250,6 +2393,8 @@ ${url}`);
|
|
|
2250
2393
|
return false;
|
|
2251
2394
|
}
|
|
2252
2395
|
async _signOut(session, options) {
|
|
2396
|
+
this._eventTracker?.clearBuffer();
|
|
2397
|
+
this._sessionRecorder?.clearBuffer();
|
|
2253
2398
|
await storeLock.withWriteLock(async () => {
|
|
2254
2399
|
await this._interface.signOut(session);
|
|
2255
2400
|
if (options?.redirectUrl) {
|
|
@@ -2355,7 +2500,10 @@ ${url}`);
|
|
|
2355
2500
|
await this._refreshSession(session);
|
|
2356
2501
|
}
|
|
2357
2502
|
async _refreshSession(session) {
|
|
2358
|
-
await
|
|
2503
|
+
await Promise.all([
|
|
2504
|
+
this._currentUserCache.refresh([session]),
|
|
2505
|
+
this._currentUserConnectedAccountsCache.refresh([session])
|
|
2506
|
+
]);
|
|
2359
2507
|
session.suggestAccessTokenExpired();
|
|
2360
2508
|
}
|
|
2361
2509
|
async _refreshUsers() {
|