@nixxie-cms/core 1.0.3 → 2.0.0
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/CHANGELOG.md +36 -0
- package/CHANGES-1.1.md +134 -0
- package/context/dist/nixxie-cms-core-context.cjs.js +4 -3
- package/context/dist/nixxie-cms-core-context.esm.js +3 -2
- package/dist/declarations/src/access.d.ts +2 -2
- package/dist/declarations/src/access.d.ts.map +1 -1
- package/dist/declarations/src/admin-ui/components/Navigation.d.ts +2 -2
- package/dist/declarations/src/admin-ui/components/Navigation.d.ts.map +1 -1
- package/dist/declarations/src/admin-ui/context.d.ts +6 -6
- package/dist/declarations/src/admin-ui/context.d.ts.map +1 -1
- package/dist/declarations/src/admin-ui/utils/Fields.d.ts +3 -3
- package/dist/declarations/src/admin-ui/utils/Fields.d.ts.map +1 -1
- package/dist/declarations/src/admin-ui/utils/filters.d.ts +5 -5
- package/dist/declarations/src/admin-ui/utils/filters.d.ts.map +1 -1
- package/dist/declarations/src/admin-ui/utils/useCreateItem.d.ts +3 -3
- package/dist/declarations/src/admin-ui/utils/useCreateItem.d.ts.map +1 -1
- package/dist/declarations/src/admin-ui/utils/utils.d.ts +2 -2
- package/dist/declarations/src/admin-ui/utils/utils.d.ts.map +1 -1
- package/dist/declarations/src/context.d.ts +1 -1
- package/dist/declarations/src/context.d.ts.map +1 -1
- package/dist/declarations/src/fields/types/bigInt/index.d.ts +3 -3
- package/dist/declarations/src/fields/types/bigInt/index.d.ts.map +1 -1
- package/dist/declarations/src/fields/types/bytes/index.d.ts +3 -3
- package/dist/declarations/src/fields/types/bytes/index.d.ts.map +1 -1
- package/dist/declarations/src/fields/types/calendarDay/index.d.ts +3 -3
- package/dist/declarations/src/fields/types/calendarDay/index.d.ts.map +1 -1
- package/dist/declarations/src/fields/types/checkbox/index.d.ts +3 -3
- package/dist/declarations/src/fields/types/checkbox/index.d.ts.map +1 -1
- package/dist/declarations/src/fields/types/decimal/index.d.ts +3 -3
- package/dist/declarations/src/fields/types/decimal/index.d.ts.map +1 -1
- package/dist/declarations/src/fields/types/file/index.d.ts +4 -4
- package/dist/declarations/src/fields/types/file/index.d.ts.map +1 -1
- package/dist/declarations/src/fields/types/float/index.d.ts +3 -3
- package/dist/declarations/src/fields/types/float/index.d.ts.map +1 -1
- package/dist/declarations/src/fields/types/image/index.d.ts +4 -4
- package/dist/declarations/src/fields/types/image/index.d.ts.map +1 -1
- package/dist/declarations/src/fields/types/integer/index.d.ts +3 -3
- package/dist/declarations/src/fields/types/integer/index.d.ts.map +1 -1
- package/dist/declarations/src/fields/types/json/index.d.ts +3 -3
- package/dist/declarations/src/fields/types/json/index.d.ts.map +1 -1
- package/dist/declarations/src/fields/types/multiselect/index.d.ts +3 -3
- package/dist/declarations/src/fields/types/multiselect/index.d.ts.map +1 -1
- package/dist/declarations/src/fields/types/multiselect/views/index.d.ts.map +1 -1
- package/dist/declarations/src/fields/types/password/index.d.ts +3 -3
- package/dist/declarations/src/fields/types/password/index.d.ts.map +1 -1
- package/dist/declarations/src/fields/types/relationship/index.d.ts +8 -8
- package/dist/declarations/src/fields/types/relationship/index.d.ts.map +1 -1
- package/dist/declarations/src/fields/types/relationship/views/ComboboxMany.d.ts +3 -3
- package/dist/declarations/src/fields/types/relationship/views/ComboboxMany.d.ts.map +1 -1
- package/dist/declarations/src/fields/types/relationship/views/ComboboxSingle.d.ts +3 -3
- package/dist/declarations/src/fields/types/relationship/views/ComboboxSingle.d.ts.map +1 -1
- package/dist/declarations/src/fields/types/relationship/views/index.d.ts +3 -3
- package/dist/declarations/src/fields/types/relationship/views/index.d.ts.map +1 -1
- package/dist/declarations/src/fields/types/relationship/views/types.d.ts +3 -3
- package/dist/declarations/src/fields/types/relationship/views/types.d.ts.map +1 -1
- package/dist/declarations/src/fields/types/select/index.d.ts +3 -3
- package/dist/declarations/src/fields/types/select/index.d.ts.map +1 -1
- package/dist/declarations/src/fields/types/text/index.d.ts +3 -3
- package/dist/declarations/src/fields/types/text/index.d.ts.map +1 -1
- package/dist/declarations/src/fields/types/timestamp/index.d.ts +3 -3
- package/dist/declarations/src/fields/types/timestamp/index.d.ts.map +1 -1
- package/dist/declarations/src/fields/types/virtual/index.d.ts +7 -7
- package/dist/declarations/src/fields/types/virtual/index.d.ts.map +1 -1
- package/dist/declarations/src/helpers.d.ts +249 -13
- package/dist/declarations/src/helpers.d.ts.map +1 -1
- package/dist/declarations/src/index.d.ts +9 -4
- package/dist/declarations/src/index.d.ts.map +1 -1
- package/dist/declarations/src/internal-unstable/admin-ui/pages/ListPage/index.d.ts.map +1 -1
- package/dist/declarations/src/lib/admin-meta.d.ts +11 -11
- package/dist/declarations/src/lib/admin-meta.d.ts.map +1 -1
- package/dist/declarations/src/lib/core/access-control.d.ts +18 -18
- package/dist/declarations/src/lib/core/access-control.d.ts.map +1 -1
- package/dist/declarations/src/lib/core/cascade.d.ts +47 -0
- package/dist/declarations/src/lib/core/cascade.d.ts.map +1 -0
- package/dist/declarations/src/lib/core/initialise-lists.d.ts +27 -24
- package/dist/declarations/src/lib/core/initialise-lists.d.ts.map +1 -1
- package/dist/declarations/src/lib/env.d.ts +9 -0
- package/dist/declarations/src/lib/env.d.ts.map +1 -0
- package/dist/declarations/src/lib/system.d.ts +1 -1
- package/dist/declarations/src/lib/system.d.ts.map +1 -1
- package/dist/declarations/src/list-features.d.ts +162 -0
- package/dist/declarations/src/list-features.d.ts.map +1 -0
- package/dist/declarations/src/schema.d.ts +24 -23
- package/dist/declarations/src/schema.d.ts.map +1 -1
- package/dist/declarations/src/session.d.ts +75 -0
- package/dist/declarations/src/session.d.ts.map +1 -1
- package/dist/declarations/src/types/admin-meta.d.ts +11 -11
- package/dist/declarations/src/types/admin-meta.d.ts.map +1 -1
- package/dist/declarations/src/types/config/access-control.d.ts +42 -42
- package/dist/declarations/src/types/config/access-control.d.ts.map +1 -1
- package/dist/declarations/src/types/config/fields.d.ts +19 -19
- package/dist/declarations/src/types/config/fields.d.ts.map +1 -1
- package/dist/declarations/src/types/config/hooks.d.ts +131 -131
- package/dist/declarations/src/types/config/hooks.d.ts.map +1 -1
- package/dist/declarations/src/types/config/index.d.ts +190 -8
- package/dist/declarations/src/types/config/index.d.ts.map +1 -1
- package/dist/declarations/src/types/config/lists.d.ts +146 -108
- package/dist/declarations/src/types/config/lists.d.ts.map +1 -1
- package/dist/declarations/src/types/context.d.ts +507 -47
- package/dist/declarations/src/types/context.d.ts.map +1 -1
- package/dist/declarations/src/types/next-fields.d.ts +28 -28
- package/dist/declarations/src/types/next-fields.d.ts.map +1 -1
- package/dist/declarations/src/types/type-info.d.ts +3 -3
- package/dist/declarations/src/types/type-info.d.ts.map +1 -1
- package/dist/{express-455ae20c.cjs.js → express-84d534c2.cjs.js} +6 -6
- package/dist/{express-7559ca2d.esm.js → express-d0a4ce99.esm.js} +6 -6
- package/dist/{index-15c8f81e.esm.js → index-5d8b0b4e.esm.js} +363 -183
- package/dist/index-6055753b.cjs.js +393 -0
- package/dist/{index-42045902.cjs.js → index-ac29f382.cjs.js} +363 -185
- package/dist/index-f1703b7b.esm.js +386 -0
- package/dist/nixxie-cms-core.cjs.js +1388 -30
- package/dist/nixxie-cms-core.esm.js +1362 -24
- package/dist/{non-null-graphql-add6bb3d.cjs.js → non-null-graphql-4a44c122.cjs.js} +1 -1
- package/dist/{non-null-graphql-a84ed64d.esm.js → non-null-graphql-8c5feaae.esm.js} +1 -1
- package/dist/{resolve-hooks-165a9ce2.cjs.js → resolve-hooks-10a5f84c.cjs.js} +240 -6
- package/dist/{resolve-hooks-6813a045.esm.js → resolve-hooks-9e676794.esm.js} +238 -7
- package/dist/{system-a321642d.cjs.js → system-6b37a5f8.cjs.js} +33 -7
- package/dist/{system-03e49e4f.esm.js → system-e591d821.esm.js} +33 -7
- package/fields/dist/nixxie-cms-core-fields.cjs.js +29 -576
- package/fields/dist/nixxie-cms-core-fields.esm.js +18 -565
- package/fields/types/bytes/dist/nixxie-cms-core-fields-types-bytes.cjs.js +4 -2
- package/fields/types/bytes/dist/nixxie-cms-core-fields-types-bytes.esm.js +4 -2
- package/fields/types/multiselect/views/dist/nixxie-cms-core-fields-types-multiselect-views.cjs.js +1 -6
- package/fields/types/multiselect/views/dist/nixxie-cms-core-fields-types-multiselect-views.esm.js +1 -6
- package/fields/types/password/dist/nixxie-cms-core-fields-types-password.cjs.js +4 -2
- package/fields/types/password/dist/nixxie-cms-core-fields-types-password.esm.js +4 -2
- package/internal-unstable/artifacts/dist/nixxie-cms-core-internal-unstable-artifacts.cjs.js +4 -3
- package/internal-unstable/artifacts/dist/nixxie-cms-core-internal-unstable-artifacts.esm.js +4 -3
- package/package.json +4 -4
- package/scripts/cli/dist/nixxie-cms-core-scripts-cli.cjs.js +4 -3
- package/scripts/cli/dist/nixxie-cms-core-scripts-cli.esm.js +4 -3
- package/scripts/dist/nixxie-cms-core-scripts.cjs.js +4 -3
- package/scripts/dist/nixxie-cms-core-scripts.esm.js +4 -3
- package/session/dist/nixxie-cms-core-session.cjs.js +286 -0
- package/session/dist/nixxie-cms-core-session.esm.js +279 -1
- package/src/access.ts +25 -25
- package/src/admin-ui/admin-meta-graphql.ts +5 -5
- package/src/admin-ui/components/CreateButtonLink.tsx +46 -46
- package/src/admin-ui/components/Navigation.tsx +3 -3
- package/src/admin-ui/context.tsx +6 -6
- package/src/admin-ui/utils/Fields.tsx +241 -241
- package/src/admin-ui/utils/actionData.ts +36 -36
- package/src/admin-ui/utils/filters.ts +148 -148
- package/src/admin-ui/utils/useCreateItem.ts +171 -171
- package/src/admin-ui/utils/utils.tsx +127 -127
- package/src/context.ts +1 -1
- package/src/fields/non-null-graphql.ts +115 -115
- package/src/fields/types/bigInt/index.ts +6 -6
- package/src/fields/types/bytes/index.ts +6 -6
- package/src/fields/types/calendarDay/index.ts +18 -19
- package/src/fields/types/checkbox/index.ts +6 -6
- package/src/fields/types/decimal/index.ts +6 -6
- package/src/fields/types/file/index.ts +8 -8
- package/src/fields/types/float/index.ts +6 -6
- package/src/fields/types/image/index.ts +8 -8
- package/src/fields/types/integer/index.ts +6 -6
- package/src/fields/types/json/index.ts +5 -5
- package/src/fields/types/multiselect/index.ts +7 -7
- package/src/fields/types/multiselect/views/index.tsx +149 -151
- package/src/fields/types/password/index.ts +6 -6
- package/src/fields/types/relationship/index.ts +13 -13
- package/src/fields/types/relationship/views/ComboboxMany.tsx +110 -110
- package/src/fields/types/relationship/views/ComboboxSingle.tsx +115 -115
- package/src/fields/types/relationship/views/ContextualActions.tsx +139 -139
- package/src/fields/types/relationship/views/index.tsx +492 -492
- package/src/fields/types/relationship/views/types.ts +46 -46
- package/src/fields/types/relationship/views/useApolloQuery.ts +185 -185
- package/src/fields/types/relationship/views/useFilter.tsx +109 -109
- package/src/fields/types/select/index.ts +6 -6
- package/src/fields/types/text/index.ts +6 -6
- package/src/fields/types/timestamp/index.ts +23 -21
- package/src/fields/types/virtual/index.ts +11 -11
- package/src/helpers.ts +773 -42
- package/src/index.ts +66 -24
- package/src/internal-unstable/admin-ui/pages/ItemPage/common.tsx +4 -4
- package/src/internal-unstable/admin-ui/pages/ItemPage/index.tsx +5 -5
- package/src/internal-unstable/admin-ui/pages/ListPage/index.tsx +8 -8
- package/src/lib/admin-meta.ts +369 -369
- package/src/lib/context/createContext.ts +6 -0
- package/src/lib/core/access-control.ts +434 -434
- package/src/lib/core/cascade.ts +236 -0
- package/src/lib/core/initialise-lists.ts +49 -33
- package/src/lib/core/mutations/index.ts +7 -0
- package/src/lib/core/mutations/nested-mutation-many-input-resolvers.ts +145 -145
- package/src/lib/core/mutations/nested-mutation-one-input-resolvers.ts +71 -71
- package/src/lib/core/queries/output-field.ts +178 -178
- package/src/lib/env.ts +50 -0
- package/src/lib/id-field.ts +2 -2
- package/src/lib/system.ts +221 -207
- package/src/lib/typescript-schema-printer.ts +227 -227
- package/src/list-features.ts +476 -0
- package/src/schema.ts +92 -22
- package/src/session.ts +225 -0
- package/src/types/admin-meta.ts +218 -218
- package/src/types/config/access-control.ts +186 -186
- package/src/types/config/fields.ts +96 -96
- package/src/types/config/hooks.ts +529 -529
- package/src/types/config/index.ts +206 -7
- package/src/types/config/lists.ts +606 -565
- package/src/types/context.ts +592 -55
- package/src/types/next-fields.ts +31 -31
- package/src/types/type-info.ts +38 -38
- package/src/types/type-tests.ts +21 -21
package/src/lib/admin-meta.ts
CHANGED
|
@@ -1,369 +1,369 @@
|
|
|
1
|
-
import path from 'node:path'
|
|
2
|
-
|
|
3
|
-
import type {
|
|
4
|
-
BaseFieldTypeInfo,
|
|
5
|
-
BaseItem,
|
|
6
|
-
|
|
7
|
-
ConditionalFilter,
|
|
8
|
-
ConditionalFilterCase,
|
|
9
|
-
NixxieConfig,
|
|
10
|
-
NixxieContext,
|
|
11
|
-
|
|
12
|
-
MaybeBooleanItemFunctionWithFilter,
|
|
13
|
-
MaybeFieldFunction,
|
|
14
|
-
MaybeItemActionFunctionWithFilter,
|
|
15
|
-
MaybeItemFieldFunction,
|
|
16
|
-
MaybeItemFieldFunctionWithFilter,
|
|
17
|
-
MaybePromise,
|
|
18
|
-
MaybeSessionFunction,
|
|
19
|
-
} from '../types'
|
|
20
|
-
import type { ActionMeta, FieldMeta,
|
|
21
|
-
import type { GraphQLNames, JSONValue } from '../types/utils'
|
|
22
|
-
import type { InitialisedList } from './core/initialise-lists'
|
|
23
|
-
|
|
24
|
-
type EmptyResolver<Return> = (args: {}, context: NixxieContext) => MaybePromise<Return>
|
|
25
|
-
|
|
26
|
-
type FieldMetaSource_ = {
|
|
27
|
-
listKey: string
|
|
28
|
-
fieldKey: string
|
|
29
|
-
isOrderable: EmptyResolver<boolean>
|
|
30
|
-
isFilterable: EmptyResolver<boolean>
|
|
31
|
-
|
|
32
|
-
createView: {
|
|
33
|
-
fieldMode: EmptyResolver<ConditionalFilter<'edit' | 'hidden', 'hidden',
|
|
34
|
-
isRequired: EmptyResolver<ConditionalFilterCase<
|
|
35
|
-
}
|
|
36
|
-
itemView: {
|
|
37
|
-
fieldMode: MaybeItemFieldFunctionWithFilter<
|
|
38
|
-
'edit' | 'read' | 'hidden',
|
|
39
|
-
'read' | 'hidden',
|
|
40
|
-
|
|
41
|
-
BaseFieldTypeInfo
|
|
42
|
-
>
|
|
43
|
-
fieldPosition: MaybeItemFieldFunction<'form' | 'sidebar',
|
|
44
|
-
isRequired: MaybeBooleanItemFunctionWithFilter<
|
|
45
|
-
}
|
|
46
|
-
listView: {
|
|
47
|
-
fieldMode: EmptyResolver<'read' | 'hidden'>
|
|
48
|
-
}
|
|
49
|
-
item: BaseItem | null
|
|
50
|
-
itemField: BaseItem[string] | null
|
|
51
|
-
}
|
|
52
|
-
export type FieldMetaSource = FieldMetaSource_ &
|
|
53
|
-
Omit<FieldMeta, keyof FieldMetaSource_ | 'controller' | 'views'>
|
|
54
|
-
|
|
55
|
-
type ActionMetaSource_ = {
|
|
56
|
-
listKey: string
|
|
57
|
-
itemView: Omit<ActionMeta['itemView'], 'actionMode'> & {
|
|
58
|
-
actionMode: MaybeItemActionFunctionWithFilter<
|
|
59
|
-
'enabled' | 'disabled' | 'hidden',
|
|
60
|
-
'disabled' | 'hidden',
|
|
61
|
-
|
|
62
|
-
>
|
|
63
|
-
}
|
|
64
|
-
listView: {
|
|
65
|
-
actionMode: EmptyResolver<
|
|
66
|
-
ConditionalFilter<'enabled' | 'disabled' | 'hidden', 'disabled' | 'hidden',
|
|
67
|
-
>
|
|
68
|
-
}
|
|
69
|
-
item: BaseItem | null
|
|
70
|
-
}
|
|
71
|
-
export type ActionMetaSource = ActionMetaSource_ & Omit<ActionMeta, keyof ActionMetaSource_>
|
|
72
|
-
|
|
73
|
-
type ListMetaSource_ = {
|
|
74
|
-
fields: FieldMetaSource[]
|
|
75
|
-
fieldsByKey: Record<string, FieldMetaSource>
|
|
76
|
-
groups: {
|
|
77
|
-
label: string
|
|
78
|
-
description: string
|
|
79
|
-
fields: FieldMetaSource[]
|
|
80
|
-
}[]
|
|
81
|
-
actions: ActionMetaSource[]
|
|
82
|
-
graphql: { names: GraphQLNames }
|
|
83
|
-
pageSize: number
|
|
84
|
-
initialColumns: string[]
|
|
85
|
-
initialFilter: EmptyResolver<JSONValue>
|
|
86
|
-
initialSearchFields: string[]
|
|
87
|
-
initialSort:
|
|
88
|
-
isSingleton: boolean
|
|
89
|
-
|
|
90
|
-
hideNavigation: EmptyResolver<boolean>
|
|
91
|
-
hideCreate: EmptyResolver<boolean>
|
|
92
|
-
hideDelete: EmptyResolver<boolean>
|
|
93
|
-
item: BaseItem | null
|
|
94
|
-
}
|
|
95
|
-
export type ListMetaSource = ListMetaSource_ & Omit<
|
|
96
|
-
|
|
97
|
-
export type AdminMetaSource = {
|
|
98
|
-
lists: ListMetaSource[]
|
|
99
|
-
listsByKey: Record<string, ListMetaSource>
|
|
100
|
-
views: string[]
|
|
101
|
-
isAccessAllowed: (context: NixxieContext) => MaybePromise<boolean>
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
export function createAdminMeta(
|
|
105
|
-
config: NixxieConfig,
|
|
106
|
-
initialisedLists: Record<string, InitialisedList>
|
|
107
|
-
) {
|
|
108
|
-
const { lists } = config
|
|
109
|
-
const adminMetaRoot: AdminMetaSource = {
|
|
110
|
-
listsByKey: {},
|
|
111
|
-
lists: [],
|
|
112
|
-
views: [],
|
|
113
|
-
isAccessAllowed: config.ui?.isAccessAllowed,
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
const omittedLists: string[] = []
|
|
117
|
-
|
|
118
|
-
for (const [listKey, list] of Object.entries(initialisedLists)) {
|
|
119
|
-
const listConfig = lists[listKey]
|
|
120
|
-
|
|
121
|
-
// TODO: is this reasonable?
|
|
122
|
-
if (list.graphql.isEnabled.query === false) {
|
|
123
|
-
omittedLists.push(listKey)
|
|
124
|
-
continue
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
let initialColumns: string[]
|
|
128
|
-
if (listConfig.ui?.listView?.initialColumns) {
|
|
129
|
-
// if they've asked for a particular thing, give them that thing
|
|
130
|
-
initialColumns = listConfig.ui.listView.initialColumns as string[]
|
|
131
|
-
} else {
|
|
132
|
-
// otherwise, we'll start with the labelfield on the left and then add
|
|
133
|
-
// 2 more fields to the right of that. We don't include the 'id' field
|
|
134
|
-
// unless it happened to be the labelField
|
|
135
|
-
initialColumns = [
|
|
136
|
-
list.ui.labelField,
|
|
137
|
-
...Object.keys(list.fields)
|
|
138
|
-
.filter(fieldKey => list.fields[fieldKey].graphql.isEnabled.read)
|
|
139
|
-
.filter(fieldKey => fieldKey !== list.ui.labelField)
|
|
140
|
-
.filter(fieldKey => fieldKey !== 'id'),
|
|
141
|
-
].slice(0, 3)
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
let initialSearchFields = listConfig.ui?.searchFields?.concat()
|
|
145
|
-
if (!initialSearchFields) {
|
|
146
|
-
initialSearchFields = [...list.ui.triviallySearchableFields]
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
const maximumPageSize = Math.min(
|
|
150
|
-
listConfig.ui?.listView?.pageSize ?? 50,
|
|
151
|
-
(list.graphql.types.findManyArgs.take.defaultValue ?? Infinity) as number
|
|
152
|
-
)
|
|
153
|
-
|
|
154
|
-
adminMetaRoot.listsByKey[listKey] = {
|
|
155
|
-
key: listKey,
|
|
156
|
-
path: list.ui.labels.path,
|
|
157
|
-
|
|
158
|
-
label: list.ui.labels.label,
|
|
159
|
-
singular: list.ui.labels.singular,
|
|
160
|
-
plural: list.ui.labels.plural,
|
|
161
|
-
|
|
162
|
-
labelField: list.ui.labelField,
|
|
163
|
-
fields: [],
|
|
164
|
-
fieldsByKey: {},
|
|
165
|
-
groups: [],
|
|
166
|
-
actions: [],
|
|
167
|
-
|
|
168
|
-
graphql: {
|
|
169
|
-
names: list.graphql.names,
|
|
170
|
-
},
|
|
171
|
-
|
|
172
|
-
pageSize: maximumPageSize,
|
|
173
|
-
initialColumns,
|
|
174
|
-
initialSearchFields,
|
|
175
|
-
initialSort:
|
|
176
|
-
(listConfig.ui?.listView?.initialSort as
|
|
177
|
-
initialFilter: normalizeMaybeSessionFunction(listConfig.ui?.listView?.initialFilter ?? {}),
|
|
178
|
-
isSingleton: list.isSingleton,
|
|
179
|
-
|
|
180
|
-
hideNavigation: normalizeMaybeSessionFunction(listConfig.ui?.hideNavigation ?? false),
|
|
181
|
-
hideCreate: normalizeMaybeSessionFunction(
|
|
182
|
-
listConfig.ui?.hideCreate ?? !list.graphql.isEnabled.create
|
|
183
|
-
),
|
|
184
|
-
hideDelete: normalizeMaybeSessionFunction(
|
|
185
|
-
listConfig.ui?.hideDelete ?? !list.graphql.isEnabled.delete
|
|
186
|
-
),
|
|
187
|
-
|
|
188
|
-
item: null, // part of resolver
|
|
189
|
-
} satisfies ListMetaSource
|
|
190
|
-
|
|
191
|
-
adminMetaRoot.lists.push(adminMetaRoot.listsByKey[listKey])
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
let uniqueViewCount = -1
|
|
195
|
-
const stringViewsToIndex: Record<string, number> = {}
|
|
196
|
-
function getViewId(view: string) {
|
|
197
|
-
if (stringViewsToIndex[view] !== undefined) return stringViewsToIndex[view]
|
|
198
|
-
|
|
199
|
-
uniqueViewCount++
|
|
200
|
-
stringViewsToIndex[view] = uniqueViewCount
|
|
201
|
-
adminMetaRoot.views.push(view)
|
|
202
|
-
return uniqueViewCount
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
for (const [listKey, list] of Object.entries(initialisedLists)) {
|
|
206
|
-
if (omittedLists.includes(listKey)) continue
|
|
207
|
-
|
|
208
|
-
const listMeta = adminMetaRoot.listsByKey[listKey]
|
|
209
|
-
|
|
210
|
-
// populate .fields
|
|
211
|
-
for (const [fieldKey, field] of Object.entries(list.fields)) {
|
|
212
|
-
// if the field is a relationship field and is related to an omitted list, skip.
|
|
213
|
-
if (field.dbField.kind === 'relation' && omittedLists.includes(field.dbField.list)) continue
|
|
214
|
-
if (Object.values(field.graphql.isEnabled).every(x => x === false)) continue
|
|
215
|
-
assertValidView(
|
|
216
|
-
field.views,
|
|
217
|
-
`The \`views\` on the implementation of the field type at lists.${listKey}.fields.${fieldKey}`
|
|
218
|
-
)
|
|
219
|
-
|
|
220
|
-
const baseOrderFilterArgs = { fieldKey, listKey: list.listKey }
|
|
221
|
-
const isNonNull = (['read', 'create', 'update'] as const).filter(
|
|
222
|
-
operation => field.graphql.isNonNull[operation]
|
|
223
|
-
)
|
|
224
|
-
const fieldMeta = {
|
|
225
|
-
// FieldMeta
|
|
226
|
-
key: fieldKey,
|
|
227
|
-
label: field.ui.label,
|
|
228
|
-
description: field.ui.description,
|
|
229
|
-
fieldMeta: null,
|
|
230
|
-
viewsIndex: getViewId(field.views),
|
|
231
|
-
customViewsIndex:
|
|
232
|
-
field.ui.views === null
|
|
233
|
-
? null
|
|
234
|
-
: (assertValidView(field.views, `lists.${listKey}.fields.${fieldKey}.ui.views`),
|
|
235
|
-
getViewId(field.ui.views)),
|
|
236
|
-
search: list.ui.searchableFields.get(fieldKey) ?? null,
|
|
237
|
-
|
|
238
|
-
// FieldMetaSource_
|
|
239
|
-
listKey: listKey,
|
|
240
|
-
fieldKey: fieldKey,
|
|
241
|
-
isFilterable: normalizeIsOrderFilter(
|
|
242
|
-
field.input?.where ? field.graphql.isEnabled.filter : false,
|
|
243
|
-
baseOrderFilterArgs
|
|
244
|
-
),
|
|
245
|
-
isOrderable: normalizeIsOrderFilter(
|
|
246
|
-
field.input?.orderBy ? field.graphql.isEnabled.orderBy : false,
|
|
247
|
-
baseOrderFilterArgs
|
|
248
|
-
),
|
|
249
|
-
|
|
250
|
-
isNonNull,
|
|
251
|
-
createView: {
|
|
252
|
-
fieldMode: normalizeMaybeSessionFunction(field.ui.createView.fieldMode),
|
|
253
|
-
isRequired: normalizeMaybeSessionFunction(field.ui.createView.isRequired ?? false),
|
|
254
|
-
},
|
|
255
|
-
itemView: {
|
|
256
|
-
fieldMode: field.ui.itemView.fieldMode,
|
|
257
|
-
fieldPosition: field.ui.itemView.fieldPosition,
|
|
258
|
-
isRequired: field.ui.itemView.isRequired,
|
|
259
|
-
},
|
|
260
|
-
listView: {
|
|
261
|
-
fieldMode: normalizeMaybeSessionFunction(field.ui.listView.fieldMode),
|
|
262
|
-
},
|
|
263
|
-
|
|
264
|
-
item: null, // part of resolver
|
|
265
|
-
itemField: null, // part of resolver
|
|
266
|
-
} satisfies FieldMetaSource
|
|
267
|
-
|
|
268
|
-
listMeta.fields.push(fieldMeta)
|
|
269
|
-
listMeta.fieldsByKey[fieldKey] = fieldMeta
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
// populate .actions
|
|
273
|
-
for (const action of list.actions) {
|
|
274
|
-
listMeta.actions.push({
|
|
275
|
-
// ActionMeta
|
|
276
|
-
key: action.actionKey,
|
|
277
|
-
label: action.ui.label,
|
|
278
|
-
icon: action.ui.icon,
|
|
279
|
-
messages: {
|
|
280
|
-
...action.ui.messages,
|
|
281
|
-
},
|
|
282
|
-
graphql: {
|
|
283
|
-
arguments: action.graphql.arguments,
|
|
284
|
-
names: action.graphql.names,
|
|
285
|
-
},
|
|
286
|
-
|
|
287
|
-
// ActionMetaSource_
|
|
288
|
-
listKey,
|
|
289
|
-
itemView: {
|
|
290
|
-
...action.ui.itemView,
|
|
291
|
-
},
|
|
292
|
-
listView: {
|
|
293
|
-
actionMode: normalizeMaybeSessionFunction(action.ui.listView.actionMode),
|
|
294
|
-
},
|
|
295
|
-
item: null, // part of resolver
|
|
296
|
-
})
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
// populate .groups
|
|
300
|
-
for (const group of list.groups) {
|
|
301
|
-
listMeta.groups.push({
|
|
302
|
-
label: group.label,
|
|
303
|
-
description: group.description,
|
|
304
|
-
fields: group.fields.map(
|
|
305
|
-
fieldKey => adminMetaRoot.listsByKey[listKey].fieldsByKey[fieldKey]
|
|
306
|
-
),
|
|
307
|
-
})
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
// we do this seperately to the above so that fields can check other fields to validate their config or etc.
|
|
312
|
-
// (ofc they won't necessarily be able to see other field's fieldMeta)
|
|
313
|
-
for (const [key, list] of Object.entries(initialisedLists)) {
|
|
314
|
-
if (list.graphql.isEnabled.query === false) continue
|
|
315
|
-
for (const fieldMetaSource of adminMetaRoot.listsByKey[key].fields) {
|
|
316
|
-
// if the field is a relationship field and is related to an omitted list, skip.
|
|
317
|
-
const dbField = list.fields[fieldMetaSource.fieldKey].dbField
|
|
318
|
-
if (dbField.kind === 'relation' && omittedLists.includes(dbField.list)) continue
|
|
319
|
-
|
|
320
|
-
currentAdminMeta = adminMetaRoot
|
|
321
|
-
try {
|
|
322
|
-
fieldMetaSource.fieldMeta = list.fields[fieldMetaSource.fieldKey].getAdminMeta?.() ?? null
|
|
323
|
-
} finally {
|
|
324
|
-
currentAdminMeta = undefined
|
|
325
|
-
}
|
|
326
|
-
}
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
return adminMetaRoot
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
let currentAdminMeta: undefined | AdminMetaSource
|
|
333
|
-
|
|
334
|
-
export function getAdminMetaForRelationshipField() {
|
|
335
|
-
if (currentAdminMeta) return currentAdminMeta
|
|
336
|
-
throw new Error('unexpected call to getAdminMetaInRelationshipField')
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
function assertValidView(view: string, location: string) {
|
|
340
|
-
if (view.includes('\\')) {
|
|
341
|
-
throw new Error(
|
|
342
|
-
`${location} contains a backslash, which is invalid. You need to use a module path that is resolved from where 'nixxie start' is run (see https://github.com/nixxiecms/nixxie/pull/7805)`
|
|
343
|
-
)
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
if (path.isAbsolute(view)) {
|
|
347
|
-
throw new Error(
|
|
348
|
-
`${location} is an absolute path, which is invalid. You need to use a module path that is resolved from where 'nixxie start' is run (see https://github.com/nixxiecms/nixxie/pull/7805)`
|
|
349
|
-
)
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
function normalizeMaybeSessionFunction<Return extends string | boolean | object | null | number>(
|
|
354
|
-
input: MaybeSessionFunction<Return,
|
|
355
|
-
): EmptyResolver<Return> {
|
|
356
|
-
if (typeof input !== 'function') return () => input
|
|
357
|
-
return (_, context) => input({ context, session: context.session })
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
function normalizeIsOrderFilter(
|
|
361
|
-
input: MaybeFieldFunction<
|
|
362
|
-
baseOrderFilterArgs: {
|
|
363
|
-
listKey: string
|
|
364
|
-
fieldKey: string
|
|
365
|
-
}
|
|
366
|
-
): EmptyResolver<boolean> {
|
|
367
|
-
if (typeof input !== 'function') return () => input
|
|
368
|
-
return (_, context) => input({ context, session: context.session, ...baseOrderFilterArgs })
|
|
369
|
-
}
|
|
1
|
+
import path from 'node:path'
|
|
2
|
+
|
|
3
|
+
import type {
|
|
4
|
+
BaseFieldTypeInfo,
|
|
5
|
+
BaseItem,
|
|
6
|
+
BaseCollectionTypeInfo,
|
|
7
|
+
ConditionalFilter,
|
|
8
|
+
ConditionalFilterCase,
|
|
9
|
+
NixxieConfig,
|
|
10
|
+
NixxieContext,
|
|
11
|
+
CollectionSortDescriptor,
|
|
12
|
+
MaybeBooleanItemFunctionWithFilter,
|
|
13
|
+
MaybeFieldFunction,
|
|
14
|
+
MaybeItemActionFunctionWithFilter,
|
|
15
|
+
MaybeItemFieldFunction,
|
|
16
|
+
MaybeItemFieldFunctionWithFilter,
|
|
17
|
+
MaybePromise,
|
|
18
|
+
MaybeSessionFunction,
|
|
19
|
+
} from '../types'
|
|
20
|
+
import type { ActionMeta, FieldMeta, CollectionMeta } from '../types/admin-meta'
|
|
21
|
+
import type { GraphQLNames, JSONValue } from '../types/utils'
|
|
22
|
+
import type { InitialisedList } from './core/initialise-lists'
|
|
23
|
+
|
|
24
|
+
type EmptyResolver<Return> = (args: {}, context: NixxieContext) => MaybePromise<Return>
|
|
25
|
+
|
|
26
|
+
type FieldMetaSource_ = {
|
|
27
|
+
listKey: string
|
|
28
|
+
fieldKey: string
|
|
29
|
+
isOrderable: EmptyResolver<boolean>
|
|
30
|
+
isFilterable: EmptyResolver<boolean>
|
|
31
|
+
|
|
32
|
+
createView: {
|
|
33
|
+
fieldMode: EmptyResolver<ConditionalFilter<'edit' | 'hidden', 'hidden', BaseCollectionTypeInfo>>
|
|
34
|
+
isRequired: EmptyResolver<ConditionalFilterCase<BaseCollectionTypeInfo>>
|
|
35
|
+
}
|
|
36
|
+
itemView: {
|
|
37
|
+
fieldMode: MaybeItemFieldFunctionWithFilter<
|
|
38
|
+
'edit' | 'read' | 'hidden',
|
|
39
|
+
'read' | 'hidden',
|
|
40
|
+
BaseCollectionTypeInfo,
|
|
41
|
+
BaseFieldTypeInfo
|
|
42
|
+
>
|
|
43
|
+
fieldPosition: MaybeItemFieldFunction<'form' | 'sidebar', BaseCollectionTypeInfo, BaseFieldTypeInfo>
|
|
44
|
+
isRequired: MaybeBooleanItemFunctionWithFilter<BaseCollectionTypeInfo, BaseFieldTypeInfo>
|
|
45
|
+
}
|
|
46
|
+
listView: {
|
|
47
|
+
fieldMode: EmptyResolver<'read' | 'hidden'>
|
|
48
|
+
}
|
|
49
|
+
item: BaseItem | null
|
|
50
|
+
itemField: BaseItem[string] | null
|
|
51
|
+
}
|
|
52
|
+
export type FieldMetaSource = FieldMetaSource_ &
|
|
53
|
+
Omit<FieldMeta, keyof FieldMetaSource_ | 'controller' | 'views'>
|
|
54
|
+
|
|
55
|
+
type ActionMetaSource_ = {
|
|
56
|
+
listKey: string
|
|
57
|
+
itemView: Omit<ActionMeta['itemView'], 'actionMode'> & {
|
|
58
|
+
actionMode: MaybeItemActionFunctionWithFilter<
|
|
59
|
+
'enabled' | 'disabled' | 'hidden',
|
|
60
|
+
'disabled' | 'hidden',
|
|
61
|
+
BaseCollectionTypeInfo
|
|
62
|
+
>
|
|
63
|
+
}
|
|
64
|
+
listView: {
|
|
65
|
+
actionMode: EmptyResolver<
|
|
66
|
+
ConditionalFilter<'enabled' | 'disabled' | 'hidden', 'disabled' | 'hidden', BaseCollectionTypeInfo>
|
|
67
|
+
>
|
|
68
|
+
}
|
|
69
|
+
item: BaseItem | null
|
|
70
|
+
}
|
|
71
|
+
export type ActionMetaSource = ActionMetaSource_ & Omit<ActionMeta, keyof ActionMetaSource_>
|
|
72
|
+
|
|
73
|
+
type ListMetaSource_ = {
|
|
74
|
+
fields: FieldMetaSource[]
|
|
75
|
+
fieldsByKey: Record<string, FieldMetaSource>
|
|
76
|
+
groups: {
|
|
77
|
+
label: string
|
|
78
|
+
description: string
|
|
79
|
+
fields: FieldMetaSource[]
|
|
80
|
+
}[]
|
|
81
|
+
actions: ActionMetaSource[]
|
|
82
|
+
graphql: { names: GraphQLNames }
|
|
83
|
+
pageSize: number
|
|
84
|
+
initialColumns: string[]
|
|
85
|
+
initialFilter: EmptyResolver<JSONValue>
|
|
86
|
+
initialSearchFields: string[]
|
|
87
|
+
initialSort: CollectionSortDescriptor<string> | null
|
|
88
|
+
isSingleton: boolean
|
|
89
|
+
|
|
90
|
+
hideNavigation: EmptyResolver<boolean>
|
|
91
|
+
hideCreate: EmptyResolver<boolean>
|
|
92
|
+
hideDelete: EmptyResolver<boolean>
|
|
93
|
+
item: BaseItem | null
|
|
94
|
+
}
|
|
95
|
+
export type ListMetaSource = ListMetaSource_ & Omit<CollectionMeta, keyof ListMetaSource_>
|
|
96
|
+
|
|
97
|
+
export type AdminMetaSource = {
|
|
98
|
+
lists: ListMetaSource[]
|
|
99
|
+
listsByKey: Record<string, ListMetaSource>
|
|
100
|
+
views: string[]
|
|
101
|
+
isAccessAllowed: (context: NixxieContext) => MaybePromise<boolean>
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export function createAdminMeta(
|
|
105
|
+
config: NixxieConfig,
|
|
106
|
+
initialisedLists: Record<string, InitialisedList>
|
|
107
|
+
) {
|
|
108
|
+
const { lists } = config
|
|
109
|
+
const adminMetaRoot: AdminMetaSource = {
|
|
110
|
+
listsByKey: {},
|
|
111
|
+
lists: [],
|
|
112
|
+
views: [],
|
|
113
|
+
isAccessAllowed: config.ui?.isAccessAllowed,
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const omittedLists: string[] = []
|
|
117
|
+
|
|
118
|
+
for (const [listKey, list] of Object.entries(initialisedLists)) {
|
|
119
|
+
const listConfig = lists[listKey]
|
|
120
|
+
|
|
121
|
+
// TODO: is this reasonable?
|
|
122
|
+
if (list.graphql.isEnabled.query === false) {
|
|
123
|
+
omittedLists.push(listKey)
|
|
124
|
+
continue
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
let initialColumns: string[]
|
|
128
|
+
if (listConfig.ui?.listView?.initialColumns) {
|
|
129
|
+
// if they've asked for a particular thing, give them that thing
|
|
130
|
+
initialColumns = listConfig.ui.listView.initialColumns as string[]
|
|
131
|
+
} else {
|
|
132
|
+
// otherwise, we'll start with the labelfield on the left and then add
|
|
133
|
+
// 2 more fields to the right of that. We don't include the 'id' field
|
|
134
|
+
// unless it happened to be the labelField
|
|
135
|
+
initialColumns = [
|
|
136
|
+
list.ui.labelField,
|
|
137
|
+
...Object.keys(list.fields)
|
|
138
|
+
.filter(fieldKey => list.fields[fieldKey].graphql.isEnabled.read)
|
|
139
|
+
.filter(fieldKey => fieldKey !== list.ui.labelField)
|
|
140
|
+
.filter(fieldKey => fieldKey !== 'id'),
|
|
141
|
+
].slice(0, 3)
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
let initialSearchFields = listConfig.ui?.searchFields?.concat()
|
|
145
|
+
if (!initialSearchFields) {
|
|
146
|
+
initialSearchFields = [...list.ui.triviallySearchableFields]
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const maximumPageSize = Math.min(
|
|
150
|
+
listConfig.ui?.listView?.pageSize ?? 50,
|
|
151
|
+
(list.graphql.types.findManyArgs.take.defaultValue ?? Infinity) as number
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
adminMetaRoot.listsByKey[listKey] = {
|
|
155
|
+
key: listKey,
|
|
156
|
+
path: list.ui.labels.path,
|
|
157
|
+
|
|
158
|
+
label: list.ui.labels.label,
|
|
159
|
+
singular: list.ui.labels.singular,
|
|
160
|
+
plural: list.ui.labels.plural,
|
|
161
|
+
|
|
162
|
+
labelField: list.ui.labelField,
|
|
163
|
+
fields: [],
|
|
164
|
+
fieldsByKey: {},
|
|
165
|
+
groups: [],
|
|
166
|
+
actions: [],
|
|
167
|
+
|
|
168
|
+
graphql: {
|
|
169
|
+
names: list.graphql.names,
|
|
170
|
+
},
|
|
171
|
+
|
|
172
|
+
pageSize: maximumPageSize,
|
|
173
|
+
initialColumns,
|
|
174
|
+
initialSearchFields,
|
|
175
|
+
initialSort:
|
|
176
|
+
(listConfig.ui?.listView?.initialSort as CollectionSortDescriptor<string> | undefined) ?? null,
|
|
177
|
+
initialFilter: normalizeMaybeSessionFunction(listConfig.ui?.listView?.initialFilter ?? {}),
|
|
178
|
+
isSingleton: list.isSingleton,
|
|
179
|
+
|
|
180
|
+
hideNavigation: normalizeMaybeSessionFunction(listConfig.ui?.hideNavigation ?? false),
|
|
181
|
+
hideCreate: normalizeMaybeSessionFunction(
|
|
182
|
+
listConfig.ui?.hideCreate ?? !list.graphql.isEnabled.create
|
|
183
|
+
),
|
|
184
|
+
hideDelete: normalizeMaybeSessionFunction(
|
|
185
|
+
listConfig.ui?.hideDelete ?? !list.graphql.isEnabled.delete
|
|
186
|
+
),
|
|
187
|
+
|
|
188
|
+
item: null, // part of resolver
|
|
189
|
+
} satisfies ListMetaSource
|
|
190
|
+
|
|
191
|
+
adminMetaRoot.lists.push(adminMetaRoot.listsByKey[listKey])
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
let uniqueViewCount = -1
|
|
195
|
+
const stringViewsToIndex: Record<string, number> = {}
|
|
196
|
+
function getViewId(view: string) {
|
|
197
|
+
if (stringViewsToIndex[view] !== undefined) return stringViewsToIndex[view]
|
|
198
|
+
|
|
199
|
+
uniqueViewCount++
|
|
200
|
+
stringViewsToIndex[view] = uniqueViewCount
|
|
201
|
+
adminMetaRoot.views.push(view)
|
|
202
|
+
return uniqueViewCount
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
for (const [listKey, list] of Object.entries(initialisedLists)) {
|
|
206
|
+
if (omittedLists.includes(listKey)) continue
|
|
207
|
+
|
|
208
|
+
const listMeta = adminMetaRoot.listsByKey[listKey]
|
|
209
|
+
|
|
210
|
+
// populate .fields
|
|
211
|
+
for (const [fieldKey, field] of Object.entries(list.fields)) {
|
|
212
|
+
// if the field is a relationship field and is related to an omitted list, skip.
|
|
213
|
+
if (field.dbField.kind === 'relation' && omittedLists.includes(field.dbField.list)) continue
|
|
214
|
+
if (Object.values(field.graphql.isEnabled).every(x => x === false)) continue
|
|
215
|
+
assertValidView(
|
|
216
|
+
field.views,
|
|
217
|
+
`The \`views\` on the implementation of the field type at lists.${listKey}.fields.${fieldKey}`
|
|
218
|
+
)
|
|
219
|
+
|
|
220
|
+
const baseOrderFilterArgs = { fieldKey, listKey: list.listKey }
|
|
221
|
+
const isNonNull = (['read', 'create', 'update'] as const).filter(
|
|
222
|
+
operation => field.graphql.isNonNull[operation]
|
|
223
|
+
)
|
|
224
|
+
const fieldMeta = {
|
|
225
|
+
// FieldMeta
|
|
226
|
+
key: fieldKey,
|
|
227
|
+
label: field.ui.label,
|
|
228
|
+
description: field.ui.description,
|
|
229
|
+
fieldMeta: null,
|
|
230
|
+
viewsIndex: getViewId(field.views),
|
|
231
|
+
customViewsIndex:
|
|
232
|
+
field.ui.views === null
|
|
233
|
+
? null
|
|
234
|
+
: (assertValidView(field.views, `lists.${listKey}.fields.${fieldKey}.ui.views`),
|
|
235
|
+
getViewId(field.ui.views)),
|
|
236
|
+
search: list.ui.searchableFields.get(fieldKey) ?? null,
|
|
237
|
+
|
|
238
|
+
// FieldMetaSource_
|
|
239
|
+
listKey: listKey,
|
|
240
|
+
fieldKey: fieldKey,
|
|
241
|
+
isFilterable: normalizeIsOrderFilter(
|
|
242
|
+
field.input?.where ? field.graphql.isEnabled.filter : false,
|
|
243
|
+
baseOrderFilterArgs
|
|
244
|
+
),
|
|
245
|
+
isOrderable: normalizeIsOrderFilter(
|
|
246
|
+
field.input?.orderBy ? field.graphql.isEnabled.orderBy : false,
|
|
247
|
+
baseOrderFilterArgs
|
|
248
|
+
),
|
|
249
|
+
|
|
250
|
+
isNonNull,
|
|
251
|
+
createView: {
|
|
252
|
+
fieldMode: normalizeMaybeSessionFunction(field.ui.createView.fieldMode),
|
|
253
|
+
isRequired: normalizeMaybeSessionFunction(field.ui.createView.isRequired ?? false),
|
|
254
|
+
},
|
|
255
|
+
itemView: {
|
|
256
|
+
fieldMode: field.ui.itemView.fieldMode,
|
|
257
|
+
fieldPosition: field.ui.itemView.fieldPosition,
|
|
258
|
+
isRequired: field.ui.itemView.isRequired,
|
|
259
|
+
},
|
|
260
|
+
listView: {
|
|
261
|
+
fieldMode: normalizeMaybeSessionFunction(field.ui.listView.fieldMode),
|
|
262
|
+
},
|
|
263
|
+
|
|
264
|
+
item: null, // part of resolver
|
|
265
|
+
itemField: null, // part of resolver
|
|
266
|
+
} satisfies FieldMetaSource
|
|
267
|
+
|
|
268
|
+
listMeta.fields.push(fieldMeta)
|
|
269
|
+
listMeta.fieldsByKey[fieldKey] = fieldMeta
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// populate .actions
|
|
273
|
+
for (const action of list.actions) {
|
|
274
|
+
listMeta.actions.push({
|
|
275
|
+
// ActionMeta
|
|
276
|
+
key: action.actionKey,
|
|
277
|
+
label: action.ui.label,
|
|
278
|
+
icon: action.ui.icon,
|
|
279
|
+
messages: {
|
|
280
|
+
...action.ui.messages,
|
|
281
|
+
},
|
|
282
|
+
graphql: {
|
|
283
|
+
arguments: action.graphql.arguments,
|
|
284
|
+
names: action.graphql.names,
|
|
285
|
+
},
|
|
286
|
+
|
|
287
|
+
// ActionMetaSource_
|
|
288
|
+
listKey,
|
|
289
|
+
itemView: {
|
|
290
|
+
...action.ui.itemView,
|
|
291
|
+
},
|
|
292
|
+
listView: {
|
|
293
|
+
actionMode: normalizeMaybeSessionFunction(action.ui.listView.actionMode),
|
|
294
|
+
},
|
|
295
|
+
item: null, // part of resolver
|
|
296
|
+
})
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// populate .groups
|
|
300
|
+
for (const group of list.groups) {
|
|
301
|
+
listMeta.groups.push({
|
|
302
|
+
label: group.label,
|
|
303
|
+
description: group.description,
|
|
304
|
+
fields: group.fields.map(
|
|
305
|
+
fieldKey => adminMetaRoot.listsByKey[listKey].fieldsByKey[fieldKey]
|
|
306
|
+
),
|
|
307
|
+
})
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// we do this seperately to the above so that fields can check other fields to validate their config or etc.
|
|
312
|
+
// (ofc they won't necessarily be able to see other field's fieldMeta)
|
|
313
|
+
for (const [key, list] of Object.entries(initialisedLists)) {
|
|
314
|
+
if (list.graphql.isEnabled.query === false) continue
|
|
315
|
+
for (const fieldMetaSource of adminMetaRoot.listsByKey[key].fields) {
|
|
316
|
+
// if the field is a relationship field and is related to an omitted list, skip.
|
|
317
|
+
const dbField = list.fields[fieldMetaSource.fieldKey].dbField
|
|
318
|
+
if (dbField.kind === 'relation' && omittedLists.includes(dbField.list)) continue
|
|
319
|
+
|
|
320
|
+
currentAdminMeta = adminMetaRoot
|
|
321
|
+
try {
|
|
322
|
+
fieldMetaSource.fieldMeta = list.fields[fieldMetaSource.fieldKey].getAdminMeta?.() ?? null
|
|
323
|
+
} finally {
|
|
324
|
+
currentAdminMeta = undefined
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
return adminMetaRoot
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
let currentAdminMeta: undefined | AdminMetaSource
|
|
333
|
+
|
|
334
|
+
export function getAdminMetaForRelationshipField() {
|
|
335
|
+
if (currentAdminMeta) return currentAdminMeta
|
|
336
|
+
throw new Error('unexpected call to getAdminMetaInRelationshipField')
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
function assertValidView(view: string, location: string) {
|
|
340
|
+
if (view.includes('\\')) {
|
|
341
|
+
throw new Error(
|
|
342
|
+
`${location} contains a backslash, which is invalid. You need to use a module path that is resolved from where 'nixxie start' is run (see https://github.com/nixxiecms/nixxie/pull/7805)`
|
|
343
|
+
)
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
if (path.isAbsolute(view)) {
|
|
347
|
+
throw new Error(
|
|
348
|
+
`${location} is an absolute path, which is invalid. You need to use a module path that is resolved from where 'nixxie start' is run (see https://github.com/nixxiecms/nixxie/pull/7805)`
|
|
349
|
+
)
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
function normalizeMaybeSessionFunction<Return extends string | boolean | object | null | number>(
|
|
354
|
+
input: MaybeSessionFunction<Return, BaseCollectionTypeInfo>
|
|
355
|
+
): EmptyResolver<Return> {
|
|
356
|
+
if (typeof input !== 'function') return () => input
|
|
357
|
+
return (_, context) => input({ context, session: context.session })
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
function normalizeIsOrderFilter(
|
|
361
|
+
input: MaybeFieldFunction<BaseCollectionTypeInfo>,
|
|
362
|
+
baseOrderFilterArgs: {
|
|
363
|
+
listKey: string
|
|
364
|
+
fieldKey: string
|
|
365
|
+
}
|
|
366
|
+
): EmptyResolver<boolean> {
|
|
367
|
+
if (typeof input !== 'function') return () => input
|
|
368
|
+
return (_, context) => input({ context, session: context.session, ...baseOrderFilterArgs })
|
|
369
|
+
}
|