@nixxie-cms/core 1.0.0 → 1.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.
Files changed (186) hide show
  1. package/README.md +2 -2
  2. package/admin-ui/components/dist/nixxie-cms-core-admin-ui-components.cjs.js +4 -4
  3. package/admin-ui/components/dist/nixxie-cms-core-admin-ui-components.esm.js +4 -4
  4. package/admin-ui/context/dist/nixxie-cms-core-admin-ui-context.cjs.js +2 -2
  5. package/admin-ui/context/dist/nixxie-cms-core-admin-ui-context.esm.js +2 -2
  6. package/context/dist/nixxie-cms-core-context.cjs.js +2 -2
  7. package/context/dist/nixxie-cms-core-context.esm.js +2 -2
  8. package/dist/{CreateItemDialog-33335548.esm.js → CreateItemDialog-7008b050.esm.js} +1 -1
  9. package/dist/{CreateItemDialog-56cf59b7.cjs.js → CreateItemDialog-a0cab315.cjs.js} +1 -1
  10. package/dist/{PageContainer-7db73317.esm.js → PageContainer-5ae731cc.esm.js} +25 -18
  11. package/dist/{PageContainer-27c27f10.cjs.js → PageContainer-abd7159f.cjs.js} +25 -18
  12. package/dist/{admin-meta-graphql-6f7f5331.esm.js → admin-meta-graphql-0e6e606e.esm.js} +1 -1
  13. package/dist/{admin-meta-graphql-c8f926e9.cjs.js → admin-meta-graphql-306c224a.cjs.js} +1 -1
  14. package/dist/{context-3132c3ed.esm.js → context-af9957ed.esm.js} +2 -2
  15. package/dist/{context-e7a45152.cjs.js → context-b5204629.cjs.js} +2 -2
  16. package/dist/declarations/src/admin-ui/components/Navigation.d.ts.map +1 -1
  17. package/dist/declarations/src/admin-ui/components/PageContainer.d.ts.map +1 -1
  18. package/dist/declarations/src/helpers.d.ts.map +1 -1
  19. package/dist/declarations/src/index.d.ts +1 -0
  20. package/dist/declarations/src/index.d.ts.map +1 -1
  21. package/dist/declarations/src/internal-unstable/admin-ui/id-field-view.d.ts.map +1 -0
  22. package/dist/declarations/src/internal-unstable/admin-ui/pages/App/index.d.ts.map +1 -0
  23. package/dist/declarations/src/internal-unstable/admin-ui/pages/CreateItemPage/index.d.ts.map +1 -0
  24. package/dist/declarations/src/internal-unstable/admin-ui/pages/HomePage/index.d.ts.map +1 -0
  25. package/dist/declarations/src/internal-unstable/admin-ui/pages/ItemPage/index.d.ts.map +1 -0
  26. package/dist/declarations/src/internal-unstable/admin-ui/pages/ListPage/index.d.ts.map +1 -0
  27. package/dist/declarations/src/internal-unstable/admin-ui/pages/NoAccessPage/index.d.ts.map +1 -0
  28. package/dist/declarations/src/internal-unstable/artifacts.d.ts.map +1 -0
  29. package/dist/declarations/src/lib/core/initialise-lists.d.ts +1 -1
  30. package/dist/declarations/src/schema.d.ts.map +1 -1
  31. package/dist/declarations/src/types/config/index.d.ts +60 -1
  32. package/dist/declarations/src/types/config/index.d.ts.map +1 -1
  33. package/dist/declarations/src/types/config/lists.d.ts +4 -4
  34. package/dist/declarations/src/types/context.d.ts +150 -0
  35. package/dist/declarations/src/types/context.d.ts.map +1 -1
  36. package/dist/declarations/src/types/next-fields.d.ts +1 -1
  37. package/dist/{express-e9ed9a7d.cjs.js → express-455ae20c.cjs.js} +1 -1
  38. package/dist/{express-6743b918.esm.js → express-7559ca2d.esm.js} +1 -1
  39. package/dist/{index-ac01583b.cjs.js → index-89635494.cjs.js} +4 -4
  40. package/dist/{index-24b78415.esm.js → index-baa799e0.esm.js} +4 -4
  41. package/dist/nixxie-cms-core.cjs.js +104 -77
  42. package/dist/nixxie-cms-core.esm.js +104 -77
  43. package/dist/{non-null-graphql-5315718c.esm.js → non-null-graphql-a84ed64d.esm.js} +1 -1
  44. package/dist/{non-null-graphql-17b83ddc.cjs.js → non-null-graphql-add6bb3d.cjs.js} +1 -1
  45. package/dist/{resolve-hooks-66fe8a8e.cjs.js → resolve-hooks-165a9ce2.cjs.js} +1 -1
  46. package/dist/{resolve-hooks-17aafd37.esm.js → resolve-hooks-6813a045.esm.js} +2 -2
  47. package/dist/{system-dfec2f0a.esm.js → system-03e49e4f.esm.js} +8 -4
  48. package/dist/{system-48c5f6df.cjs.js → system-a321642d.cjs.js} +8 -4
  49. package/dist/{useFilter-0b5a1ee6.esm.js → useFilter-9b6db1f9.esm.js} +1 -1
  50. package/dist/{useFilter-1a4e6900.cjs.js → useFilter-acc9d413.cjs.js} +1 -1
  51. package/fields/dist/nixxie-cms-core-fields.cjs.js +16 -16
  52. package/fields/dist/nixxie-cms-core-fields.esm.js +17 -17
  53. package/fields/types/bytes/dist/nixxie-cms-core-fields-types-bytes.cjs.js +3 -3
  54. package/fields/types/bytes/dist/nixxie-cms-core-fields-types-bytes.esm.js +3 -3
  55. package/fields/types/bytes/views/dist/nixxie-cms-core-fields-types-bytes-views.cjs.js +1 -1
  56. package/fields/types/bytes/views/dist/nixxie-cms-core-fields-types-bytes-views.esm.js +1 -1
  57. package/fields/types/password/dist/nixxie-cms-core-fields-types-password.cjs.js +3 -3
  58. package/fields/types/password/dist/nixxie-cms-core-fields-types-password.esm.js +3 -3
  59. package/fields/types/relationship/views/dist/nixxie-cms-core-fields-types-relationship-views.cjs.js +4 -4
  60. package/fields/types/relationship/views/dist/nixxie-cms-core-fields-types-relationship-views.esm.js +4 -4
  61. package/fields/types/select/views/dist/nixxie-cms-core-fields-types-select-views.cjs.js +1 -1
  62. package/fields/types/select/views/dist/nixxie-cms-core-fields-types-select-views.esm.js +1 -1
  63. package/fields/types/text/views/dist/nixxie-cms-core-fields-types-text-views.cjs.js +1 -1
  64. package/fields/types/text/views/dist/nixxie-cms-core-fields-types-text-views.esm.js +1 -1
  65. package/internal-unstable/admin-ui/id-field-view/dist/nixxie-cms-core-internal-unstable-admin-ui-id-field-view.cjs.d.ts +2 -0
  66. package/internal-unstable/admin-ui/id-field-view/dist/nixxie-cms-core-internal-unstable-admin-ui-id-field-view.cjs.js +244 -0
  67. package/internal-unstable/admin-ui/id-field-view/dist/nixxie-cms-core-internal-unstable-admin-ui-id-field-view.esm.js +235 -0
  68. package/internal-unstable/admin-ui/id-field-view/package.json +4 -0
  69. package/internal-unstable/admin-ui/next-config/package.json +4 -0
  70. package/internal-unstable/admin-ui/pages/App/dist/nixxie-cms-core-internal-unstable-admin-ui-pages-App.cjs.d.ts +2 -0
  71. package/internal-unstable/admin-ui/pages/App/dist/nixxie-cms-core-internal-unstable-admin-ui-pages-App.cjs.js +59 -0
  72. package/internal-unstable/admin-ui/pages/App/dist/nixxie-cms-core-internal-unstable-admin-ui-pages-App.esm.js +55 -0
  73. package/internal-unstable/admin-ui/pages/App/package.json +4 -0
  74. package/internal-unstable/admin-ui/pages/CreateItemPage/dist/nixxie-cms-core-internal-unstable-admin-ui-pages-CreateItemPage.cjs.d.ts +2 -0
  75. package/internal-unstable/admin-ui/pages/CreateItemPage/dist/nixxie-cms-core-internal-unstable-admin-ui-pages-CreateItemPage.cjs.js +116 -0
  76. package/internal-unstable/admin-ui/pages/CreateItemPage/dist/nixxie-cms-core-internal-unstable-admin-ui-pages-CreateItemPage.esm.js +112 -0
  77. package/internal-unstable/admin-ui/pages/CreateItemPage/package.json +4 -0
  78. package/internal-unstable/admin-ui/pages/HomePage/dist/nixxie-cms-core-internal-unstable-admin-ui-pages-HomePage.cjs.d.ts +2 -0
  79. package/internal-unstable/admin-ui/pages/HomePage/dist/nixxie-cms-core-internal-unstable-admin-ui-pages-HomePage.cjs.js +336 -0
  80. package/internal-unstable/admin-ui/pages/HomePage/dist/nixxie-cms-core-internal-unstable-admin-ui-pages-HomePage.esm.js +332 -0
  81. package/internal-unstable/admin-ui/pages/HomePage/package.json +4 -0
  82. package/internal-unstable/admin-ui/pages/ItemPage/dist/nixxie-cms-core-internal-unstable-admin-ui-pages-ItemPage.cjs.d.ts +2 -0
  83. package/internal-unstable/admin-ui/pages/ItemPage/dist/nixxie-cms-core-internal-unstable-admin-ui-pages-ItemPage.cjs.js +463 -0
  84. package/internal-unstable/admin-ui/pages/ItemPage/dist/nixxie-cms-core-internal-unstable-admin-ui-pages-ItemPage.esm.js +455 -0
  85. package/internal-unstable/admin-ui/pages/ItemPage/package.json +4 -0
  86. package/internal-unstable/admin-ui/pages/ListPage/dist/nixxie-cms-core-internal-unstable-admin-ui-pages-ListPage.cjs.d.ts +2 -0
  87. package/internal-unstable/admin-ui/pages/ListPage/dist/nixxie-cms-core-internal-unstable-admin-ui-pages-ListPage.cjs.js +1195 -0
  88. package/internal-unstable/admin-ui/pages/ListPage/dist/nixxie-cms-core-internal-unstable-admin-ui-pages-ListPage.esm.js +1187 -0
  89. package/internal-unstable/admin-ui/pages/ListPage/package.json +4 -0
  90. package/internal-unstable/admin-ui/pages/NoAccessPage/dist/nixxie-cms-core-internal-unstable-admin-ui-pages-NoAccessPage.cjs.d.ts +2 -0
  91. package/internal-unstable/admin-ui/pages/NoAccessPage/dist/nixxie-cms-core-internal-unstable-admin-ui-pages-NoAccessPage.cjs.js +40 -0
  92. package/internal-unstable/admin-ui/pages/NoAccessPage/dist/nixxie-cms-core-internal-unstable-admin-ui-pages-NoAccessPage.esm.js +35 -0
  93. package/internal-unstable/admin-ui/pages/NoAccessPage/package.json +4 -0
  94. package/internal-unstable/artifacts/dist/nixxie-cms-core-internal-unstable-artifacts.cjs.d.ts +2 -0
  95. package/internal-unstable/artifacts/dist/nixxie-cms-core-internal-unstable-artifacts.cjs.js +51 -0
  96. package/internal-unstable/artifacts/dist/nixxie-cms-core-internal-unstable-artifacts.esm.js +38 -0
  97. package/internal-unstable/artifacts/package.json +4 -0
  98. package/package.json +44 -44
  99. package/scripts/cli/dist/nixxie-cms-core-scripts-cli.cjs.js +15 -15
  100. package/scripts/cli/dist/nixxie-cms-core-scripts-cli.esm.js +15 -15
  101. package/scripts/dist/nixxie-cms-core-scripts.cjs.js +3 -3
  102. package/scripts/dist/nixxie-cms-core-scripts.esm.js +3 -3
  103. package/src/admin-ui/admin-meta-graphql.ts +168 -168
  104. package/src/admin-ui/components/CommandPalette.tsx +433 -431
  105. package/src/admin-ui/components/Navigation.tsx +389 -385
  106. package/src/admin-ui/components/PageContainer.tsx +311 -310
  107. package/src/admin-ui/components/WelcomeDialog.tsx +1 -1
  108. package/src/admin-ui/context.tsx +338 -338
  109. package/src/admin-ui/templates/app.ts +60 -60
  110. package/src/admin-ui/templates/create-item.ts +5 -5
  111. package/src/admin-ui/templates/home.ts +2 -2
  112. package/src/admin-ui/templates/item.tsx +5 -5
  113. package/src/admin-ui/templates/list.tsx +5 -5
  114. package/src/admin-ui/templates/no-access.ts +7 -7
  115. package/src/fields/types/bigInt/index.ts +181 -181
  116. package/src/fields/types/bytes/index.ts +275 -275
  117. package/src/fields/types/calendarDay/index.ts +194 -194
  118. package/src/fields/types/checkbox/index.ts +76 -76
  119. package/src/fields/types/decimal/index.ts +182 -182
  120. package/src/fields/types/file/index.ts +168 -168
  121. package/src/fields/types/float/index.ts +133 -133
  122. package/src/fields/types/image/index.ts +244 -244
  123. package/src/fields/types/integer/index.ts +156 -156
  124. package/src/fields/types/json/index.ts +77 -77
  125. package/src/fields/types/multiselect/index.ts +212 -212
  126. package/src/fields/types/password/index.ts +241 -241
  127. package/src/fields/types/relationship/index.ts +381 -381
  128. package/src/fields/types/relationship/views/RelationshipTable.tsx +190 -190
  129. package/src/fields/types/select/index.ts +226 -226
  130. package/src/fields/types/text/index.ts +207 -207
  131. package/src/fields/types/timestamp/index.ts +116 -116
  132. package/src/fields/types/virtual/index.ts +108 -108
  133. package/src/helpers.ts +342 -316
  134. package/src/index.ts +4 -0
  135. package/src/{___internal-do-not-use-will-break-in-patch → internal-unstable}/admin-ui/id-field-view.tsx +167 -167
  136. package/src/{___internal-do-not-use-will-break-in-patch → internal-unstable}/admin-ui/pages/App/index.tsx +22 -22
  137. package/src/{___internal-do-not-use-will-break-in-patch → internal-unstable}/admin-ui/pages/CreateItemPage/index.tsx +71 -71
  138. package/src/{___internal-do-not-use-will-break-in-patch → internal-unstable}/admin-ui/pages/HomePage/index.tsx +333 -333
  139. package/src/{___internal-do-not-use-will-break-in-patch → internal-unstable}/admin-ui/pages/ItemPage/common.tsx +358 -358
  140. package/src/{___internal-do-not-use-will-break-in-patch → internal-unstable}/admin-ui/pages/ItemPage/index.tsx +483 -483
  141. package/src/{___internal-do-not-use-will-break-in-patch → internal-unstable}/admin-ui/pages/ListPage/FilterAdd.tsx +221 -221
  142. package/src/{___internal-do-not-use-will-break-in-patch → internal-unstable}/admin-ui/pages/ListPage/PaginationControls.tsx +170 -170
  143. package/src/{___internal-do-not-use-will-break-in-patch → internal-unstable}/admin-ui/pages/ListPage/Tag.tsx +72 -72
  144. package/src/{___internal-do-not-use-will-break-in-patch → internal-unstable}/admin-ui/pages/ListPage/index.tsx +1006 -1006
  145. package/src/{___internal-do-not-use-will-break-in-patch → internal-unstable}/admin-ui/pages/NoAccessPage/index.tsx +24 -24
  146. package/src/{___internal-do-not-use-will-break-in-patch → internal-unstable}/artifacts.ts +5 -5
  147. package/src/lib/context/createContext.ts +165 -161
  148. package/src/lib/core/initialise-lists.ts +1097 -1097
  149. package/src/lib/id-field.ts +214 -214
  150. package/src/lib/telemetry.ts +342 -342
  151. package/src/schema.ts +237 -233
  152. package/src/scripts/telemetry.ts +1 -1
  153. package/src/types/config/index.ts +400 -333
  154. package/src/types/config/lists.ts +4 -4
  155. package/src/types/context.ts +700 -530
  156. package/src/types/next-fields.ts +499 -499
  157. package/src/types/telemetry.ts +51 -51
  158. package/tests/telemetry.test.ts +361 -361
  159. package/CHANGELOG.md +0 -3158
  160. package/___internal-do-not-use-will-break-in-patch/admin-ui/id-field-view/package.json +0 -4
  161. package/___internal-do-not-use-will-break-in-patch/admin-ui/next-config/package.json +0 -4
  162. package/___internal-do-not-use-will-break-in-patch/admin-ui/pages/App/package.json +0 -4
  163. package/___internal-do-not-use-will-break-in-patch/admin-ui/pages/CreateItemPage/package.json +0 -4
  164. package/___internal-do-not-use-will-break-in-patch/admin-ui/pages/HomePage/package.json +0 -4
  165. package/___internal-do-not-use-will-break-in-patch/admin-ui/pages/ItemPage/package.json +0 -4
  166. package/___internal-do-not-use-will-break-in-patch/admin-ui/pages/ListPage/package.json +0 -4
  167. package/___internal-do-not-use-will-break-in-patch/admin-ui/pages/NoAccessPage/package.json +0 -4
  168. package/___internal-do-not-use-will-break-in-patch/artifacts/package.json +0 -4
  169. package/dist/declarations/src/___internal-do-not-use-will-break-in-patch/admin-ui/id-field-view.d.ts.map +0 -1
  170. package/dist/declarations/src/___internal-do-not-use-will-break-in-patch/admin-ui/pages/App/index.d.ts.map +0 -1
  171. package/dist/declarations/src/___internal-do-not-use-will-break-in-patch/admin-ui/pages/CreateItemPage/index.d.ts.map +0 -1
  172. package/dist/declarations/src/___internal-do-not-use-will-break-in-patch/admin-ui/pages/HomePage/index.d.ts.map +0 -1
  173. package/dist/declarations/src/___internal-do-not-use-will-break-in-patch/admin-ui/pages/ItemPage/index.d.ts.map +0 -1
  174. package/dist/declarations/src/___internal-do-not-use-will-break-in-patch/admin-ui/pages/ListPage/index.d.ts.map +0 -1
  175. package/dist/declarations/src/___internal-do-not-use-will-break-in-patch/admin-ui/pages/NoAccessPage/index.d.ts.map +0 -1
  176. package/dist/declarations/src/___internal-do-not-use-will-break-in-patch/artifacts.d.ts.map +0 -1
  177. /package/dist/{common-1a350e11.cjs.js → common-5933f758.cjs.js} +0 -0
  178. /package/dist/{common-29fc82e6.esm.js → common-ea5c441a.esm.js} +0 -0
  179. /package/dist/declarations/src/{___internal-do-not-use-will-break-in-patch → internal-unstable}/admin-ui/id-field-view.d.ts +0 -0
  180. /package/dist/declarations/src/{___internal-do-not-use-will-break-in-patch → internal-unstable}/admin-ui/pages/App/index.d.ts +0 -0
  181. /package/dist/declarations/src/{___internal-do-not-use-will-break-in-patch → internal-unstable}/admin-ui/pages/CreateItemPage/index.d.ts +0 -0
  182. /package/dist/declarations/src/{___internal-do-not-use-will-break-in-patch → internal-unstable}/admin-ui/pages/HomePage/index.d.ts +0 -0
  183. /package/dist/declarations/src/{___internal-do-not-use-will-break-in-patch → internal-unstable}/admin-ui/pages/ItemPage/index.d.ts +0 -0
  184. /package/dist/declarations/src/{___internal-do-not-use-will-break-in-patch → internal-unstable}/admin-ui/pages/ListPage/index.d.ts +0 -0
  185. /package/dist/declarations/src/{___internal-do-not-use-will-break-in-patch → internal-unstable}/admin-ui/pages/NoAccessPage/index.d.ts +0 -0
  186. /package/dist/declarations/src/{___internal-do-not-use-will-break-in-patch → internal-unstable}/artifacts.d.ts +0 -0
@@ -1,212 +1,212 @@
1
- import { g } from '../../..'
2
- import { humanize } from '../../../lib/utils'
3
- import type { JSONValue } from '../../../types'
4
- import {
5
- type BaseListTypeInfo,
6
- type CommonFieldConfig,
7
- type FieldData,
8
- fieldType,
9
- type FieldTypeFunc,
10
- } from '../../../types'
11
- import { makeValidateHook } from '../../non-null-graphql'
12
- import type { controller } from './views'
13
-
14
- type FieldTypeInfo = {
15
- item: JSONValue | null
16
- inputs: {
17
- where: never
18
- create: JSONValue | undefined
19
- update: JSONValue | undefined
20
- uniqueWhere: never
21
- orderBy: never
22
- }
23
- prisma: {
24
- create: JSONValue | undefined
25
- update: JSONValue | undefined
26
- }
27
- }
28
-
29
- export type MultiselectFieldConfig<ListTypeInfo extends BaseListTypeInfo> = CommonFieldConfig<
30
- ListTypeInfo,
31
- FieldTypeInfo
32
- > &
33
- (
34
- | {
35
- /**
36
- * When a value is provided as just a string, it will be formatted in the same way
37
- * as field labels are to create the label.
38
- */
39
- options: readonly ({ label: string; value: string } | string)[]
40
- /**
41
- * If `enum` is provided on SQLite, it will use an enum in GraphQL but a string in the database.
42
- */
43
- type?: 'string' | 'enum'
44
- defaultValue?: readonly string[] | null
45
- }
46
- | {
47
- options: readonly { label: string; value: number }[]
48
- type: 'integer'
49
- defaultValue?: readonly number[] | null
50
- }
51
- ) & {
52
- ui?: {
53
- displayMode?: 'checkboxes' | 'select'
54
- }
55
- db?: {
56
- isNullable?: boolean
57
- map?: string
58
- extendPrismaSchema?: (field: string) => string
59
- }
60
- }
61
-
62
- // these are the lowest and highest values for a signed 32-bit integer
63
- const MAX_INT = 2147483647
64
- const MIN_INT = -2147483648
65
-
66
- export function multiselect<ListTypeInfo extends BaseListTypeInfo>(
67
- config: MultiselectFieldConfig<ListTypeInfo>
68
- ): FieldTypeFunc<ListTypeInfo> {
69
- const { defaultValue: defaultValue_, ui: { displayMode = 'select', ...ui } = {} } = config
70
-
71
- config.db ??= {}
72
- config.db.isNullable ??= false // TODO: deprecated, remove in breaking change
73
- const defaultValue = config.db.isNullable ? defaultValue_ : (defaultValue_ ?? []) // TODO: deprecated, remove in breaking change?
74
-
75
- return meta => {
76
- if ((config as any).isIndexed === 'unique') {
77
- throw TypeError("isIndexed: 'unique' is not a supported option for field type multiselect")
78
- }
79
-
80
- const resolveCreate = <T extends string | number>(val: T[] | null | undefined): T[] | null => {
81
- const resolved = resolveUpdate(val)
82
- if (resolved === undefined) {
83
- return defaultValue as T[]
84
- }
85
- return resolved
86
- }
87
-
88
- const resolveUpdate = <T extends string | number>(
89
- val: T[] | null | undefined
90
- ): T[] | null | undefined => {
91
- return val
92
- }
93
-
94
- const transformedConfig = configToOptionsAndGraphQLType(config, meta)
95
-
96
- const type = g.list(g.nonNull(transformedConfig.graphqlType))
97
-
98
- const accepted = new Set(transformedConfig.options.map(x => x.value))
99
- if (accepted.size !== transformedConfig.options.length) {
100
- throw new Error(`${meta.listKey}.${meta.fieldKey} has duplicate options, this is not allowed`)
101
- }
102
-
103
- const { mode, validate } = makeValidateHook(
104
- meta,
105
- config,
106
- ({ inputData, operation, addValidationError }) => {
107
- if (operation === 'delete') return
108
-
109
- const values: readonly (string | number)[] | null | undefined = inputData[meta.fieldKey] // resolvedData is JSON
110
- if (values != null) {
111
- for (const value of values) {
112
- if (!accepted.has(value)) {
113
- addValidationError(`'${value}' is not an accepted option`)
114
- }
115
- }
116
- if (new Set(values).size !== values.length) {
117
- addValidationError(`non-unique set of options selected`)
118
- }
119
- }
120
- }
121
- )
122
-
123
- return fieldType({
124
- kind: 'scalar',
125
- scalar: 'Json',
126
- mode,
127
- map: config?.db?.map,
128
- extendPrismaSchema: config.db?.extendPrismaSchema,
129
- default:
130
- meta.provider === 'sqlite'
131
- ? undefined
132
- : {
133
- kind: 'literal',
134
- // TODO: waiting on https://github.com/prisma/prisma/issues/26571
135
- // input.create manages defaultValues anyway
136
- value: JSON.stringify(defaultValue ?? null),
137
- },
138
- })({
139
- ...config,
140
- ui,
141
- __ksTelemetryFieldTypeName: '@nixxie-cms/multiselect',
142
- hooks: {
143
- ...config.hooks,
144
- validate,
145
- },
146
- views: '@nixxie-cms/core/fields/types/multiselect/views',
147
- getAdminMeta: (): Parameters<typeof controller>[0]['fieldMeta'] => ({
148
- options: transformedConfig.options,
149
- type: config.type ?? 'string',
150
- displayMode: displayMode,
151
- defaultValue: [],
152
- }),
153
- input: {
154
- create: { arg: g.arg({ type }), resolve: resolveCreate },
155
- update: { arg: g.arg({ type }), resolve: resolveUpdate },
156
- },
157
- output: g.field({
158
- type: type,
159
- resolve({ value }) {
160
- return value as any
161
- },
162
- }),
163
- })
164
- }
165
- }
166
-
167
- function configToOptionsAndGraphQLType(
168
- config: MultiselectFieldConfig<BaseListTypeInfo>,
169
- meta: FieldData
170
- ) {
171
- if (config.type === 'integer') {
172
- if (
173
- config.options.some(
174
- ({ value }) => !Number.isInteger(value) || value > MAX_INT || value < MIN_INT
175
- )
176
- ) {
177
- throw new Error(
178
- `${meta.listKey}.${meta.fieldKey} specifies integer values that are outside the range of a 32-bit signed integer`
179
- )
180
- }
181
- return {
182
- graphqlType: g.Int,
183
- options: config.options,
184
- }
185
- }
186
-
187
- const options = config.options.map(option => {
188
- if (typeof option === 'string') {
189
- return {
190
- label: humanize(option),
191
- value: option,
192
- }
193
- }
194
- return option
195
- })
196
-
197
- if (config.type === 'enum') {
198
- const enumName = `${meta.listKey}${humanize(meta.fieldKey).replace(/ /g, '')}Type`
199
- const graphqlType = g.enum({
200
- name: enumName,
201
- values: g.enumValues(options.map(x => x.value)),
202
- })
203
- return {
204
- graphqlType,
205
- options,
206
- }
207
- }
208
- return {
209
- graphqlType: g.String,
210
- options,
211
- }
212
- }
1
+ import { g } from '../../..'
2
+ import { humanize } from '../../../lib/utils'
3
+ import type { JSONValue } from '../../../types'
4
+ import {
5
+ type BaseListTypeInfo,
6
+ type CommonFieldConfig,
7
+ type FieldData,
8
+ fieldType,
9
+ type FieldTypeFunc,
10
+ } from '../../../types'
11
+ import { makeValidateHook } from '../../non-null-graphql'
12
+ import type { controller } from './views'
13
+
14
+ type FieldTypeInfo = {
15
+ item: JSONValue | null
16
+ inputs: {
17
+ where: never
18
+ create: JSONValue | undefined
19
+ update: JSONValue | undefined
20
+ uniqueWhere: never
21
+ orderBy: never
22
+ }
23
+ prisma: {
24
+ create: JSONValue | undefined
25
+ update: JSONValue | undefined
26
+ }
27
+ }
28
+
29
+ export type MultiselectFieldConfig<ListTypeInfo extends BaseListTypeInfo> = CommonFieldConfig<
30
+ ListTypeInfo,
31
+ FieldTypeInfo
32
+ > &
33
+ (
34
+ | {
35
+ /**
36
+ * When a value is provided as just a string, it will be formatted in the same way
37
+ * as field labels are to create the label.
38
+ */
39
+ options: readonly ({ label: string; value: string } | string)[]
40
+ /**
41
+ * If `enum` is provided on SQLite, it will use an enum in GraphQL but a string in the database.
42
+ */
43
+ type?: 'string' | 'enum'
44
+ defaultValue?: readonly string[] | null
45
+ }
46
+ | {
47
+ options: readonly { label: string; value: number }[]
48
+ type: 'integer'
49
+ defaultValue?: readonly number[] | null
50
+ }
51
+ ) & {
52
+ ui?: {
53
+ displayMode?: 'checkboxes' | 'select'
54
+ }
55
+ db?: {
56
+ isNullable?: boolean
57
+ map?: string
58
+ extendPrismaSchema?: (field: string) => string
59
+ }
60
+ }
61
+
62
+ // these are the lowest and highest values for a signed 32-bit integer
63
+ const MAX_INT = 2147483647
64
+ const MIN_INT = -2147483648
65
+
66
+ export function multiselect<ListTypeInfo extends BaseListTypeInfo>(
67
+ config: MultiselectFieldConfig<ListTypeInfo>
68
+ ): FieldTypeFunc<ListTypeInfo> {
69
+ const { defaultValue: defaultValue_, ui: { displayMode = 'select', ...ui } = {} } = config
70
+
71
+ config.db ??= {}
72
+ config.db.isNullable ??= false // TODO: deprecated, remove in breaking change
73
+ const defaultValue = config.db.isNullable ? defaultValue_ : (defaultValue_ ?? []) // TODO: deprecated, remove in breaking change?
74
+
75
+ return meta => {
76
+ if ((config as any).isIndexed === 'unique') {
77
+ throw TypeError("isIndexed: 'unique' is not a supported option for field type multiselect")
78
+ }
79
+
80
+ const resolveCreate = <T extends string | number>(val: T[] | null | undefined): T[] | null => {
81
+ const resolved = resolveUpdate(val)
82
+ if (resolved === undefined) {
83
+ return defaultValue as T[]
84
+ }
85
+ return resolved
86
+ }
87
+
88
+ const resolveUpdate = <T extends string | number>(
89
+ val: T[] | null | undefined
90
+ ): T[] | null | undefined => {
91
+ return val
92
+ }
93
+
94
+ const transformedConfig = configToOptionsAndGraphQLType(config, meta)
95
+
96
+ const type = g.list(g.nonNull(transformedConfig.graphqlType))
97
+
98
+ const accepted = new Set(transformedConfig.options.map(x => x.value))
99
+ if (accepted.size !== transformedConfig.options.length) {
100
+ throw new Error(`${meta.listKey}.${meta.fieldKey} has duplicate options, this is not allowed`)
101
+ }
102
+
103
+ const { mode, validate } = makeValidateHook(
104
+ meta,
105
+ config,
106
+ ({ inputData, operation, addValidationError }) => {
107
+ if (operation === 'delete') return
108
+
109
+ const values: readonly (string | number)[] | null | undefined = inputData[meta.fieldKey] // resolvedData is JSON
110
+ if (values != null) {
111
+ for (const value of values) {
112
+ if (!accepted.has(value)) {
113
+ addValidationError(`'${value}' is not an accepted option`)
114
+ }
115
+ }
116
+ if (new Set(values).size !== values.length) {
117
+ addValidationError(`non-unique set of options selected`)
118
+ }
119
+ }
120
+ }
121
+ )
122
+
123
+ return fieldType({
124
+ kind: 'scalar',
125
+ scalar: 'Json',
126
+ mode,
127
+ map: config?.db?.map,
128
+ extendPrismaSchema: config.db?.extendPrismaSchema,
129
+ default:
130
+ meta.provider === 'sqlite'
131
+ ? undefined
132
+ : {
133
+ kind: 'literal',
134
+ // TODO: waiting on https://github.com/prisma/prisma/issues/26571
135
+ // input.create manages defaultValues anyway
136
+ value: JSON.stringify(defaultValue ?? null),
137
+ },
138
+ })({
139
+ ...config,
140
+ ui,
141
+ __nxTelemetryFieldTypeName: '@nixxie-cms/multiselect',
142
+ hooks: {
143
+ ...config.hooks,
144
+ validate,
145
+ },
146
+ views: '@nixxie-cms/core/fields/types/multiselect/views',
147
+ getAdminMeta: (): Parameters<typeof controller>[0]['fieldMeta'] => ({
148
+ options: transformedConfig.options,
149
+ type: config.type ?? 'string',
150
+ displayMode: displayMode,
151
+ defaultValue: [],
152
+ }),
153
+ input: {
154
+ create: { arg: g.arg({ type }), resolve: resolveCreate },
155
+ update: { arg: g.arg({ type }), resolve: resolveUpdate },
156
+ },
157
+ output: g.field({
158
+ type: type,
159
+ resolve({ value }) {
160
+ return value as any
161
+ },
162
+ }),
163
+ })
164
+ }
165
+ }
166
+
167
+ function configToOptionsAndGraphQLType(
168
+ config: MultiselectFieldConfig<BaseListTypeInfo>,
169
+ meta: FieldData
170
+ ) {
171
+ if (config.type === 'integer') {
172
+ if (
173
+ config.options.some(
174
+ ({ value }) => !Number.isInteger(value) || value > MAX_INT || value < MIN_INT
175
+ )
176
+ ) {
177
+ throw new Error(
178
+ `${meta.listKey}.${meta.fieldKey} specifies integer values that are outside the range of a 32-bit signed integer`
179
+ )
180
+ }
181
+ return {
182
+ graphqlType: g.Int,
183
+ options: config.options,
184
+ }
185
+ }
186
+
187
+ const options = config.options.map(option => {
188
+ if (typeof option === 'string') {
189
+ return {
190
+ label: humanize(option),
191
+ value: option,
192
+ }
193
+ }
194
+ return option
195
+ })
196
+
197
+ if (config.type === 'enum') {
198
+ const enumName = `${meta.listKey}${humanize(meta.fieldKey).replace(/ /g, '')}Type`
199
+ const graphqlType = g.enum({
200
+ name: enumName,
201
+ values: g.enumValues(options.map(x => x.value)),
202
+ })
203
+ return {
204
+ graphqlType,
205
+ options,
206
+ }
207
+ }
208
+ return {
209
+ graphqlType: g.String,
210
+ options,
211
+ }
212
+ }