Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Accept readonly arrays in public APIs #6790

Merged
merged 2 commits into from
Oct 14, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/lemon-paws-sneeze.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@keystone-next/auth': patch
'@keystone-next/keystone': patch
---

Readonly arrays are now accepted where previously mutable arrays were required. This means that if you use `as const` when writing an array and then pass it to various APIs in keystone, that will now work.
2 changes: 1 addition & 1 deletion packages/auth/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ export type AuthConfig<GeneratedListTypes extends BaseGeneratedListTypes> = {

export type InitFirstItemConfig<GeneratedListTypes extends BaseGeneratedListTypes> = {
/** Array of fields to collect, e.g ['name', 'email', 'password'] */
fields: GeneratedListTypes['fields'][];
fields: readonly GeneratedListTypes['fields'][];
/** Suppresses the second screen where we ask people to subscribe and follow Keystone */
skipKeystoneWelcome?: boolean;
/** Extra input to add for the create mutation */
Expand Down
6 changes: 3 additions & 3 deletions packages/keystone/src/fields/types/relationship/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,15 @@ type CardsDisplayConfig = {
// Sets the relationship to display as a list of Cards
displayMode: 'cards';
/* The set of fields to render in the default Card component **/
cardFields: string[];
cardFields: readonly string[];
/** Causes the default Card component to render as a link to navigate to the related item */
linkToItem?: boolean;
/** Determines whether removing a related item in the UI will delete or unlink it */
removeMode?: 'disconnect' | 'none'; // | 'delete';
/** Configures inline create mode for cards (alternative to opening the create modal) */
inlineCreate?: { fields: string[] };
inlineCreate?: { fields: readonly string[] };
/** Configures inline edit mode for cards */
inlineEdit?: { fields: string[] };
inlineEdit?: { fields: readonly string[] };
/** Configures whether a select to add existing items should be shown or not */
inlineConnect?: boolean;
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export function InlineCreate({
}: {
list: ListMeta;
selectedFields: string;
fields: string[];
fields: readonly string[];
onCancel: () => void;
onCreate: (itemGetter: DataGetter<ItemData>) => void;
}) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export function InlineEdit({
onCancel,
onSave,
}: {
fields: string[];
fields: readonly string[];
list: ListMeta;
selectedFields: string;
itemGetter: DataGetter<ItemData>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ export function useItemState({
};
}

export function useFieldsObj(list: ListMeta, fields: string[] | undefined) {
export function useFieldsObj(list: ListMeta, fields: readonly string[] | undefined) {
return useMemo(() => {
const editFields: Record<string, FieldMeta> = {};
fields?.forEach(fieldPath => {
Expand Down
12 changes: 6 additions & 6 deletions packages/keystone/src/fields/types/relationship/views/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -375,11 +375,11 @@ type RelationshipController = FieldController<
}
| {
mode: 'cards';
cardFields: string[];
cardFields: readonly string[];
linkToItem: boolean;
removeMode: 'disconnect' | 'none';
inlineCreate: { fields: string[] } | null;
inlineEdit: { fields: string[] } | null;
inlineCreate: { fields: readonly string[] } | null;
inlineEdit: { fields: readonly string[] } | null;
inlineConnect: boolean;
}
| { mode: 'count' };
Expand All @@ -404,11 +404,11 @@ export const controller = (
}
| {
displayMode: 'cards';
cardFields: string[];
cardFields: readonly string[];
linkToItem: boolean;
removeMode: 'disconnect' | 'none';
inlineCreate: { fields: string[] } | null;
inlineEdit: { fields: string[] } | null;
inlineCreate: { fields: readonly string[] } | null;
inlineEdit: { fields: readonly string[] } | null;
inlineConnect: boolean;
refLabelField: string;
}
Expand Down
6 changes: 3 additions & 3 deletions packages/keystone/src/fields/types/select/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,15 @@ export type SelectFieldConfig<TGeneratedListTypes extends BaseGeneratedListTypes
* When a value is provided as just a string, it will be formatted in the same way
* as field labels are to create the label.
*/
options: ({ label: string; value: string } | string)[];
options: readonly ({ label: string; value: string } | string)[];
/**
* If `enum` is provided on SQLite, it will use an enum in GraphQL but a string in the database.
*/
type?: 'string' | 'enum';
defaultValue?: string;
}
| {
options: { label: string; value: number }[];
options: readonly { label: string; value: number }[];
type: 'integer';
defaultValue?: number;
}
Expand Down Expand Up @@ -80,7 +80,7 @@ export const select =

assertCreateIsNonNullAllowed(meta, config);
const commonConfig = (
options: { value: string | number; label: string }[]
options: readonly { value: string | number; label: string }[]
): CommonFieldConfig<TGeneratedListTypes> & {
views: string;
getAdminMeta: () => import('./views').AdminSelectFieldMeta;
Expand Down
2 changes: 1 addition & 1 deletion packages/keystone/src/fields/types/select/views/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ export const CardValue: CardValueComponent<typeof controller> = ({ item, field }
};

export type AdminSelectFieldMeta = {
options: { label: string; value: string | number }[];
options: readonly { label: string; value: string | number }[];
type: 'string' | 'integer' | 'enum';
displayMode: 'select' | 'segmented-control';
isRequired: boolean;
Expand Down
2 changes: 1 addition & 1 deletion packages/keystone/src/fields/types/virtual/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export type VirtualFieldConfig<TGeneratedListTypes extends BaseGeneratedListType
field:
| VirtualFieldGraphQLField
| ((lists: Record<string, ListInfo>) => VirtualFieldGraphQLField);
unreferencedConcreteInterfaceImplementations?: graphql.ObjectType<any>[];
unreferencedConcreteInterfaceImplementations?: readonly graphql.ObjectType<any>[];
ui?: {
/**
* Defines what the Admin UI should fetch from this field, it's interpolated into a query like this:
Expand Down
6 changes: 3 additions & 3 deletions packages/keystone/src/lib/core/prisma-schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { ScalarDBField, ScalarDBFieldDefault, DatabaseProvider } from '../../typ
import { ResolvedDBField, ListsWithResolvedRelations } from './resolve-relationships';
import { getDBFieldKeyForFieldOnMultiField } from './utils';

function areArraysEqual(a: unknown[], b: unknown[]) {
function areArraysEqual(a: readonly unknown[], b: readonly unknown[]) {
if (a.length !== b.length) {
return false;
}
Expand Down Expand Up @@ -116,7 +116,7 @@ function printField(
}

function collectEnums(lists: ListsWithResolvedRelations) {
const enums: Record<string, { values: string[]; firstDefinedByRef: string }> = {};
const enums: Record<string, { values: readonly string[]; firstDefinedByRef: string }> = {};
for (const [listKey, { resolvedDbFields }] of Object.entries(lists)) {
for (const [fieldPath, field] of Object.entries(resolvedDbFields)) {
const fields =
Expand Down Expand Up @@ -195,7 +195,7 @@ export function printPrismaSchema(
lists: ListsWithResolvedRelations,
provider: DatabaseProvider,
clientDir: string,
prismaPreviewFeatures?: string[]
prismaPreviewFeatures: readonly string[] | undefined
) {
let prismaFlags = '';
if (prismaPreviewFeatures && prismaPreviewFeatures.length) {
Expand Down
2 changes: 1 addition & 1 deletion packages/keystone/src/types/config/fields.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export type CommonFieldConfig<TGeneratedListTypes extends BaseGeneratedListTypes
// If `true` then the field will be completely removed from all types.
//
// Default: undefined
omit?: true | ('read' | 'create' | 'update')[];
omit?: true | readonly ('read' | 'create' | 'update')[];
};
// Disabled by default...
isFilterable?: boolean | ((args: FilterOrderArgs) => MaybePromise<boolean>);
Expand Down
8 changes: 5 additions & 3 deletions packages/keystone/src/types/config/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ export type DatabaseConfig = {
enableLogging?: boolean;
idField?: IdFieldConfig;
provider: 'postgresql' | 'sqlite';
prismaPreviewFeatures?: string[]; // https://www.prisma.io/docs/concepts/components/preview-features
prismaPreviewFeatures?: readonly string[]; // https://www.prisma.io/docs/concepts/components/preview-features
};

// config.ui
Expand All @@ -71,11 +71,13 @@ export type AdminUIConfig = {
/** A function that can be run to validate that the current session should have access to the Admin UI */
isAccessAllowed?: (context: KeystoneContext) => MaybePromise<boolean>;
/** An array of page routes that can be accessed without passing the isAccessAllowed check */
publicPages?: string[];
publicPages?: readonly string[];
/** The basePath for the Admin UI App */
// FIXME: currently unused
// path?: string;
getAdditionalFiles?: ((config: KeystoneConfig) => MaybePromise<AdminFileToWrite[]>)[];
getAdditionalFiles?: readonly ((
config: KeystoneConfig
) => MaybePromise<readonly AdminFileToWrite[]>)[];
pageMiddleware?: (args: {
context: KeystoneContext;
isValidSession: boolean;
Expand Down
6 changes: 3 additions & 3 deletions packages/keystone/src/types/config/lists.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ export type ListAdminUIConfig<
* It is always possible to search by id and `id` should not be specified in this option.
* @default The `labelField` if it has a string `contains` filter, otherwise none.
*/
searchFields?: Extract<keyof Fields, string>[];
searchFields?: readonly Extract<keyof Fields, string>[];

/** The path that the list should be at in the Admin UI */
// Not currently used. Should be passed into `keystone.createList()`.
Expand Down Expand Up @@ -162,7 +162,7 @@ export type ListAdminUIConfig<
* Users of the Admin UI can select different columns to show in the UI.
* @default the first three fields in the list
*/
initialColumns?: ('id' | keyof Fields)[];
initialColumns?: readonly ('id' | keyof Fields)[];
// was previously top-level defaultSort
initialSort?: { field: 'id' | keyof Fields; direction: 'ASC' | 'DESC' };
// was previously defaultPageSize
Expand Down Expand Up @@ -211,7 +211,7 @@ export type ListGraphQLConfig = {
// including from the point of view of relationships to this list.
//
// Default: undefined
omit?: true | ('query' | 'create' | 'update' | 'delete')[];
omit?: true | readonly ('query' | 'create' | 'update' | 'delete')[];
};

export type CacheHintArgs = { results: any; operationName?: string; meta: boolean };
Expand Down
14 changes: 6 additions & 8 deletions packages/keystone/src/types/next-fields.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@ export { Decimal };

export type ItemRootValue = { id: { toString(): string }; [key: string]: unknown };

export type MaybeFunction<Params extends any[], Ret> = Ret | ((...params: Params) => Ret);

export type ListInfo = { types: TypesForList };

export type FieldData = {
Expand Down Expand Up @@ -125,7 +123,7 @@ export type RelationDBField<Mode extends 'many' | 'one'> = {
export type EnumDBField<Value extends string, Mode extends 'required' | 'many' | 'optional'> = {
kind: 'enum';
name: string;
values: Value[];
values: readonly Value[];
mode: Mode;
default?: { kind: 'literal'; value: Value };
index?: 'unique' | 'index';
Expand Down Expand Up @@ -155,15 +153,15 @@ type DBFieldToInputValue<TDBField extends DBField> = TDBField extends ScalarDBFi
? {
optional: ScalarPrismaTypes[Scalar] | null | undefined;
required: ScalarPrismaTypes[Scalar] | undefined;
many: ScalarPrismaTypes[Scalar][] | undefined;
many: readonly ScalarPrismaTypes[Scalar][] | undefined;
}[Mode]
: TDBField extends RelationDBField<'many' | 'one'>
? { connect?: {}; disconnect?: boolean } | undefined
: TDBField extends EnumDBField<infer Value, infer Mode>
? {
optional: Value | null | undefined;
required: Value | undefined;
many: Value[] | undefined;
many: readonly Value[] | undefined;
}[Mode]
: TDBField extends NoDBField
? undefined
Expand Down Expand Up @@ -191,7 +189,7 @@ type DBFieldToOutputValue<TDBField extends DBField> = TDBField extends ScalarDBF
? {
optional: ScalarPrismaTypes[Scalar] | null;
required: ScalarPrismaTypes[Scalar];
many: ScalarPrismaTypes[Scalar][];
many: readonly ScalarPrismaTypes[Scalar][];
}[Mode]
: TDBField extends RelationDBField<infer Mode>
? {
Expand All @@ -205,7 +203,7 @@ type DBFieldToOutputValue<TDBField extends DBField> = TDBField extends ScalarDBF
? {
optional: Value | null;
required: Value;
many: Value[];
many: readonly Value[];
}[Mode]
: TDBField extends NoDBField
? undefined
Expand Down Expand Up @@ -370,7 +368,7 @@ export type FieldTypeWithoutDBField<
views: string;
extraOutputFields?: Record<string, FieldTypeOutputField<TDBField>>;
getAdminMeta?: (adminMeta: AdminMetaRootVal) => JSONValue;
unreferencedConcreteInterfaceImplementations?: graphql.ObjectType<any>[];
unreferencedConcreteInterfaceImplementations?: readonly graphql.ObjectType<any>[];
} & CommonFieldConfig<BaseGeneratedListTypes>;

type AnyInputObj = graphql.InputObjectType<Record<string, graphql.Arg<graphql.InputType, any>>>;
Expand Down
2 changes: 1 addition & 1 deletion packages/keystone/src/types/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export type JSONValue =
| number
| boolean
| null
| Array<JSONValue>
| readonly JSONValue[]
| { [key: string]: JSONValue };

export type GqlNames = {
Expand Down