Skip to content

Commit 7bc20b3

Browse files
committed
feat(bb-auth-cognito)!: make AuthCognito generic a const type parameter
Change AuthCognito<O> to AuthCognito<const O> across all four entries so inline options literals narrow without `as const`. This narrows five projections by default — GroupOf (requireRole), AttrOf (updateUserAttribute family), ReadAttrOf (fetchUserAttributes), MfaTypeOf (confirmSignIn/updateMFAPreference), and CustomAttrNames. Fixes the call sites that pass wire strings to now-narrowed params by casting at the boundary (runtime validates regardless): comprehensive authCRequireRole + authCMfaUpdateMFAPreference, native-bindings cognitoRequireRole. Tightens admin.types-test case 5 to assert group narrowing without `as const`. BREAKING CHANGE: callers passing widened variables to requireRole / updateUserAttribute / updateMFAPreference now need a cast or literal args. Acceptable pre-release.
1 parent 1e6f997 commit 7bc20b3

7 files changed

Lines changed: 17 additions & 12 deletions

File tree

packages/bb-auth-cognito/src/admin.types-test.ts

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -57,13 +57,11 @@ async function actionsScopeGrantNotTypes() {
5757
}
5858

5959
// ─────────────────────────────────────────────────────────────────────────────
60-
// (5) Group narrowing on admin methods. With `as const` (today) the group union
61-
// narrows and a typo is rejected. (Task T5 — `const O` — will make this hold
62-
// WITHOUT `as const`; the @ts-expect-error below is tightened to the
63-
// non-const form then.)
60+
// (5) `const O` narrows group names on admin methods WITHOUT `as const` → typo
61+
// is a compile error. (Enabled by the `const O` class generic, T5.)
6462
// ─────────────────────────────────────────────────────────────────────────────
6563
async function groupNarrowing() {
66-
const auth = new AuthCognito(scope, 'a5', { groups: ['admins', 'readers'] as const, admin: { actions: ['groups'] } });
64+
const auth = new AuthCognito(scope, 'a5', { groups: ['admins', 'readers'], admin: { actions: ['groups'] } });
6765
await auth.admin.addUserToGroup('u', 'admins');
6866
await auth.admin.addUserToGroup('u', 'readers');
6967
// @ts-expect-error — 'editor' is not in 'admins' | 'readers'.

packages/bb-auth-cognito/src/index.aws.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -601,7 +601,7 @@ function statusForCognitoError(name: string): number {
601601
* See the mock entry (`./index.ts`) for full class-level JSDoc; both runtimes
602602
* share the same public API.
603603
*/
604-
export class AuthCognito<O extends AuthCognitoOptions = AuthCognitoOptions>
604+
export class AuthCognito<const O extends AuthCognitoOptions = AuthCognitoOptions>
605605
extends Scope
606606
implements BlocksAuth
607607
{

packages/bb-auth-cognito/src/index.browser.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ export * from './types.js';
2828
// that references `AuthCognito<typeof options>` typechecks identically under
2929
// `--conditions=browser`. The browser stub never executes any method —
3030
// bundlers scan imports, they don't call anything.
31-
export class AuthCognito<O extends AuthCognitoOptions = AuthCognitoOptions> {
31+
export class AuthCognito<const O extends AuthCognitoOptions = AuthCognitoOptions> {
3232
constructor(..._args: unknown[]) { /* no-op */ }
3333
/**
3434
* Server-only admin surface. Present so `auth.admin` typechecks under

packages/bb-auth-cognito/src/index.cdk.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ export * from './types.js';
6464
* Grants the Lambda `cognito-idp:*` scoped to this pool's ARN; the SSM
6565
* secret's IAM is granted by AppSetting itself.
6666
*/
67-
export class AuthCognito<O extends AuthCognitoOptions = AuthCognitoOptions> extends Scope {
67+
export class AuthCognito<const O extends AuthCognitoOptions = AuthCognitoOptions> extends Scope {
6868
public readonly userPool: cognito.IUserPool;
6969
public readonly userPoolClient: cognito.IUserPoolClient;
7070
private readonly sessions: KVStore;

packages/bb-auth-cognito/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,7 @@ interface PersistedState {
203203
* in a nested DynamoDB table provisioned by this BB via KVStore — single-
204204
* digit ms reads, pay-per-request billing.
205205
*/
206-
export class AuthCognito<O extends AuthCognitoMockOptions = AuthCognitoMockOptions>
206+
export class AuthCognito<const O extends AuthCognitoMockOptions = AuthCognitoMockOptions>
207207
extends Scope
208208
implements BlocksAuth
209209
{

test-apps/comprehensive/aws-blocks/index.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -952,7 +952,10 @@ export const api = new ApiNamespace(scope, 'api', (context) => ({
952952
},
953953

954954
async authCRequireRole(role: string) {
955-
const user = await authC.requireRole(context, role);
955+
// Role arrives over the wire as a string; narrow to the pool's group union
956+
// at the boundary (runtime validates membership regardless). `const O` made
957+
// requireRole's param the literal union 'admins' | 'readers'.
958+
const user = await authC.requireRole(context, role as Parameters<typeof authC.requireRole>[1]);
956959
return user;
957960
},
958961

@@ -1082,7 +1085,9 @@ export const api = new ApiNamespace(scope, 'api', (context) => ({
10821085
totp?: 'ENABLED' | 'DISABLED' | 'PREFERRED' | 'NOT_PREFERRED';
10831086
email?: 'ENABLED' | 'DISABLED' | 'PREFERRED' | 'NOT_PREFERRED';
10841087
}) {
1085-
await authCMfa.updateMFAPreference(context, input);
1088+
// `const O` narrowed updateMFAPreference's input to the pool's mfaTypes
1089+
// (here ['TOTP']); the wire shape is wider, so narrow at the boundary.
1090+
await authCMfa.updateMFAPreference(context, input as Parameters<typeof authCMfa.updateMFAPreference>[1]);
10861091
return { success: true };
10871092
},
10881093

test-apps/native-bindings/aws-blocks/index.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,9 @@ export const api = new ApiNamespace(scope, 'api', (context) => ({
238238
},
239239

240240
async cognitoRequireRole(role: string) {
241-
return await authCognito.requireRole(context, role);
241+
// `const O` narrowed requireRole's param to the pool's group union; narrow
242+
// the wire string at the boundary (runtime validates membership).
243+
return await authCognito.requireRole(context, role as Parameters<typeof authCognito.requireRole>[1]);
242244
},
243245

244246
async cognitoFetchUserAttributes() {

0 commit comments

Comments
 (0)