Skip to content

Commit

Permalink
Strict up some types (#88)
Browse files Browse the repository at this point in the history
  • Loading branch information
robertherber authored May 10, 2024
2 parents 1ac0e73 + a72f59b commit 72acf30
Show file tree
Hide file tree
Showing 73 changed files with 673 additions and 148 deletions.
14 changes: 14 additions & 0 deletions .changeset/strange-rice-march.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
"@zemble/auth-anonymous": patch
"@zemble/auth-api-token": patch
"@zemble/auth-apple": patch
"@zemble/auth-expo": patch
"@zemble/auth-otp": patch
"@zemble/auth": patch
"@zemble/bull": patch
"@zemble/cms": patch
"create-zemble-app": patch
"supplement-stack": patch
---

Strict up some types
2 changes: 1 addition & 1 deletion .env.github
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
BUN_VERSION=1.1.5
BUN_VERSION=1.1.7
MONGOMS_PREFER_GLOBAL_PATH=true
3 changes: 3 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,10 @@
"cSpell.words": [
"composability",
"dataloaders",
"pino",
"pucelle",
"SENDGRID",
"supabase",
"tldr",
"zemble"
],
Expand Down
6 changes: 3 additions & 3 deletions apps/supplement-stack/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ void bunRunner({
}),
AuthOTP.configure({
from: { email: '[email protected]' },
generateTokenContents: async ({ email }): Promise<Zemble.OtpToken> => {
generateTokenContents: async ({ email }) => {
const user = await Users.findOneAndUpdate({ email }, {
$set: {
lastLoginAt: new Date(),
Expand All @@ -41,12 +41,12 @@ void bunRunner({
returnDocument: 'after',
})

const ret = ({
const ret = {
email,
type: 'AuthOtp' as const,
userId: user!._id.toHexString(),
sub: user!._id.toHexString(),
})
}

return ret
},
Expand Down
2 changes: 1 addition & 1 deletion apps/supplement-stack/clients/papr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export let client: MongoClient | undefined
const papr = new Papr()

export async function connect({ logger }: {readonly logger: IStandardLogger}) {
const mongoUrl = process.env.MONGO_URL
const mongoUrl = process.env['MONGO_URL']

if (!mongoUrl) throw new Error('MONGO_URL not set')

Expand Down
2 changes: 1 addition & 1 deletion apps/supplement-stack/graphql/Mutation/addIngredient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import type {
} from '../schema.generated'
import type { WithoutId } from 'mongodb'

export const addIngredient: NonNullable<MutationResolvers['addIngredient']> = async (parent, {
export const addIngredient: NonNullable<MutationResolvers['addIngredient']> = async (_, {
title, imageUrls, nutrientsPer100g, servingSizes, ingredientId,
}, { logger }) => {
const _id = ingredientId ? new ObjectId(ingredientId) : new ObjectId()
Expand Down
4 changes: 2 additions & 2 deletions apps/supplement-stack/graphql/Mutation/addSupplement.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@ import type {
} from '../schema.generated'
import type { DocumentForInsert } from 'papr'

export const addSupplement: NonNullable<MutationResolvers['addSupplement']> = async (parent, {
export const addSupplement: NonNullable<MutationResolvers['addSupplement']> = async (_, {
amountInGrams, foodId, intakeTime, supplementId,
}, { decodedToken }) => {
const supplement: DocumentForInsert<typeof SupplementIntakeDbType[0], typeof SupplementIntakeDbType[1]> = {
amountInGrams,
foodId: new ObjectId(foodId),
userId: new ObjectId(decodedToken!.userId),
userId: new ObjectId(decodedToken!.sub),
intakeTime,
}

Expand Down
2 changes: 1 addition & 1 deletion apps/supplement-stack/graphql/Query/mySupplementIntakes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import type { QueryResolvers } from '../schema.generated'

export const mySupplementIntakes: NonNullable<QueryResolvers['mySupplementIntakes']> = async (_, __, { decodedToken }) => {
const results = await Supplements.find({
userId: new ObjectId(decodedToken!.userId),
userId: new ObjectId(decodedToken!.sub),
})

return results
Expand Down
2 changes: 1 addition & 1 deletion apps/supplement-stack/graphql/Type/SupplementIntake.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { Eatables } from '../../models'
import type { SupplementIntakeResolvers } from '../schema.generated'

const SupplementIntake: SupplementIntakeResolvers = {
food: async ({ foodId }, args, context) => {
food: async ({ foodId }) => {
const eatable = await Eatables.findOne({ _id: foodId })

return eatable!
Expand Down
4 changes: 2 additions & 2 deletions build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { readdirSync, statSync } from 'node:fs'
import { join } from 'node:path'

const target = process.argv.slice(2)[0] ?? 'node'
if (process.env.DEBUG) {
if (process.env['DEBUG']) {
console.log(`Target: ${target}`)
}

Expand Down Expand Up @@ -36,7 +36,7 @@ void Bun.build({
if (stdout.logs.length > 0) {
console.log(stdout.logs)
}
if (process.env.DEBUG) {
if (process.env['DEBUG']) {
if (stdout.success) {
console.log('success!')
}
Expand Down
Binary file modified bun.lockb
Binary file not shown.
4 changes: 2 additions & 2 deletions packages/auth-anonymous/graphql/Mutation/login.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ import plugin from '../../plugin'

import type { MutationResolvers } from '../schema.generated'

const login: MutationResolvers['login'] = async (_: unknown, __, { honoContext }) => {
const login: MutationResolvers['loginAnonymous'] = async (_: unknown, __, { honoContext }) => {
const userId = plugin.config.generateUserId(),
tokenData = plugin.config.generateTokenContents(userId),
bearerToken = await encodeToken(tokenData, userId),
refreshToken = await generateRefreshToken({ sub: tokenData.sub })
refreshToken = await generateRefreshToken({ sub: tokenData.userId })

if (authPlugin.config.cookies.isEnabled) {
setTokenCookies(honoContext, bearerToken, refreshToken)
Expand Down
14 changes: 8 additions & 6 deletions packages/auth-anonymous/plugin.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,23 @@
/* eslint-disable @typescript-eslint/no-namespace */

import Auth from '@zemble/auth'
import { Plugin, type BaseToken } from '@zemble/core'
import { Plugin } from '@zemble/core'

declare global {
namespace Zemble {
interface AnonymousAuthToken {
readonly type: 'AnonymousAuth',
readonly userId: string,
}

interface TokenRegistry {
readonly AnonymousAuth: {
readonly type: 'AnonymousAuth',
readonly userId: string,
} & BaseToken
readonly AnonymousAuth: AnonymousAuthToken
}
}
}

interface AnonymousConfig extends Zemble.GlobalConfig {
readonly generateTokenContents?: (userId: string) => Zemble.TokenRegistry[keyof Zemble.TokenRegistry],
readonly generateTokenContents?: (userId: string) => Zemble.AnonymousAuthToken,
readonly generateUserId?: () => string,
}

Expand Down
4 changes: 2 additions & 2 deletions packages/auth-api-token/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import Auth from '@zemble/auth'
import { Plugin } from '@zemble/core'
import GraphQLYoga from '@zemble/graphql'

const API_KEY_SECRET = process.env.API_KEY_SECRET ?? 'top-secret'
const INVALIDATE_API_KEYS_IAT_BEFORE = process.env.INVALIDATE_API_KEYS_IAT_BEFORE ? parseInt(process.env.INVALIDATE_API_KEYS_IAT_BEFORE, 10) : 0
const API_KEY_SECRET = process.env['API_KEY_SECRET'] ?? 'top-secret'
const INVALIDATE_API_KEYS_IAT_BEFORE = process.env['INVALIDATE_API_KEYS_IAT_BEFORE'] ? parseInt(process.env['INVALIDATE_API_KEYS_IAT_BEFORE'], 10) : 0

interface AuthConfig extends Zemble.GlobalConfig {
readonly API_KEY_SECRET?: string;
Expand Down
2 changes: 1 addition & 1 deletion packages/auth-api-token/utils/isAPIKeyValid.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import plugin from '../plugin'
const { INVALIDATE_API_KEYS_IAT_BEFORE } = plugin.config

export const isAPIKeyValid = async (token: string) => {
const keyContents = await verifyJwt(token)
const keyContents = await verifyJwt(token) as { readonly isAPIKey: boolean, readonly iat: number }

return 'isAPIKey' in keyContents && !!keyContents.isAPIKey && keyContents.iat && INVALIDATE_API_KEYS_IAT_BEFORE < keyContents.iat
}
93 changes: 93 additions & 0 deletions packages/auth-apple/graphql/schema.generated.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export type Scalars = {
Boolean: { input: boolean; output: boolean; }
Int: { input: number; output: number; }
Float: { input: number; output: number; }
DateTime: { input: any; output: any; }
JSONObject: { input: any; output: any; }
};

Expand Down Expand Up @@ -45,9 +46,16 @@ export type AuthOr = {
readonly match?: InputMaybe<Scalars['JSONObject']['input']>;
};

export type Error = {
readonly message: Scalars['String']['output'];
};

export type Mutation = {
readonly __typename?: 'Mutation';
readonly loginWithApple: AppleLoginResponse;
readonly logout: Scalars['DateTime']['output'];
readonly logoutFromAllDevices: Scalars['DateTime']['output'];
readonly refreshToken: NewTokenResponse;
};


Expand All @@ -61,9 +69,41 @@ export type MutationLoginWithAppleArgs = {
userUUID: Scalars['String']['input'];
};


export type MutationRefreshTokenArgs = {
bearerToken: Scalars['String']['input'];
refreshToken: Scalars['String']['input'];
};

export type NewTokenResponse = NewTokenSuccessResponse | RefreshTokenInvalidError;

export type NewTokenSuccessResponse = {
readonly __typename?: 'NewTokenSuccessResponse';
readonly bearerToken: Scalars['String']['output'];
readonly refreshToken: Scalars['String']['output'];
};

export type Query = {
readonly __typename?: 'Query';
readonly publicKey?: Maybe<Scalars['String']['output']>;
readonly readJWT: Scalars['JSONObject']['output'];
readonly state: Scalars['String']['output'];
readonly validateJWT: Scalars['Boolean']['output'];
};


export type QueryReadJwtArgs = {
token: Scalars['String']['input'];
};


export type QueryValidateJwtArgs = {
token: Scalars['String']['input'];
};

export type RefreshTokenInvalidError = {
readonly __typename?: 'RefreshTokenInvalidError';
readonly message: Scalars['String']['output'];
};

export type WithIndex<TObject> = TObject & Record<string, any>;
Expand Down Expand Up @@ -134,7 +174,15 @@ export type DirectiveResolverFn<TResult = {}, TParent = {}, TContext = {}, TArgs
info: GraphQLResolveInfo
) => TResult | Promise<TResult>;

/** Mapping of union types */
export type ResolversUnionTypes<RefType extends Record<string, unknown>> = ResolversObject<{
NewTokenResponse: ( NewTokenSuccessResponse ) | ( RefreshTokenInvalidError );
}>;

/** Mapping of interface types */
export type ResolversInterfaceTypes<RefType extends Record<string, unknown>> = ResolversObject<{
Error: never;
}>;

/** Mapping between all available schema types and the resolvers types */
export type ResolversTypes = ResolversObject<{
Expand All @@ -143,9 +191,14 @@ export type ResolversTypes = ResolversObject<{
AppleLoginResponse: ResolverTypeWrapper<AppleLoginResponse>;
AuthOr: AuthOr;
Boolean: ResolverTypeWrapper<Scalars['Boolean']['output']>;
DateTime: ResolverTypeWrapper<Scalars['DateTime']['output']>;
Error: ResolverTypeWrapper<ResolversInterfaceTypes<ResolversTypes>['Error']>;
JSONObject: ResolverTypeWrapper<Scalars['JSONObject']['output']>;
Mutation: ResolverTypeWrapper<{}>;
NewTokenResponse: ResolverTypeWrapper<ResolversUnionTypes<ResolversTypes>['NewTokenResponse']>;
NewTokenSuccessResponse: ResolverTypeWrapper<NewTokenSuccessResponse>;
Query: ResolverTypeWrapper<{}>;
RefreshTokenInvalidError: ResolverTypeWrapper<RefreshTokenInvalidError>;
String: ResolverTypeWrapper<Scalars['String']['output']>;
}>;

Expand All @@ -155,9 +208,14 @@ export type ResolversParentTypes = ResolversObject<{
AppleLoginResponse: AppleLoginResponse;
AuthOr: AuthOr;
Boolean: Scalars['Boolean']['output'];
DateTime: Scalars['DateTime']['output'];
Error: ResolversInterfaceTypes<ResolversParentTypes>['Error'];
JSONObject: Scalars['JSONObject']['output'];
Mutation: {};
NewTokenResponse: ResolversUnionTypes<ResolversParentTypes>['NewTokenResponse'];
NewTokenSuccessResponse: NewTokenSuccessResponse;
Query: {};
RefreshTokenInvalidError: RefreshTokenInvalidError;
String: Scalars['String']['output'];
}>;

Expand All @@ -176,23 +234,58 @@ export type AppleLoginResponseResolvers<ContextType = Zemble.GraphQLContext, Par
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
}>;

export interface DateTimeScalarConfig extends GraphQLScalarTypeConfig<ResolversTypes['DateTime'], any> {
name: 'DateTime';
}

export type ErrorResolvers<ContextType = Zemble.GraphQLContext, ParentType extends ResolversParentTypes['Error'] = ResolversParentTypes['Error']> = ResolversObject<{
__resolveType: TypeResolveFn<null, ParentType, ContextType>;
message?: Resolver<ResolversTypes['String'], ParentType, Zemble.AuthContextWithToken<ContextType>>;
}>;

export interface JsonObjectScalarConfig extends GraphQLScalarTypeConfig<ResolversTypes['JSONObject'], any> {
name: 'JSONObject';
}

export type MutationResolvers<ContextType = Zemble.GraphQLContext, ParentType extends ResolversParentTypes['Mutation'] = ResolversParentTypes['Mutation']> = ResolversObject<{
loginWithApple?: Resolver<ResolversTypes['AppleLoginResponse'], ParentType, Zemble.AuthContextWithToken<ContextType>, RequireFields<MutationLoginWithAppleArgs, 'authorizationCode' | 'identityToken' | 'realUserStatus' | 'userUUID'>>;
logout?: Resolver<ResolversTypes['DateTime'], ParentType, ContextType>;
logoutFromAllDevices?: Resolver<ResolversTypes['DateTime'], ParentType, ContextType>;
refreshToken?: Resolver<ResolversTypes['NewTokenResponse'], ParentType, Zemble.AuthContextWithToken<ContextType>, RequireFields<MutationRefreshTokenArgs, 'bearerToken' | 'refreshToken'>>;
}>;

export type NewTokenResponseResolvers<ContextType = Zemble.GraphQLContext, ParentType extends ResolversParentTypes['NewTokenResponse'] = ResolversParentTypes['NewTokenResponse']> = ResolversObject<{
__resolveType: TypeResolveFn<'NewTokenSuccessResponse' | 'RefreshTokenInvalidError', ParentType, ContextType>;
}>;

export type NewTokenSuccessResponseResolvers<ContextType = Zemble.GraphQLContext, ParentType extends ResolversParentTypes['NewTokenSuccessResponse'] = ResolversParentTypes['NewTokenSuccessResponse']> = ResolversObject<{
bearerToken?: Resolver<ResolversTypes['String'], ParentType, Zemble.AuthContextWithToken<ContextType>>;
refreshToken?: Resolver<ResolversTypes['String'], ParentType, Zemble.AuthContextWithToken<ContextType>>;
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
}>;

export type QueryResolvers<ContextType = Zemble.GraphQLContext, ParentType extends ResolversParentTypes['Query'] = ResolversParentTypes['Query']> = ResolversObject<{
publicKey?: Resolver<Maybe<ResolversTypes['String']>, ParentType, Zemble.AuthContextWithToken<ContextType>>;
readJWT?: Resolver<ResolversTypes['JSONObject'], ParentType, Zemble.AuthContextWithToken<ContextType>, RequireFields<QueryReadJwtArgs, 'token'>>;
state?: Resolver<ResolversTypes['String'], ParentType, Zemble.AuthContextWithToken<ContextType>>;
validateJWT?: Resolver<ResolversTypes['Boolean'], ParentType, Zemble.AuthContextWithToken<ContextType>, RequireFields<QueryValidateJwtArgs, 'token'>>;
}>;

export type RefreshTokenInvalidErrorResolvers<ContextType = Zemble.GraphQLContext, ParentType extends ResolversParentTypes['RefreshTokenInvalidError'] = ResolversParentTypes['RefreshTokenInvalidError']> = ResolversObject<{
message?: Resolver<ResolversTypes['String'], ParentType, Zemble.AuthContextWithToken<ContextType>>;
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
}>;

export type Resolvers<ContextType = Zemble.GraphQLContext> = ResolversObject<{
AppleLoginResponse?: AppleLoginResponseResolvers<ContextType>;
DateTime?: GraphQLScalarType;
Error?: ErrorResolvers<ContextType>;
JSONObject?: GraphQLScalarType;
Mutation?: MutationResolvers<ContextType>;
NewTokenResponse?: NewTokenResponseResolvers<ContextType>;
NewTokenSuccessResponse?: NewTokenSuccessResponseResolvers<ContextType>;
Query?: QueryResolvers<ContextType>;
RefreshTokenInvalidError?: RefreshTokenInvalidErrorResolvers<ContextType>;
}>;

export type DirectiveResolvers<ContextType = Zemble.GraphQLContext> = ResolversObject<{
Expand Down
12 changes: 6 additions & 6 deletions packages/auth-apple/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,12 +56,12 @@ function generateTokenContents(jwtContents: AppleJwtContents): Zemble.AppleToken

const defaultConfig = {
generateTokenContents,
AUTHENTICATED_REDIRECT_URL: process.env.AUTH_LOGGED_IN_REDIRECT_URL ?? '/',
UNAUTHENTICATED_REDIRECT_URL: process.env.AUTH_LOGIN_REDIRECT_URL ?? '/login',
INTERNAL_URL: process.env.INTERNAL_URL ?? 'http://localhost:3000',
APPLE_CLIENT_ID: process.env.APPLE_CLIENT_ID,
PRIVATE_KEY: process.env.PRIVATE_KEY,
PUBLIC_KEY: process.env.PUBLIC_KEY,
AUTHENTICATED_REDIRECT_URL: process.env['AUTH_LOGGED_IN_REDIRECT_URL'] ?? '/',
UNAUTHENTICATED_REDIRECT_URL: process.env['AUTH_LOGIN_REDIRECT_URL'] ?? '/login',
INTERNAL_URL: process.env['INTERNAL_URL'] ?? 'http://localhost:3000',
APPLE_CLIENT_ID: process.env['APPLE_CLIENT_ID'],
PRIVATE_KEY: process.env['PRIVATE_KEY'],
PUBLIC_KEY: process.env['PUBLIC_KEY'],
appleAuthInitializePath: '/auth/apple',
appleAuthCallbackPath: '/auth/apple/callback',
} satisfies AppleAuthConfig
Expand Down
2 changes: 1 addition & 1 deletion packages/auth-expo/config.ts
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export const TOKEN_KEY = process.env.EXPO_PUBLIC_TOKEN_KEY ?? 'auth-token'
export const TOKEN_KEY = process.env['EXPO_PUBLIC_TOKEN_KEY'] ?? 'auth-token'
3 changes: 2 additions & 1 deletion packages/auth-expo/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"extends": "expo/tsconfig.base",
"compilerOptions": {
"strict": true
"strict": true,
"noUncheckedIndexedAccess": true
}
}
Loading

0 comments on commit 72acf30

Please sign in to comment.