@isomorph.ai/app-sdk 1.5.2 → 1.5.3
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/README.md +7 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +20 -9
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -88,3 +88,10 @@ the host mints a fresh identity cookie from its sign-in session and redirects
|
|
|
88
88
|
to the identity GET. When that refresh does not end in a 200 with the user —
|
|
89
89
|
the session is gone, the network failed — the original 401 is surfaced
|
|
90
90
|
without a resend, and the page's sign-in handling takes over.
|
|
91
|
+
|
|
92
|
+
On a separate API host the identity GET itself (`identity.current()`, and the
|
|
93
|
+
background refresh every 60 s that drives `identity.onChange`) is renewed the
|
|
94
|
+
same way: its refresh ends at the identity GET, so that answer is the identity
|
|
95
|
+
and no second GET is sent. When the refresh fails, `identity.current()`
|
|
96
|
+
answers `null` and listeners hear `null`, as before. On the page's own origin
|
|
97
|
+
the identity GET is never renewed (it is the renewal). (1.5.3)
|
package/dist/index.d.ts
CHANGED
|
@@ -250,7 +250,7 @@ interface Query {
|
|
|
250
250
|
}
|
|
251
251
|
interface IdentityRenewal {
|
|
252
252
|
renewals(): number;
|
|
253
|
-
renew(sentAt: number): Promise<void |
|
|
253
|
+
renew(sentAt: number): Promise<void | false | IsomorphUser>;
|
|
254
254
|
}
|
|
255
255
|
declare class Transport {
|
|
256
256
|
private readonly onUnauthorized?;
|
package/dist/index.js
CHANGED
|
@@ -133,9 +133,11 @@ const CONTEXT_REFRESH_PATH = `/__harbour/context/refresh?return_to=${encodeURICo
|
|
|
133
133
|
// database: the handler never ran, so the retry is the first and only write.
|
|
134
134
|
// The body is a JSON string, FormData or Blob — all replayable as-is.
|
|
135
135
|
//
|
|
136
|
-
// The identity GET itself is never renewed
|
|
137
|
-
// the runner's identity is a static token no GET
|
|
138
|
-
// surfaces at once.
|
|
136
|
+
// The identity GET itself is never renewed on the page-relative path (it IS
|
|
137
|
+
// the renewal), and under Node the runner's identity is a static token no GET
|
|
138
|
+
// can renew, so a refusal there surfaces at once. On a separate API host the
|
|
139
|
+
// renewal is the context refresh instead, so the identity GET is renewed too
|
|
140
|
+
// (Transport.json, ADR 0027).
|
|
139
141
|
function renewableOnUnauthorized(path) {
|
|
140
142
|
if (typeof location === 'undefined')
|
|
141
143
|
return false;
|
|
@@ -163,7 +165,7 @@ class Transport {
|
|
|
163
165
|
this.identity = options.identity;
|
|
164
166
|
}
|
|
165
167
|
json(path, init = {}) {
|
|
166
|
-
return this.request(path, init, renewableOnUnauthorized(path));
|
|
168
|
+
return this.request(path, init, renewableOnUnauthorized(path) || (this.separateApiHost && path === IDENTITY_PATH));
|
|
167
169
|
}
|
|
168
170
|
async request(path, init, renewOnUnauthorized) {
|
|
169
171
|
const sentAt = this.renewal?.renewals() ?? 0;
|
|
@@ -184,6 +186,10 @@ class Transport {
|
|
|
184
186
|
// exception is an API-host refresh that failed: the original 401
|
|
185
187
|
// surfaces, and the page's shim takes over with its login hop.
|
|
186
188
|
const renewed = await this.renewal.renew(sentAt).catch(() => undefined);
|
|
189
|
+
// The API host's refresh is followed to the identity GET, so its
|
|
190
|
+
// answer IS the identity: the GET is not sent a second time.
|
|
191
|
+
if (renewed && path === IDENTITY_PATH)
|
|
192
|
+
return renewed;
|
|
187
193
|
if (renewed !== false)
|
|
188
194
|
return this.request(path, init, false);
|
|
189
195
|
}
|
|
@@ -356,7 +362,10 @@ export function createClient(options = {}) {
|
|
|
356
362
|
refreshesStarted += 1;
|
|
357
363
|
refreshInFlight = transport.json(IDENTITY_PATH)
|
|
358
364
|
.then((user) => {
|
|
359
|
-
|
|
365
|
+
// A user answered by the API host's context refresh was announced by
|
|
366
|
+
// that refresh already (it is the same object): once is enough.
|
|
367
|
+
if (user === null || user !== currentUser)
|
|
368
|
+
notifyIdentity(user, true);
|
|
360
369
|
return user;
|
|
361
370
|
})
|
|
362
371
|
.catch((error) => {
|
|
@@ -384,9 +393,11 @@ export function createClient(options = {}) {
|
|
|
384
393
|
};
|
|
385
394
|
// On a separate API host the same sharing rule holds, but the renewal is
|
|
386
395
|
// the host's context refresh, credentialed and followed to the identity
|
|
387
|
-
// GET. It succeeds only on a 200 carrying the signed-in user
|
|
388
|
-
// CORS failure (no gateway session: governance
|
|
389
|
-
// non-200 or an HTML page answers false.
|
|
396
|
+
// GET. It succeeds only on a 200 carrying the signed-in user, and answers
|
|
397
|
+
// that user; a network or CORS failure (no gateway session: governance
|
|
398
|
+
// redirects to the IdP), a non-200 or an HTML page answers false. The
|
|
399
|
+
// identity GET itself is renewed this way too (ADR 0027), so the periodic
|
|
400
|
+
// refresh behind identity.onChange survives a lapsed context.
|
|
390
401
|
if (transport.separateApiHost) {
|
|
391
402
|
let contextRefreshes = 0;
|
|
392
403
|
let contextRefreshInFlight;
|
|
@@ -402,7 +413,7 @@ export function createClient(options = {}) {
|
|
|
402
413
|
if (typeof user?.id !== 'string')
|
|
403
414
|
return false;
|
|
404
415
|
notifyIdentity(user, true);
|
|
405
|
-
return
|
|
416
|
+
return user;
|
|
406
417
|
})
|
|
407
418
|
.catch(() => false)
|
|
408
419
|
.finally(() => { contextRefreshInFlight = undefined; });
|