@superbuilders/primer-tives 3.7.1 → 4.0.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/README.md +9 -9
- package/dist/client/auth/access-token.d.ts.map +1 -1
- package/dist/client/auth/provider.d.ts +1 -0
- package/dist/client/auth/provider.d.ts.map +1 -1
- package/dist/client/consumed.d.ts.map +1 -1
- package/dist/client/index.js +137 -61
- package/dist/client/index.js.map +17 -17
- package/dist/client/session.d.ts +1 -0
- package/dist/client/session.d.ts.map +1 -1
- package/dist/client/start.d.ts.map +1 -1
- package/dist/client/transport.d.ts.map +1 -1
- package/dist/logger.d.ts +2 -6
- package/dist/logger.d.ts.map +1 -1
- package/dist/version.d.ts +1 -1
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -17,7 +17,7 @@ bun add @superbuilders/primer-tives
|
|
|
17
17
|
|
|
18
18
|
## Version
|
|
19
19
|
|
|
20
|
-
The current SDK version is `
|
|
20
|
+
The current SDK version is `4.0.1`.
|
|
21
21
|
|
|
22
22
|
## Entrypoints
|
|
23
23
|
|
|
@@ -38,7 +38,7 @@ There is no package-root export. Import from the public subpath that owns the su
|
|
|
38
38
|
Math content can require the fraction-input PCI capability, so a math renderer must declare it.
|
|
39
39
|
|
|
40
40
|
```ts
|
|
41
|
-
import
|
|
41
|
+
import { logger } from "@/logger"
|
|
42
42
|
import { start, type PrimerOptions } from "@superbuilders/primer-tives/client"
|
|
43
43
|
|
|
44
44
|
const options = {
|
|
@@ -133,7 +133,7 @@ Always switch on `state.phase`. Do not assume the first state is renderable lear
|
|
|
133
133
|
|
|
134
134
|
```ts
|
|
135
135
|
import * as errors from "@superbuilders/errors"
|
|
136
|
-
import
|
|
136
|
+
import { logger } from "@/logger"
|
|
137
137
|
import { start, type PrimerOptions } from "@superbuilders/primer-tives/client"
|
|
138
138
|
import { ErrAuthUnavailable, ErrMalformedAccessToken } from "@superbuilders/primer-tives/errors"
|
|
139
139
|
|
|
@@ -222,7 +222,7 @@ Applications should handle user-actionable auth failures directly and log unexpe
|
|
|
222
222
|
|
|
223
223
|
```ts
|
|
224
224
|
import * as errors from "@superbuilders/errors"
|
|
225
|
-
import
|
|
225
|
+
import { logger } from "@/logger"
|
|
226
226
|
import {
|
|
227
227
|
ErrAuthCancelled,
|
|
228
228
|
ErrAuthPopupBlocked,
|
|
@@ -309,7 +309,7 @@ The runtime also protects the trust boundary. If Primer presents a portable cust
|
|
|
309
309
|
Every renderer should switch on `state.phase`. Interaction rendering should then switch on `state.kind`.
|
|
310
310
|
|
|
311
311
|
```ts
|
|
312
|
-
import
|
|
312
|
+
import { logger } from "@/logger"
|
|
313
313
|
import type { PrimerState } from "@superbuilders/primer-tives/client"
|
|
314
314
|
|
|
315
315
|
async function runPrimer(initialState: PrimerState): Promise<void> {
|
|
@@ -916,7 +916,7 @@ Always use `safeParse` when parsing arbitrary input.
|
|
|
916
916
|
|
|
917
917
|
```ts
|
|
918
918
|
import * as errors from "@superbuilders/errors"
|
|
919
|
-
import
|
|
919
|
+
import { logger } from "@/logger"
|
|
920
920
|
import { RendererSubmissionSchema } from "@superbuilders/primer-tives/contracts"
|
|
921
921
|
|
|
922
922
|
const parsed = RendererSubmissionSchema.safeParse(payload)
|
|
@@ -965,7 +965,7 @@ The built-in standard interaction state methods call this before submitting. Cus
|
|
|
965
965
|
|
|
966
966
|
```ts
|
|
967
967
|
import * as errors from "@superbuilders/errors"
|
|
968
|
-
import
|
|
968
|
+
import { logger } from "@/logger"
|
|
969
969
|
import {
|
|
970
970
|
submissionValidationMessage,
|
|
971
971
|
validateSubmissionForInteraction
|
|
@@ -1274,10 +1274,10 @@ interface PrimerLogger {
|
|
|
1274
1274
|
}
|
|
1275
1275
|
```
|
|
1276
1276
|
|
|
1277
|
-
|
|
1277
|
+
Pino through the central `@/logger` module matches this interface directly.
|
|
1278
1278
|
|
|
1279
1279
|
```ts
|
|
1280
|
-
import
|
|
1280
|
+
import { logger } from "@/logger"
|
|
1281
1281
|
import { start, type PrimerOptions } from "@superbuilders/primer-tives/client"
|
|
1282
1282
|
|
|
1283
1283
|
const options = {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"access-token.d.ts","sourceRoot":"","sources":["../../../src/client/auth/access-token.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oCAAoC,CAAA;
|
|
1
|
+
{"version":3,"file":"access-token.d.ts","sourceRoot":"","sources":["../../../src/client/auth/access-token.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oCAAoC,CAAA;AAItE,QAAA,MAAM,wBAAwB,EAAE,OAAO,MAA+C,CAAA;AAEtF,KAAK,mBAAmB,GAAG;IAC1B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,CAAC,wBAAwB,CAAC,EAAE,IAAI,CAAA;CACzC,CAAA;AAmED,iBAAS,kBAAkB,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,GAAG,mBAAmB,CAUpF;AAED,OAAO,EAAE,kBAAkB,EAAE,CAAA;AAC7B,YAAY,EAAE,mBAAmB,EAAE,CAAA"}
|
|
@@ -11,6 +11,7 @@ type AccessTokenResolverOptions = {
|
|
|
11
11
|
type ResolvedAccessTokenResult = {
|
|
12
12
|
readonly kind: "resolved";
|
|
13
13
|
readonly accessToken: ResolvedAccessToken;
|
|
14
|
+
readonly invalidate?: () => void;
|
|
14
15
|
};
|
|
15
16
|
type UnauthenticatedAccessTokenResult = {
|
|
16
17
|
readonly kind: "unauthenticated";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../../../src/client/auth/provider.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oCAAoC,CAAA;AACtE,OAAO,EAIN,KAAK,iBAAiB,EACtB,MAAM,iDAAiD,CAAA;AAExD,OAAO,EAEN,KAAK,mBAAmB,EACxB,MAAM,sDAAsD,CAAA;AAS7D,KAAK,0BAA0B,GAAG;IACjC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAA;IAC/B,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;IAC7B,QAAQ,CAAC,UAAU,CAAC,EAAE,iBAAiB,CAAA;IACvC,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAA;CAC7B,CAAA;AAED,KAAK,yBAAyB,GAAG;IAChC,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAA;IACzB,QAAQ,CAAC,WAAW,EAAE,mBAAmB,CAAA;
|
|
1
|
+
{"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../../../src/client/auth/provider.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oCAAoC,CAAA;AACtE,OAAO,EAIN,KAAK,iBAAiB,EACtB,MAAM,iDAAiD,CAAA;AAExD,OAAO,EAEN,KAAK,mBAAmB,EACxB,MAAM,sDAAsD,CAAA;AAS7D,KAAK,0BAA0B,GAAG;IACjC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAA;IAC/B,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;IAC7B,QAAQ,CAAC,UAAU,CAAC,EAAE,iBAAiB,CAAA;IACvC,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAA;CAC7B,CAAA;AAED,KAAK,yBAAyB,GAAG;IAChC,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAA;IACzB,QAAQ,CAAC,WAAW,EAAE,mBAAmB,CAAA;IACzC,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,IAAI,CAAA;CAChC,CAAA;AAED,KAAK,gCAAgC,GAAG;IACvC,QAAQ,CAAC,IAAI,EAAE,iBAAiB,CAAA;IAChC,QAAQ,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI,CAAA;CAC5B,CAAA;AAED,KAAK,sBAAsB,GAAG;IAC7B,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAA;IACtB,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAA;CACrB,CAAA;AAED,KAAK,yBAAyB,GAC3B,yBAAyB,GACzB,gCAAgC,GAChC,sBAAsB,CAAA;AAEzB,KAAK,iBAAiB,GAAG,yBAAyB,GAAG,gCAAgC,CAAA;AAmErF,iBAAS,0BAA0B,CAClC,OAAO,EAAE,0BAA0B,GACjC,yBAAyB,CAe3B;AAED,iBAAe,gBAAgB,CAAC,OAAO,EAAE,0BAA0B,GAAG,OAAO,CAAC,iBAAiB,CAAC,CA0C/F;AAED,OAAO,EAAE,gBAAgB,EAAE,0BAA0B,EAAE,CAAA;AACvD,YAAY,EAAE,0BAA0B,EAAE,yBAAyB,EAAE,iBAAiB,EAAE,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"consumed.d.ts","sourceRoot":"","sources":["../../src/client/consumed.ts"],"names":[],"mappings":"AAEA,iBAAS,YAAY,IAAI,KAAK,
|
|
1
|
+
{"version":3,"file":"consumed.d.ts","sourceRoot":"","sources":["../../src/client/consumed.ts"],"names":[],"mappings":"AAEA,iBAAS,YAAY,IAAI,KAAK,CAE7B;AAED,OAAO,EAAE,YAAY,EAAE,CAAA"}
|
package/dist/client/index.js
CHANGED
|
@@ -379,7 +379,7 @@ function submissionValidationMessage(result) {
|
|
|
379
379
|
import * as errors2 from "@superbuilders/errors";
|
|
380
380
|
|
|
381
381
|
// src/version.ts
|
|
382
|
-
var SDK_VERSION = "
|
|
382
|
+
var SDK_VERSION = "4.0.1";
|
|
383
383
|
var NPM_PACKAGE_URL = "https://www.npmjs.com/package/@superbuilders/primer-tives";
|
|
384
384
|
|
|
385
385
|
// src/client/transport.ts
|
|
@@ -466,11 +466,11 @@ function httpSentinel(status, body) {
|
|
|
466
466
|
function buildSdkUpgradeRequiredError(sentinel, text, logger) {
|
|
467
467
|
const parsed = parseAdvanceErrorBody(text);
|
|
468
468
|
if (parsed === null) {
|
|
469
|
-
logger.error(
|
|
469
|
+
logger.error({
|
|
470
470
|
receivedSdkVersion: null,
|
|
471
471
|
minimumSdkVersion: "<unknown>",
|
|
472
472
|
upgradeUrl: NPM_PACKAGE_URL
|
|
473
|
-
});
|
|
473
|
+
}, "sdk upgrade required");
|
|
474
474
|
const message2 = `<missing> < <unknown>; bump @superbuilders/primer-tives at ${NPM_PACKAGE_URL}`;
|
|
475
475
|
return errors2.wrap(sentinel, message2);
|
|
476
476
|
}
|
|
@@ -478,11 +478,11 @@ function buildSdkUpgradeRequiredError(sentinel, text, logger) {
|
|
|
478
478
|
const receivedSdkVersion = parsed.receivedSdkVersion === undefined ? null : parsed.receivedSdkVersion;
|
|
479
479
|
const receivedDisplay = receivedSdkVersion === null ? "<missing>" : receivedSdkVersion;
|
|
480
480
|
const upgradeUrl = parsed.upgradeUrl === undefined ? NPM_PACKAGE_URL : parsed.upgradeUrl;
|
|
481
|
-
logger.error(
|
|
481
|
+
logger.error({
|
|
482
482
|
receivedSdkVersion,
|
|
483
483
|
minimumSdkVersion,
|
|
484
484
|
upgradeUrl
|
|
485
|
-
});
|
|
485
|
+
}, "sdk upgrade required");
|
|
486
486
|
const message = `${receivedDisplay} < ${minimumSdkVersion}; bump @superbuilders/primer-tives at ${upgradeUrl}`;
|
|
487
487
|
return errors2.wrap(sentinel, message);
|
|
488
488
|
}
|
|
@@ -498,6 +498,7 @@ function isAbortError(err) {
|
|
|
498
498
|
function createTransport(tc) {
|
|
499
499
|
const fetchFn = tc.fetch ? tc.fetch : globalThis.fetch;
|
|
500
500
|
const logger = tc.logger;
|
|
501
|
+
const advanceUrl = new URL(ADVANCE_PATH, tc.origin).toString();
|
|
501
502
|
function transportSignal() {
|
|
502
503
|
if (tc.abort) {
|
|
503
504
|
return tc.abort.signal;
|
|
@@ -505,12 +506,11 @@ function createTransport(tc) {
|
|
|
505
506
|
return;
|
|
506
507
|
}
|
|
507
508
|
async function transport(body) {
|
|
508
|
-
logger.debug(
|
|
509
|
+
logger.debug({
|
|
509
510
|
intentKind: body.intent.kind,
|
|
510
511
|
subject: body.subject
|
|
511
|
-
});
|
|
512
|
-
const
|
|
513
|
-
const fetchResult = await fetchFn(url, {
|
|
512
|
+
}, "transport request");
|
|
513
|
+
const fetchResult = await fetchFn(advanceUrl, {
|
|
514
514
|
method: "POST",
|
|
515
515
|
mode: "cors",
|
|
516
516
|
credentials: "omit",
|
|
@@ -529,14 +529,14 @@ function createTransport(tc) {
|
|
|
529
529
|
});
|
|
530
530
|
if (!fetchResult.ok) {
|
|
531
531
|
if (isAbortError(fetchResult.error)) {
|
|
532
|
-
logger.error(
|
|
532
|
+
logger.error({
|
|
533
533
|
intentKind: body.intent.kind
|
|
534
|
-
});
|
|
534
|
+
}, "transport timeout");
|
|
535
535
|
return { ok: false, error: errors2.wrap(ErrTimeout, fetchResult.error.message) };
|
|
536
536
|
}
|
|
537
|
-
logger.error(
|
|
537
|
+
logger.error({
|
|
538
538
|
error: fetchResult.error
|
|
539
|
-
});
|
|
539
|
+
}, "transport network error");
|
|
540
540
|
return { ok: false, error: errors2.wrap(ErrNetwork, fetchResult.error.message) };
|
|
541
541
|
}
|
|
542
542
|
const res = fetchResult.response;
|
|
@@ -548,12 +548,12 @@ function createTransport(tc) {
|
|
|
548
548
|
if (errors2.is(sentinel, ErrSdkUpgradeRequired)) {
|
|
549
549
|
return { ok: false, error: buildSdkUpgradeRequiredError(sentinel, text, logger) };
|
|
550
550
|
}
|
|
551
|
-
logger.error(
|
|
551
|
+
logger.error({
|
|
552
552
|
status: res.status,
|
|
553
553
|
body: text,
|
|
554
554
|
intentKind: body.intent.kind,
|
|
555
555
|
subject: body.subject
|
|
556
|
-
});
|
|
556
|
+
}, "transport http error");
|
|
557
557
|
return { ok: false, error: errors2.wrap(sentinel, text) };
|
|
558
558
|
}
|
|
559
559
|
const jsonResult = await res.json().then(function ok(data) {
|
|
@@ -562,14 +562,14 @@ function createTransport(tc) {
|
|
|
562
562
|
return { ok: false, error: err };
|
|
563
563
|
});
|
|
564
564
|
if (!jsonResult.ok) {
|
|
565
|
-
logger.error(
|
|
565
|
+
logger.error({
|
|
566
566
|
error: jsonResult.error
|
|
567
|
-
});
|
|
567
|
+
}, "transport json parse failed");
|
|
568
568
|
return { ok: false, error: errors2.wrap(ErrJsonParse, jsonResult.error.message) };
|
|
569
569
|
}
|
|
570
|
-
logger.debug(
|
|
570
|
+
logger.debug({
|
|
571
571
|
intentKind: body.intent.kind
|
|
572
|
-
});
|
|
572
|
+
}, "transport success");
|
|
573
573
|
return { ok: true, data: jsonResult.data };
|
|
574
574
|
}
|
|
575
575
|
return transport;
|
|
@@ -603,7 +603,7 @@ function choiceState(ctx, body, stimulus, interaction, options, maxChoices, minC
|
|
|
603
603
|
}
|
|
604
604
|
const validation = validateSubmissionForInteraction(interaction, submission);
|
|
605
605
|
if (!validation.ok) {
|
|
606
|
-
ctx.logger.error(
|
|
606
|
+
ctx.logger.error({ selectedKeys, issues: validation.issues }, "choice submit invalid");
|
|
607
607
|
return Promise.resolve(ctx.errored(errors3.wrap(ErrInvalidSubmission, submissionValidationMessage(validation)), "interaction", { kind: "interaction", submission }));
|
|
608
608
|
}
|
|
609
609
|
submitKey = key;
|
|
@@ -659,7 +659,7 @@ function extendedTextState(ctx, body, stimulus, interaction) {
|
|
|
659
659
|
}
|
|
660
660
|
const validation = validateSubmissionForInteraction(interaction, submission);
|
|
661
661
|
if (!validation.ok) {
|
|
662
|
-
ctx.logger.error(
|
|
662
|
+
ctx.logger.error({ value, issues: validation.issues }, "extended-text submit invalid");
|
|
663
663
|
return Promise.resolve(ctx.errored(errors4.wrap(ErrInvalidSubmission, submissionValidationMessage(validation)), "interaction", { kind: "interaction", submission }));
|
|
664
664
|
}
|
|
665
665
|
submitKey2 = key;
|
|
@@ -714,7 +714,7 @@ function extendedTextState(ctx, body, stimulus, interaction) {
|
|
|
714
714
|
}
|
|
715
715
|
const validation = validateSubmissionForInteraction(multi, submission);
|
|
716
716
|
if (!validation.ok) {
|
|
717
|
-
ctx.logger.error(
|
|
717
|
+
ctx.logger.error({ values, issues: validation.issues }, "extended-text submit invalid");
|
|
718
718
|
return Promise.resolve(ctx.errored(errors4.wrap(ErrInvalidSubmission, submissionValidationMessage(validation)), "interaction", { kind: "interaction", submission }));
|
|
719
719
|
}
|
|
720
720
|
submitKey = key;
|
|
@@ -795,7 +795,7 @@ function matchState(ctx, body, stimulus, interaction) {
|
|
|
795
795
|
}
|
|
796
796
|
const validation = validateSubmissionForInteraction(interaction, submission);
|
|
797
797
|
if (!validation.ok) {
|
|
798
|
-
ctx.logger.error(
|
|
798
|
+
ctx.logger.error({ pairs, issues: validation.issues }, "match submit invalid");
|
|
799
799
|
return Promise.resolve(ctx.errored(errors5.wrap(ErrInvalidSubmission, submissionValidationMessage(validation)), "interaction", { kind: "interaction", submission }));
|
|
800
800
|
}
|
|
801
801
|
submitKey = key;
|
|
@@ -872,7 +872,7 @@ function orderState(ctx, body, stimulus, interaction) {
|
|
|
872
872
|
}
|
|
873
873
|
const validation = validateSubmissionForInteraction(interaction, submission);
|
|
874
874
|
if (!validation.ok) {
|
|
875
|
-
ctx.logger.error(
|
|
875
|
+
ctx.logger.error({ orderedKeys, issues: validation.issues }, "order submit invalid");
|
|
876
876
|
return Promise.resolve(ctx.errored(errors6.wrap(ErrInvalidSubmission, submissionValidationMessage(validation)), "interaction", { kind: "interaction", submission }));
|
|
877
877
|
}
|
|
878
878
|
submitKey = key;
|
|
@@ -929,7 +929,7 @@ function pciInteractionState(ctx, body, stimulus, interaction) {
|
|
|
929
929
|
}
|
|
930
930
|
return Promise.resolve(ctx.errored(errors7.wrap(ErrConflict, "cannot submit a different pci payload while submit is in flight"), "interaction", { kind: "interaction", submission }));
|
|
931
931
|
}
|
|
932
|
-
ctx.logger.debug("pci submit"
|
|
932
|
+
ctx.logger.debug({ pciId }, "pci submit");
|
|
933
933
|
submitKey = key;
|
|
934
934
|
submitPending = ctx.execute({ kind: "interaction", submission }, "interaction").finally(function clearPending() {
|
|
935
935
|
submitPending = undefined;
|
|
@@ -945,7 +945,7 @@ function pciInteractionState(ctx, body, stimulus, interaction) {
|
|
|
945
945
|
if (timeoutPending) {
|
|
946
946
|
return timeoutPending;
|
|
947
947
|
}
|
|
948
|
-
ctx.logger.debug("pci timeout"
|
|
948
|
+
ctx.logger.debug({ pciId }, "pci timeout");
|
|
949
949
|
timeoutPending = ctx.execute(intent, "timeout").finally(function clearPending() {
|
|
950
950
|
timeoutPending = undefined;
|
|
951
951
|
});
|
|
@@ -985,7 +985,7 @@ function textEntryState(ctx, body, stimulus, interaction) {
|
|
|
985
985
|
}
|
|
986
986
|
const validation = validateSubmissionForInteraction(interaction, submission);
|
|
987
987
|
if (!validation.ok) {
|
|
988
|
-
ctx.logger.error(
|
|
988
|
+
ctx.logger.error({ value, issues: validation.issues }, "text-entry submit invalid");
|
|
989
989
|
return Promise.resolve(ctx.errored(errors8.wrap(ErrInvalidSubmission, submissionValidationMessage(validation)), "interaction", { kind: "interaction", submission }));
|
|
990
990
|
}
|
|
991
991
|
submitKey = key;
|
|
@@ -1073,13 +1073,13 @@ function makeSession(sc) {
|
|
|
1073
1073
|
retriable,
|
|
1074
1074
|
retry: function retry() {
|
|
1075
1075
|
if (!retriable) {
|
|
1076
|
-
logger.debug("retry ignored for non-retriable error"
|
|
1076
|
+
logger.debug({ failedPhase }, "retry ignored for non-retriable error");
|
|
1077
1077
|
return Promise.resolve(state);
|
|
1078
1078
|
}
|
|
1079
1079
|
if (pending) {
|
|
1080
1080
|
return pending;
|
|
1081
1081
|
}
|
|
1082
|
-
logger.debug("retrying from errored state"
|
|
1082
|
+
logger.debug({ failedPhase }, "retrying from errored state");
|
|
1083
1083
|
pending = execute(intent, failedPhase);
|
|
1084
1084
|
return pending;
|
|
1085
1085
|
},
|
|
@@ -1088,23 +1088,27 @@ function makeSession(sc) {
|
|
|
1088
1088
|
return state;
|
|
1089
1089
|
}
|
|
1090
1090
|
async function execute(intent, phase) {
|
|
1091
|
-
logger.debug(
|
|
1091
|
+
logger.debug({
|
|
1092
1092
|
phase,
|
|
1093
1093
|
intentKind: intent.kind,
|
|
1094
1094
|
subject: sc.subject
|
|
1095
|
-
});
|
|
1095
|
+
}, "session execute");
|
|
1096
1096
|
const body = {
|
|
1097
1097
|
subject: sc.subject,
|
|
1098
1098
|
intent
|
|
1099
1099
|
};
|
|
1100
1100
|
const result = await sc.transport(body);
|
|
1101
1101
|
if (!result.ok) {
|
|
1102
|
+
if (errors9.is(result.error, ErrTokenExpired) && sc.reauthenticate !== null) {
|
|
1103
|
+
logger.warn({ phase, intentKind: intent.kind }, "session token expired");
|
|
1104
|
+
return sc.reauthenticate();
|
|
1105
|
+
}
|
|
1102
1106
|
if (isFatalError(result.error)) {
|
|
1103
|
-
logger.error(
|
|
1107
|
+
logger.error({
|
|
1104
1108
|
error: result.error,
|
|
1105
1109
|
phase,
|
|
1106
1110
|
intentKind: intent.kind
|
|
1107
|
-
});
|
|
1111
|
+
}, "fatal transport error");
|
|
1108
1112
|
return {
|
|
1109
1113
|
phase: "fatal",
|
|
1110
1114
|
error: result.error,
|
|
@@ -1112,20 +1116,20 @@ function makeSession(sc) {
|
|
|
1112
1116
|
toJSON: poisonToJSON
|
|
1113
1117
|
};
|
|
1114
1118
|
}
|
|
1115
|
-
logger.warn(
|
|
1119
|
+
logger.warn({
|
|
1116
1120
|
error: result.error,
|
|
1117
1121
|
phase,
|
|
1118
1122
|
intentKind: intent.kind
|
|
1119
|
-
});
|
|
1123
|
+
}, "retriable transport error");
|
|
1120
1124
|
return errored(result.error, phase, intent);
|
|
1121
1125
|
}
|
|
1122
1126
|
const next = resolve(result.data);
|
|
1123
|
-
logger.debug(
|
|
1127
|
+
logger.debug({
|
|
1124
1128
|
phase,
|
|
1125
1129
|
intentKind: intent.kind,
|
|
1126
1130
|
outcome: result.data.outcome,
|
|
1127
1131
|
nextPhase: next.phase
|
|
1128
|
-
});
|
|
1132
|
+
}, "session execute resolved");
|
|
1129
1133
|
return next;
|
|
1130
1134
|
}
|
|
1131
1135
|
function isPciSupported(pciId) {
|
|
@@ -1142,7 +1146,7 @@ function makeSession(sc) {
|
|
|
1142
1146
|
}
|
|
1143
1147
|
if (interaction.type === "portable-custom") {
|
|
1144
1148
|
if (!isPciSupported(interaction.pciId)) {
|
|
1145
|
-
logger.error(
|
|
1149
|
+
logger.error({ pciId: interaction.pciId }, "unsupported pci in frame");
|
|
1146
1150
|
return {
|
|
1147
1151
|
phase: "fatal",
|
|
1148
1152
|
error: errors9.wrap(ErrUnsupportedPci, `pci '${interaction.pciId}'`),
|
|
@@ -1190,7 +1194,7 @@ function browserStorage(options, logger) {
|
|
|
1190
1194
|
function currentUrl(options, logger) {
|
|
1191
1195
|
if (options !== undefined && options.currentUrl !== undefined) {
|
|
1192
1196
|
if (!URL.canParse(options.currentUrl)) {
|
|
1193
|
-
logger.error(
|
|
1197
|
+
logger.error({ currentUrl: options.currentUrl }, "auth current url invalid");
|
|
1194
1198
|
throw ErrAuthConfigInvalid;
|
|
1195
1199
|
}
|
|
1196
1200
|
return new URL(options.currentUrl);
|
|
@@ -1204,7 +1208,7 @@ function currentUrl(options, logger) {
|
|
|
1204
1208
|
function redirectUri(options, url, logger) {
|
|
1205
1209
|
if (options !== undefined && options.redirectUri !== undefined) {
|
|
1206
1210
|
if (!URL.canParse(options.redirectUri)) {
|
|
1207
|
-
logger.error(
|
|
1211
|
+
logger.error({ redirectUri: options.redirectUri }, "auth redirect uri invalid");
|
|
1208
1212
|
throw ErrAuthConfigInvalid;
|
|
1209
1213
|
}
|
|
1210
1214
|
return options.redirectUri;
|
|
@@ -1253,7 +1257,7 @@ var AUTH_RESPONSE_MODE = "web_message";
|
|
|
1253
1257
|
function hostedAuthUrl(config) {
|
|
1254
1258
|
const logger = config.logger;
|
|
1255
1259
|
if (!URL.canParse(config.origin)) {
|
|
1256
|
-
logger.error(
|
|
1260
|
+
logger.error({ origin: config.origin }, "hosted auth origin invalid");
|
|
1257
1261
|
throw ErrAuthCallbackInvalid;
|
|
1258
1262
|
}
|
|
1259
1263
|
const authUrl = new URL("/auth/timeback", config.origin);
|
|
@@ -1362,7 +1366,7 @@ async function waitForPopupMessage(popup, config, expectedOrigin) {
|
|
|
1362
1366
|
return;
|
|
1363
1367
|
}
|
|
1364
1368
|
if (result2.kind === "error") {
|
|
1365
|
-
logger.error(
|
|
1369
|
+
logger.error({ error: result2.error }, "hosted auth popup failed");
|
|
1366
1370
|
finishWithError(result2.error);
|
|
1367
1371
|
return;
|
|
1368
1372
|
}
|
|
@@ -1385,7 +1389,7 @@ async function beginHostedPopup(config) {
|
|
|
1385
1389
|
const logger = config.logger;
|
|
1386
1390
|
const url = hostedAuthUrl(config);
|
|
1387
1391
|
if (!URL.canParse(config.origin)) {
|
|
1388
|
-
logger.error(
|
|
1392
|
+
logger.error({ origin: config.origin }, "hosted auth origin invalid");
|
|
1389
1393
|
throw ErrAuthCallbackInvalid;
|
|
1390
1394
|
}
|
|
1391
1395
|
const expectedOrigin = new URL(config.origin).origin;
|
|
@@ -1396,6 +1400,7 @@ async function beginHostedPopup(config) {
|
|
|
1396
1400
|
// src/client/auth/access-token.ts
|
|
1397
1401
|
import * as errors10 from "@superbuilders/errors";
|
|
1398
1402
|
var ACCESS_TOKEN_PREFIX = "eyJ";
|
|
1403
|
+
var TOKEN_EXPIRY_SKEW_MS = 60000;
|
|
1399
1404
|
var resolvedAccessTokenBrand = Symbol("primer resolved access token");
|
|
1400
1405
|
function isMalformedJws(token) {
|
|
1401
1406
|
if (!token.startsWith(ACCESS_TOKEN_PREFIX)) {
|
|
@@ -1404,11 +1409,65 @@ function isMalformedJws(token) {
|
|
|
1404
1409
|
const dotCount = token.split(".").length - 1;
|
|
1405
1410
|
return dotCount !== 2;
|
|
1406
1411
|
}
|
|
1412
|
+
function paddedBase64(base64Url) {
|
|
1413
|
+
const normalized = base64Url.replaceAll("-", "+").replaceAll("_", "/");
|
|
1414
|
+
const remainder = normalized.length % 4;
|
|
1415
|
+
if (remainder === 0) {
|
|
1416
|
+
return normalized;
|
|
1417
|
+
}
|
|
1418
|
+
if (remainder === 2) {
|
|
1419
|
+
return `${normalized}==`;
|
|
1420
|
+
}
|
|
1421
|
+
if (remainder === 3) {
|
|
1422
|
+
return `${normalized}=`;
|
|
1423
|
+
}
|
|
1424
|
+
throw errors10.wrap(ErrMalformedAccessToken, "payload base64");
|
|
1425
|
+
}
|
|
1426
|
+
function decodeBase64UrlJson(segment) {
|
|
1427
|
+
const decoded = errors10.trySync(function decodePayload() {
|
|
1428
|
+
const json = globalThis.atob(paddedBase64(segment));
|
|
1429
|
+
return JSON.parse(json);
|
|
1430
|
+
});
|
|
1431
|
+
if (decoded.error) {
|
|
1432
|
+
throw errors10.wrap(ErrMalformedAccessToken, "payload decode");
|
|
1433
|
+
}
|
|
1434
|
+
return decoded.data;
|
|
1435
|
+
}
|
|
1436
|
+
function numericClaim(payload, claim) {
|
|
1437
|
+
if (typeof payload !== "object" || payload === null) {
|
|
1438
|
+
return null;
|
|
1439
|
+
}
|
|
1440
|
+
const value = Reflect.get(payload, claim);
|
|
1441
|
+
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
1442
|
+
return null;
|
|
1443
|
+
}
|
|
1444
|
+
return value;
|
|
1445
|
+
}
|
|
1446
|
+
function validateAccessTokenTimestamp(token, logger) {
|
|
1447
|
+
const segments = token.split(".");
|
|
1448
|
+
const payloadSegment = segments[1];
|
|
1449
|
+
if (payloadSegment === undefined || payloadSegment.length === 0) {
|
|
1450
|
+
logger.error("access token payload missing");
|
|
1451
|
+
throw errors10.wrap(ErrMalformedAccessToken, "payload missing");
|
|
1452
|
+
}
|
|
1453
|
+
const payload = decodeBase64UrlJson(payloadSegment);
|
|
1454
|
+
const exp = numericClaim(payload, "exp");
|
|
1455
|
+
if (exp === null) {
|
|
1456
|
+
logger.error("access token exp missing");
|
|
1457
|
+
throw errors10.wrap(ErrMalformedAccessToken, "exp missing");
|
|
1458
|
+
}
|
|
1459
|
+
const expiresAtMs = exp * 1000;
|
|
1460
|
+
if (expiresAtMs <= Date.now() + TOKEN_EXPIRY_SKEW_MS) {
|
|
1461
|
+
logger.error("access token expired");
|
|
1462
|
+
throw ErrTokenExpired;
|
|
1463
|
+
}
|
|
1464
|
+
}
|
|
1407
1465
|
function resolveAccessToken(token, logger) {
|
|
1408
1466
|
if (isMalformedJws(token)) {
|
|
1409
|
-
logger.error(
|
|
1467
|
+
logger.error({ prefix: ACCESS_TOKEN_PREFIX }, "malformed access token");
|
|
1410
1468
|
throw errors10.wrap(ErrMalformedAccessToken, `token must start with '${ACCESS_TOKEN_PREFIX}' and contain two dots`);
|
|
1411
1469
|
}
|
|
1470
|
+
validateAccessTokenTimestamp(token, logger);
|
|
1412
1471
|
return { value: token, [resolvedAccessTokenBrand]: true };
|
|
1413
1472
|
}
|
|
1414
1473
|
|
|
@@ -1451,14 +1510,14 @@ function resolveProvidedAccessToken(token, logger) {
|
|
|
1451
1510
|
}
|
|
1452
1511
|
return { kind: "resolved", accessToken: result.data };
|
|
1453
1512
|
}
|
|
1454
|
-
function resolveManagedAccessToken(token, logger) {
|
|
1513
|
+
function resolveManagedAccessToken(token, logger, invalidate) {
|
|
1455
1514
|
const result = errors11.trySync(function resolveManagedToken() {
|
|
1456
1515
|
return resolveAccessToken(token, logger);
|
|
1457
1516
|
});
|
|
1458
1517
|
if (result.error) {
|
|
1459
1518
|
return { kind: "unauthenticated", error: result.error };
|
|
1460
1519
|
}
|
|
1461
|
-
return { kind: "resolved", accessToken: result.data };
|
|
1520
|
+
return { kind: "resolved", accessToken: result.data, invalidate };
|
|
1462
1521
|
}
|
|
1463
1522
|
function readBrowserAuthContext(options) {
|
|
1464
1523
|
const logger = options.logger;
|
|
@@ -1477,10 +1536,13 @@ function resolveStoredAccessToken(context, options) {
|
|
|
1477
1536
|
if (stored === null) {
|
|
1478
1537
|
return null;
|
|
1479
1538
|
}
|
|
1480
|
-
|
|
1481
|
-
if (resolved.kind === "unauthenticated") {
|
|
1539
|
+
function invalidate() {
|
|
1482
1540
|
clearStoredAccessToken(context.storage, options.publishableKey);
|
|
1483
1541
|
}
|
|
1542
|
+
const resolved = resolveManagedAccessToken(stored, options.logger, invalidate);
|
|
1543
|
+
if (resolved.kind === "unauthenticated") {
|
|
1544
|
+
invalidate();
|
|
1545
|
+
}
|
|
1484
1546
|
return resolved;
|
|
1485
1547
|
}
|
|
1486
1548
|
function resolveExistingAccessToken(options) {
|
|
@@ -1503,9 +1565,11 @@ async function beginHostedLogin(options) {
|
|
|
1503
1565
|
if ("kind" in context) {
|
|
1504
1566
|
return context;
|
|
1505
1567
|
}
|
|
1568
|
+
const storage = context.storage;
|
|
1569
|
+
const url = context.url;
|
|
1506
1570
|
const clientStateResult = errors11.trySync(function prepareHostedLogin() {
|
|
1507
1571
|
const clientState2 = randomClientState(logger);
|
|
1508
|
-
storeAuthState(
|
|
1572
|
+
storeAuthState(storage, options.publishableKey, clientState2);
|
|
1509
1573
|
return clientState2;
|
|
1510
1574
|
});
|
|
1511
1575
|
if (clientStateResult.error) {
|
|
@@ -1515,22 +1579,25 @@ async function beginHostedLogin(options) {
|
|
|
1515
1579
|
const accessTokenResult = await errors11.try(beginHostedPopup({
|
|
1516
1580
|
origin: options.origin,
|
|
1517
1581
|
publishableKey: options.publishableKey,
|
|
1518
|
-
currentUrl:
|
|
1582
|
+
currentUrl: url,
|
|
1519
1583
|
clientState,
|
|
1520
1584
|
options: options.hostedAuth,
|
|
1521
1585
|
logger
|
|
1522
1586
|
}));
|
|
1523
1587
|
if (accessTokenResult.error) {
|
|
1524
|
-
clearAuthState(
|
|
1588
|
+
clearAuthState(storage, options.publishableKey);
|
|
1525
1589
|
return { kind: "unauthenticated", error: accessTokenResult.error };
|
|
1526
1590
|
}
|
|
1527
|
-
|
|
1591
|
+
function invalidate() {
|
|
1592
|
+
clearStoredAccessToken(storage, options.publishableKey);
|
|
1593
|
+
}
|
|
1594
|
+
const resolved = resolveManagedAccessToken(accessTokenResult.data, logger, invalidate);
|
|
1528
1595
|
if (resolved.kind === "unauthenticated") {
|
|
1529
|
-
clearAuthState(
|
|
1596
|
+
clearAuthState(storage, options.publishableKey);
|
|
1530
1597
|
return resolved;
|
|
1531
1598
|
}
|
|
1532
|
-
storeAccessToken(
|
|
1533
|
-
clearAuthState(
|
|
1599
|
+
storeAccessToken(storage, options.publishableKey, accessTokenResult.data);
|
|
1600
|
+
clearAuthState(storage, options.publishableKey);
|
|
1534
1601
|
return resolved;
|
|
1535
1602
|
}
|
|
1536
1603
|
|
|
@@ -1574,9 +1641,17 @@ function primerOrigin(origin) {
|
|
|
1574
1641
|
}
|
|
1575
1642
|
return DEFAULT_PRIMER_ORIGIN;
|
|
1576
1643
|
}
|
|
1577
|
-
async function startRuntime(config,
|
|
1644
|
+
async function startRuntime(config, resolved) {
|
|
1645
|
+
let reauthenticate = null;
|
|
1646
|
+
const invalidate = resolved.invalidate;
|
|
1647
|
+
if (invalidate !== undefined) {
|
|
1648
|
+
reauthenticate = function reauthenticate2() {
|
|
1649
|
+
invalidate();
|
|
1650
|
+
return Promise.resolve(makeUnauthenticatedState(config, ErrTokenExpired));
|
|
1651
|
+
};
|
|
1652
|
+
}
|
|
1578
1653
|
const transport = createTransport({
|
|
1579
|
-
accessToken,
|
|
1654
|
+
accessToken: resolved.accessToken,
|
|
1580
1655
|
publishableKey: config.publishableKey,
|
|
1581
1656
|
subject: config.subject,
|
|
1582
1657
|
origin: config.origin,
|
|
@@ -1588,7 +1663,8 @@ async function startRuntime(config, accessToken) {
|
|
|
1588
1663
|
subject: config.subject,
|
|
1589
1664
|
supportedPcis: config.supportedPcis,
|
|
1590
1665
|
logger: config.logger,
|
|
1591
|
-
transport
|
|
1666
|
+
transport,
|
|
1667
|
+
reauthenticate
|
|
1592
1668
|
});
|
|
1593
1669
|
return session.execute({ kind: "observation" }, "start");
|
|
1594
1670
|
}
|
|
@@ -1609,7 +1685,7 @@ async function loginAndStart(config) {
|
|
|
1609
1685
|
if (result.kind === "unauthenticated") {
|
|
1610
1686
|
return makeUnauthenticatedState(config, result.error);
|
|
1611
1687
|
}
|
|
1612
|
-
return startRuntime(config, result
|
|
1688
|
+
return startRuntime(config, result);
|
|
1613
1689
|
}
|
|
1614
1690
|
async function start(options) {
|
|
1615
1691
|
const logger = options.logger;
|
|
@@ -1641,10 +1717,10 @@ async function start(options) {
|
|
|
1641
1717
|
if (accessToken.kind === "unauthenticated") {
|
|
1642
1718
|
return makeUnauthenticatedState(config, accessToken.error);
|
|
1643
1719
|
}
|
|
1644
|
-
return startRuntime(config, accessToken
|
|
1720
|
+
return startRuntime(config, accessToken);
|
|
1645
1721
|
}
|
|
1646
1722
|
export {
|
|
1647
1723
|
start
|
|
1648
1724
|
};
|
|
1649
1725
|
|
|
1650
|
-
//# debugId=
|
|
1726
|
+
//# debugId=F138BD9662B6EC5C64756E2164756E21
|