@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.
Files changed (203) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/CHANGES-1.1.md +134 -0
  3. package/context/dist/nixxie-cms-core-context.cjs.js +4 -3
  4. package/context/dist/nixxie-cms-core-context.esm.js +3 -2
  5. package/dist/declarations/src/access.d.ts +2 -2
  6. package/dist/declarations/src/access.d.ts.map +1 -1
  7. package/dist/declarations/src/admin-ui/components/Navigation.d.ts +2 -2
  8. package/dist/declarations/src/admin-ui/components/Navigation.d.ts.map +1 -1
  9. package/dist/declarations/src/admin-ui/context.d.ts +6 -6
  10. package/dist/declarations/src/admin-ui/context.d.ts.map +1 -1
  11. package/dist/declarations/src/admin-ui/utils/Fields.d.ts +3 -3
  12. package/dist/declarations/src/admin-ui/utils/Fields.d.ts.map +1 -1
  13. package/dist/declarations/src/admin-ui/utils/filters.d.ts +5 -5
  14. package/dist/declarations/src/admin-ui/utils/filters.d.ts.map +1 -1
  15. package/dist/declarations/src/admin-ui/utils/useCreateItem.d.ts +3 -3
  16. package/dist/declarations/src/admin-ui/utils/useCreateItem.d.ts.map +1 -1
  17. package/dist/declarations/src/admin-ui/utils/utils.d.ts +2 -2
  18. package/dist/declarations/src/admin-ui/utils/utils.d.ts.map +1 -1
  19. package/dist/declarations/src/context.d.ts +1 -1
  20. package/dist/declarations/src/context.d.ts.map +1 -1
  21. package/dist/declarations/src/fields/types/bigInt/index.d.ts +3 -3
  22. package/dist/declarations/src/fields/types/bigInt/index.d.ts.map +1 -1
  23. package/dist/declarations/src/fields/types/bytes/index.d.ts +3 -3
  24. package/dist/declarations/src/fields/types/bytes/index.d.ts.map +1 -1
  25. package/dist/declarations/src/fields/types/calendarDay/index.d.ts +3 -3
  26. package/dist/declarations/src/fields/types/calendarDay/index.d.ts.map +1 -1
  27. package/dist/declarations/src/fields/types/checkbox/index.d.ts +3 -3
  28. package/dist/declarations/src/fields/types/checkbox/index.d.ts.map +1 -1
  29. package/dist/declarations/src/fields/types/decimal/index.d.ts +3 -3
  30. package/dist/declarations/src/fields/types/decimal/index.d.ts.map +1 -1
  31. package/dist/declarations/src/fields/types/file/index.d.ts +4 -4
  32. package/dist/declarations/src/fields/types/file/index.d.ts.map +1 -1
  33. package/dist/declarations/src/fields/types/float/index.d.ts +3 -3
  34. package/dist/declarations/src/fields/types/float/index.d.ts.map +1 -1
  35. package/dist/declarations/src/fields/types/image/index.d.ts +4 -4
  36. package/dist/declarations/src/fields/types/image/index.d.ts.map +1 -1
  37. package/dist/declarations/src/fields/types/integer/index.d.ts +3 -3
  38. package/dist/declarations/src/fields/types/integer/index.d.ts.map +1 -1
  39. package/dist/declarations/src/fields/types/json/index.d.ts +3 -3
  40. package/dist/declarations/src/fields/types/json/index.d.ts.map +1 -1
  41. package/dist/declarations/src/fields/types/multiselect/index.d.ts +3 -3
  42. package/dist/declarations/src/fields/types/multiselect/index.d.ts.map +1 -1
  43. package/dist/declarations/src/fields/types/multiselect/views/index.d.ts.map +1 -1
  44. package/dist/declarations/src/fields/types/password/index.d.ts +3 -3
  45. package/dist/declarations/src/fields/types/password/index.d.ts.map +1 -1
  46. package/dist/declarations/src/fields/types/relationship/index.d.ts +8 -8
  47. package/dist/declarations/src/fields/types/relationship/index.d.ts.map +1 -1
  48. package/dist/declarations/src/fields/types/relationship/views/ComboboxMany.d.ts +3 -3
  49. package/dist/declarations/src/fields/types/relationship/views/ComboboxMany.d.ts.map +1 -1
  50. package/dist/declarations/src/fields/types/relationship/views/ComboboxSingle.d.ts +3 -3
  51. package/dist/declarations/src/fields/types/relationship/views/ComboboxSingle.d.ts.map +1 -1
  52. package/dist/declarations/src/fields/types/relationship/views/index.d.ts +3 -3
  53. package/dist/declarations/src/fields/types/relationship/views/index.d.ts.map +1 -1
  54. package/dist/declarations/src/fields/types/relationship/views/types.d.ts +3 -3
  55. package/dist/declarations/src/fields/types/relationship/views/types.d.ts.map +1 -1
  56. package/dist/declarations/src/fields/types/select/index.d.ts +3 -3
  57. package/dist/declarations/src/fields/types/select/index.d.ts.map +1 -1
  58. package/dist/declarations/src/fields/types/text/index.d.ts +3 -3
  59. package/dist/declarations/src/fields/types/text/index.d.ts.map +1 -1
  60. package/dist/declarations/src/fields/types/timestamp/index.d.ts +3 -3
  61. package/dist/declarations/src/fields/types/timestamp/index.d.ts.map +1 -1
  62. package/dist/declarations/src/fields/types/virtual/index.d.ts +7 -7
  63. package/dist/declarations/src/fields/types/virtual/index.d.ts.map +1 -1
  64. package/dist/declarations/src/helpers.d.ts +249 -13
  65. package/dist/declarations/src/helpers.d.ts.map +1 -1
  66. package/dist/declarations/src/index.d.ts +9 -4
  67. package/dist/declarations/src/index.d.ts.map +1 -1
  68. package/dist/declarations/src/internal-unstable/admin-ui/pages/ListPage/index.d.ts.map +1 -1
  69. package/dist/declarations/src/lib/admin-meta.d.ts +11 -11
  70. package/dist/declarations/src/lib/admin-meta.d.ts.map +1 -1
  71. package/dist/declarations/src/lib/core/access-control.d.ts +18 -18
  72. package/dist/declarations/src/lib/core/access-control.d.ts.map +1 -1
  73. package/dist/declarations/src/lib/core/cascade.d.ts +47 -0
  74. package/dist/declarations/src/lib/core/cascade.d.ts.map +1 -0
  75. package/dist/declarations/src/lib/core/initialise-lists.d.ts +27 -24
  76. package/dist/declarations/src/lib/core/initialise-lists.d.ts.map +1 -1
  77. package/dist/declarations/src/lib/env.d.ts +9 -0
  78. package/dist/declarations/src/lib/env.d.ts.map +1 -0
  79. package/dist/declarations/src/lib/system.d.ts +1 -1
  80. package/dist/declarations/src/lib/system.d.ts.map +1 -1
  81. package/dist/declarations/src/list-features.d.ts +162 -0
  82. package/dist/declarations/src/list-features.d.ts.map +1 -0
  83. package/dist/declarations/src/schema.d.ts +24 -23
  84. package/dist/declarations/src/schema.d.ts.map +1 -1
  85. package/dist/declarations/src/session.d.ts +75 -0
  86. package/dist/declarations/src/session.d.ts.map +1 -1
  87. package/dist/declarations/src/types/admin-meta.d.ts +11 -11
  88. package/dist/declarations/src/types/admin-meta.d.ts.map +1 -1
  89. package/dist/declarations/src/types/config/access-control.d.ts +42 -42
  90. package/dist/declarations/src/types/config/access-control.d.ts.map +1 -1
  91. package/dist/declarations/src/types/config/fields.d.ts +19 -19
  92. package/dist/declarations/src/types/config/fields.d.ts.map +1 -1
  93. package/dist/declarations/src/types/config/hooks.d.ts +131 -131
  94. package/dist/declarations/src/types/config/hooks.d.ts.map +1 -1
  95. package/dist/declarations/src/types/config/index.d.ts +190 -8
  96. package/dist/declarations/src/types/config/index.d.ts.map +1 -1
  97. package/dist/declarations/src/types/config/lists.d.ts +146 -108
  98. package/dist/declarations/src/types/config/lists.d.ts.map +1 -1
  99. package/dist/declarations/src/types/context.d.ts +507 -47
  100. package/dist/declarations/src/types/context.d.ts.map +1 -1
  101. package/dist/declarations/src/types/next-fields.d.ts +28 -28
  102. package/dist/declarations/src/types/next-fields.d.ts.map +1 -1
  103. package/dist/declarations/src/types/type-info.d.ts +3 -3
  104. package/dist/declarations/src/types/type-info.d.ts.map +1 -1
  105. package/dist/{express-455ae20c.cjs.js → express-84d534c2.cjs.js} +6 -6
  106. package/dist/{express-7559ca2d.esm.js → express-d0a4ce99.esm.js} +6 -6
  107. package/dist/{index-15c8f81e.esm.js → index-5d8b0b4e.esm.js} +363 -183
  108. package/dist/index-6055753b.cjs.js +393 -0
  109. package/dist/{index-42045902.cjs.js → index-ac29f382.cjs.js} +363 -185
  110. package/dist/index-f1703b7b.esm.js +386 -0
  111. package/dist/nixxie-cms-core.cjs.js +1388 -30
  112. package/dist/nixxie-cms-core.esm.js +1362 -24
  113. package/dist/{non-null-graphql-add6bb3d.cjs.js → non-null-graphql-4a44c122.cjs.js} +1 -1
  114. package/dist/{non-null-graphql-a84ed64d.esm.js → non-null-graphql-8c5feaae.esm.js} +1 -1
  115. package/dist/{resolve-hooks-165a9ce2.cjs.js → resolve-hooks-10a5f84c.cjs.js} +240 -6
  116. package/dist/{resolve-hooks-6813a045.esm.js → resolve-hooks-9e676794.esm.js} +238 -7
  117. package/dist/{system-a321642d.cjs.js → system-6b37a5f8.cjs.js} +33 -7
  118. package/dist/{system-03e49e4f.esm.js → system-e591d821.esm.js} +33 -7
  119. package/fields/dist/nixxie-cms-core-fields.cjs.js +29 -576
  120. package/fields/dist/nixxie-cms-core-fields.esm.js +18 -565
  121. package/fields/types/bytes/dist/nixxie-cms-core-fields-types-bytes.cjs.js +4 -2
  122. package/fields/types/bytes/dist/nixxie-cms-core-fields-types-bytes.esm.js +4 -2
  123. package/fields/types/multiselect/views/dist/nixxie-cms-core-fields-types-multiselect-views.cjs.js +1 -6
  124. package/fields/types/multiselect/views/dist/nixxie-cms-core-fields-types-multiselect-views.esm.js +1 -6
  125. package/fields/types/password/dist/nixxie-cms-core-fields-types-password.cjs.js +4 -2
  126. package/fields/types/password/dist/nixxie-cms-core-fields-types-password.esm.js +4 -2
  127. package/internal-unstable/artifacts/dist/nixxie-cms-core-internal-unstable-artifacts.cjs.js +4 -3
  128. package/internal-unstable/artifacts/dist/nixxie-cms-core-internal-unstable-artifacts.esm.js +4 -3
  129. package/package.json +4 -4
  130. package/scripts/cli/dist/nixxie-cms-core-scripts-cli.cjs.js +4 -3
  131. package/scripts/cli/dist/nixxie-cms-core-scripts-cli.esm.js +4 -3
  132. package/scripts/dist/nixxie-cms-core-scripts.cjs.js +4 -3
  133. package/scripts/dist/nixxie-cms-core-scripts.esm.js +4 -3
  134. package/session/dist/nixxie-cms-core-session.cjs.js +286 -0
  135. package/session/dist/nixxie-cms-core-session.esm.js +279 -1
  136. package/src/access.ts +25 -25
  137. package/src/admin-ui/admin-meta-graphql.ts +5 -5
  138. package/src/admin-ui/components/CreateButtonLink.tsx +46 -46
  139. package/src/admin-ui/components/Navigation.tsx +3 -3
  140. package/src/admin-ui/context.tsx +6 -6
  141. package/src/admin-ui/utils/Fields.tsx +241 -241
  142. package/src/admin-ui/utils/actionData.ts +36 -36
  143. package/src/admin-ui/utils/filters.ts +148 -148
  144. package/src/admin-ui/utils/useCreateItem.ts +171 -171
  145. package/src/admin-ui/utils/utils.tsx +127 -127
  146. package/src/context.ts +1 -1
  147. package/src/fields/non-null-graphql.ts +115 -115
  148. package/src/fields/types/bigInt/index.ts +6 -6
  149. package/src/fields/types/bytes/index.ts +6 -6
  150. package/src/fields/types/calendarDay/index.ts +18 -19
  151. package/src/fields/types/checkbox/index.ts +6 -6
  152. package/src/fields/types/decimal/index.ts +6 -6
  153. package/src/fields/types/file/index.ts +8 -8
  154. package/src/fields/types/float/index.ts +6 -6
  155. package/src/fields/types/image/index.ts +8 -8
  156. package/src/fields/types/integer/index.ts +6 -6
  157. package/src/fields/types/json/index.ts +5 -5
  158. package/src/fields/types/multiselect/index.ts +7 -7
  159. package/src/fields/types/multiselect/views/index.tsx +149 -151
  160. package/src/fields/types/password/index.ts +6 -6
  161. package/src/fields/types/relationship/index.ts +13 -13
  162. package/src/fields/types/relationship/views/ComboboxMany.tsx +110 -110
  163. package/src/fields/types/relationship/views/ComboboxSingle.tsx +115 -115
  164. package/src/fields/types/relationship/views/ContextualActions.tsx +139 -139
  165. package/src/fields/types/relationship/views/index.tsx +492 -492
  166. package/src/fields/types/relationship/views/types.ts +46 -46
  167. package/src/fields/types/relationship/views/useApolloQuery.ts +185 -185
  168. package/src/fields/types/relationship/views/useFilter.tsx +109 -109
  169. package/src/fields/types/select/index.ts +6 -6
  170. package/src/fields/types/text/index.ts +6 -6
  171. package/src/fields/types/timestamp/index.ts +23 -21
  172. package/src/fields/types/virtual/index.ts +11 -11
  173. package/src/helpers.ts +773 -42
  174. package/src/index.ts +66 -24
  175. package/src/internal-unstable/admin-ui/pages/ItemPage/common.tsx +4 -4
  176. package/src/internal-unstable/admin-ui/pages/ItemPage/index.tsx +5 -5
  177. package/src/internal-unstable/admin-ui/pages/ListPage/index.tsx +8 -8
  178. package/src/lib/admin-meta.ts +369 -369
  179. package/src/lib/context/createContext.ts +6 -0
  180. package/src/lib/core/access-control.ts +434 -434
  181. package/src/lib/core/cascade.ts +236 -0
  182. package/src/lib/core/initialise-lists.ts +49 -33
  183. package/src/lib/core/mutations/index.ts +7 -0
  184. package/src/lib/core/mutations/nested-mutation-many-input-resolvers.ts +145 -145
  185. package/src/lib/core/mutations/nested-mutation-one-input-resolvers.ts +71 -71
  186. package/src/lib/core/queries/output-field.ts +178 -178
  187. package/src/lib/env.ts +50 -0
  188. package/src/lib/id-field.ts +2 -2
  189. package/src/lib/system.ts +221 -207
  190. package/src/lib/typescript-schema-printer.ts +227 -227
  191. package/src/list-features.ts +476 -0
  192. package/src/schema.ts +92 -22
  193. package/src/session.ts +225 -0
  194. package/src/types/admin-meta.ts +218 -218
  195. package/src/types/config/access-control.ts +186 -186
  196. package/src/types/config/fields.ts +96 -96
  197. package/src/types/config/hooks.ts +529 -529
  198. package/src/types/config/index.ts +206 -7
  199. package/src/types/config/lists.ts +606 -565
  200. package/src/types/context.ts +592 -55
  201. package/src/types/next-fields.ts +31 -31
  202. package/src/types/type-info.ts +38 -38
  203. package/src/types/type-tests.ts +21 -21
package/src/lib/system.ts CHANGED
@@ -1,207 +1,221 @@
1
- import { randomBytes } from 'node:crypto'
2
- import path from 'node:path'
3
-
4
- import type { BaseNixxieTypeInfo, NixxieConfig, NixxieContext } from '../types'
5
- import { createAdminMeta } from './admin-meta'
6
- import { createContext } from './context/createContext'
7
- import { initialiseLists, type InitialisedList } from './core/initialise-lists'
8
- import { createGraphQLSchema } from './graphql'
9
-
10
- // TODO: this cannot be changed for now, circular dependency with getSystemPaths, getEsbuildConfig
11
- export function getBuiltNixxieConfigurationPath(cwd: string) {
12
- return path.join(cwd, '.nixxie/config.js')
13
- }
14
-
15
- function posixify(s: string) {
16
- return s.split(path.sep).join('/')
17
- }
18
-
19
- function getSystemPaths(cwd: string, config: NixxieConfig) {
20
- const prismaClientPath =
21
- config.db.prismaClientPath === '@prisma/client'
22
- ? null
23
- : config.db.prismaClientPath
24
- ? path.join(cwd, config.db.prismaClientPath)
25
- : null
26
-
27
- const builtTypesPath = config.types?.path
28
- ? path.join(cwd, config.types.path) // TODO: enforce initConfig before getSystemPaths
29
- : path.join(cwd, 'node_modules/.nixxie/types.ts')
30
-
31
- const builtPrismaPath = config.db?.prismaSchemaPath
32
- ? path.join(cwd, config.db.prismaSchemaPath) // TODO: enforce initConfig before getSystemPaths
33
- : path.join(cwd, 'schema.prisma')
34
-
35
- const relativePrismaPath = prismaClientPath
36
- ? `./${posixify(path.relative(path.dirname(builtTypesPath), prismaClientPath))}`
37
- : '@prisma/client'
38
-
39
- const builtGraphqlPath = config.graphql?.schemaPath
40
- ? path.join(cwd, config.graphql.schemaPath) // TODO: enforce initConfig before getSystemPaths
41
- : path.join(cwd, 'schema.graphql')
42
-
43
- return {
44
- config: getBuiltNixxieConfigurationPath(cwd),
45
- admin: path.join(cwd, '.nixxie/admin'),
46
- prisma: prismaClientPath ?? '@prisma/client',
47
- types: {
48
- relativePrismaPath,
49
- },
50
- schema: {
51
- types: builtTypesPath,
52
- prisma: builtPrismaPath,
53
- graphql: builtGraphqlPath,
54
- },
55
- }
56
- }
57
-
58
- function getInternalGraphQLSchema(config: NixxieConfig) {
59
- // omit `graphql.omit`
60
- const withoutOmit: NixxieConfig = {
61
- ...config,
62
- lists: Object.fromEntries(
63
- Object.entries(config.lists).map(([listKey, list]) => {
64
- return [
65
- listKey,
66
- {
67
- ...list,
68
- graphql: { ...(list.graphql || {}), omit: false },
69
- fields: Object.fromEntries(
70
- Object.entries(list.fields).map(([fieldKey, field]) => {
71
- if (fieldKey.startsWith('__group')) return [fieldKey, field]
72
- return [
73
- fieldKey,
74
- data => {
75
- const f = field(data)
76
- return {
77
- ...f,
78
- graphql: { ...(f.graphql || {}), omit: false },
79
-
80
- // WARNING: this bypasses access control checks for filtering and ordering on the internal schema
81
- isFilterable: true, // TODO: remove when moved to .access.filter and .omit.filter
82
- isOrderable: true, // TODO: remove when moved to .access.filter and .omit.filter
83
- }
84
- },
85
- ]
86
- })
87
- ),
88
- },
89
- ]
90
- })
91
- ),
92
- }
93
-
94
- const lists = initialiseLists(withoutOmit)
95
- const adminMeta = createAdminMeta(withoutOmit, lists)
96
- return createGraphQLSchema(withoutOmit, lists, adminMeta, true)
97
- }
98
-
99
- function injectNewDefaults(prismaClient: unknown, lists: Record<string, InitialisedList>) {
100
- for (const listKey in lists) {
101
- const list = lists[listKey]
102
-
103
- // TODO: other fields might use 'random' too
104
- const { dbField } = list.fields.id
105
-
106
- if ('default' in dbField && dbField.default?.kind === 'random') {
107
- const { bytes, encoding } = dbField.default
108
-
109
- prismaClient = (prismaClient as any).$extends({
110
- query: {
111
- [list.prisma.listKey]: {
112
- async create({ args, query }: any) {
113
- return query({
114
- ...args,
115
- data: {
116
- ...args.data,
117
- id: args.data.id ?? randomBytes(bytes).toString(encoding),
118
- },
119
- })
120
- },
121
- },
122
- },
123
- })
124
- }
125
- }
126
-
127
- return prismaClient
128
- }
129
-
130
- function formatUrl(provider: NixxieConfig['db']['provider'], url: string) {
131
- if (url.startsWith('file:')) {
132
- const parsed = new URL(url)
133
- if (provider === 'sqlite' && !parsed.searchParams.get('connection_limit')) {
134
- // https://github.com/prisma/prisma/issues/9562
135
- // https://github.com/prisma/prisma/issues/10403
136
- // https://github.com/prisma/prisma/issues/11789
137
- parsed.searchParams.set('connection_limit', '1')
138
-
139
- const [uri] = url.split('?')
140
- return `${uri}?${parsed.search}`
141
- }
142
- }
143
-
144
- return url
145
- }
146
-
147
- export function createSystem(config: NixxieConfig) {
148
- const lists = initialiseLists(config)
149
- const adminMeta = createAdminMeta(config, lists)
150
- const graphQLSchemas = {
151
- public: createGraphQLSchema(config, lists, adminMeta, false),
152
- internal: getInternalGraphQLSchema(config),
153
- }
154
-
155
- return {
156
- config,
157
- graphql: {
158
- schemas: graphQLSchemas,
159
- },
160
- adminMeta,
161
- lists,
162
- getPaths: (cwd: string) => getSystemPaths(cwd, config),
163
-
164
- getNixxie: (PM: any) => {
165
- const prePrismaClient = new PM.PrismaClient({
166
- datasourceUrl: formatUrl(config.db.provider, config.db.url),
167
- log: config.db.enableLogging,
168
- })
169
-
170
- const prismaClient = config.db.extendPrismaClient(injectNewDefaults(prePrismaClient, lists))
171
- const context = createContext({
172
- config,
173
- lists,
174
- graphQLSchemas,
175
- prismaClient,
176
- prismaTypes: {
177
- DbNull: PM.Prisma.DbNull,
178
- JsonNull: PM.Prisma.JsonNull,
179
- },
180
- })
181
-
182
- return {
183
- // TODO: replace with server.onStart, remove in breaking change
184
- async connect() {
185
- await (prismaClient as any).$connect()
186
- await config.db.onConnect?.(context)
187
- },
188
- // TODO: only used by tests, remove in breaking change
189
- async disconnect() {
190
- await (prismaClient as any).$disconnect()
191
- },
192
- context,
193
- }
194
- },
195
- }
196
- }
197
-
198
- export type System = ReturnType<typeof createSystem>
199
-
200
- export function getContext<TypeInfo extends BaseNixxieTypeInfo>(
201
- config: NixxieConfig<TypeInfo>,
202
- PrismaModule: unknown
203
- ): NixxieContext<TypeInfo> {
204
- const system = createSystem(config)
205
- const { context } = system.getNixxie(PrismaModule)
206
- return context
207
- }
1
+ import { randomBytes } from 'node:crypto'
2
+ import path from 'node:path'
3
+
4
+ import type { BaseNixxieTypeInfo, NixxieConfig, NixxieContext } from '../types'
5
+ import { createAdminMeta } from './admin-meta'
6
+ import { createContext } from './context/createContext'
7
+ import { initialiseLists, type InitialisedList } from './core/initialise-lists'
8
+ import { createGraphQLSchema } from './graphql'
9
+
10
+ // TODO: this cannot be changed for now, circular dependency with getSystemPaths, getEsbuildConfig
11
+ export function getBuiltNixxieConfigurationPath(cwd: string) {
12
+ return path.join(cwd, '.nixxie/config.js')
13
+ }
14
+
15
+ function posixify(s: string) {
16
+ return s.split(path.sep).join('/')
17
+ }
18
+
19
+ function getSystemPaths(cwd: string, config: NixxieConfig) {
20
+ const prismaClientPath =
21
+ config.db.prismaClientPath === '@prisma/client'
22
+ ? null
23
+ : config.db.prismaClientPath
24
+ ? path.join(cwd, config.db.prismaClientPath)
25
+ : null
26
+
27
+ const builtTypesPath = config.types?.path
28
+ ? path.join(cwd, config.types.path) // TODO: enforce initConfig before getSystemPaths
29
+ : path.join(cwd, 'node_modules/.nixxie/types.ts')
30
+
31
+ const builtPrismaPath = config.db?.prismaSchemaPath
32
+ ? path.join(cwd, config.db.prismaSchemaPath) // TODO: enforce initConfig before getSystemPaths
33
+ : path.join(cwd, 'schema.prisma')
34
+
35
+ const relativePrismaPath = prismaClientPath
36
+ ? `./${posixify(path.relative(path.dirname(builtTypesPath), prismaClientPath))}`
37
+ : '@prisma/client'
38
+
39
+ const builtGraphqlPath = config.graphql?.schemaPath
40
+ ? path.join(cwd, config.graphql.schemaPath) // TODO: enforce initConfig before getSystemPaths
41
+ : path.join(cwd, 'schema.graphql')
42
+
43
+ return {
44
+ config: getBuiltNixxieConfigurationPath(cwd),
45
+ admin: path.join(cwd, '.nixxie/admin'),
46
+ prisma: prismaClientPath ?? '@prisma/client',
47
+ types: {
48
+ relativePrismaPath,
49
+ },
50
+ schema: {
51
+ types: builtTypesPath,
52
+ prisma: builtPrismaPath,
53
+ graphql: builtGraphqlPath,
54
+ },
55
+ }
56
+ }
57
+
58
+ function getInternalGraphQLSchema(config: NixxieConfig) {
59
+ // omit `graphql.omit`
60
+ const withoutOmit: NixxieConfig = {
61
+ ...config,
62
+ lists: Object.fromEntries(
63
+ Object.entries(config.lists).map(([listKey, list]) => {
64
+ return [
65
+ listKey,
66
+ {
67
+ ...list,
68
+ graphql: { ...(list.graphql || {}), omit: false },
69
+ fields: Object.fromEntries(
70
+ Object.entries(list.fields).map(([fieldKey, field]) => {
71
+ if (fieldKey.startsWith('__group')) return [fieldKey, field]
72
+ return [
73
+ fieldKey,
74
+ data => {
75
+ const f = field(data)
76
+ return {
77
+ ...f,
78
+ graphql: { ...(f.graphql || {}), omit: false },
79
+
80
+ // WARNING: this bypasses access control checks for filtering and ordering on the internal schema
81
+ isFilterable: true, // TODO: remove when moved to .access.filter and .omit.filter
82
+ isOrderable: true, // TODO: remove when moved to .access.filter and .omit.filter
83
+ }
84
+ },
85
+ ]
86
+ })
87
+ ),
88
+ },
89
+ ]
90
+ })
91
+ ),
92
+ }
93
+
94
+ const lists = initialiseLists(withoutOmit)
95
+ const adminMeta = createAdminMeta(withoutOmit, lists)
96
+ return createGraphQLSchema(withoutOmit, lists, adminMeta, true)
97
+ }
98
+
99
+ function injectNewDefaults(prismaClient: unknown, lists: Record<string, InitialisedList>) {
100
+ for (const listKey in lists) {
101
+ const list = lists[listKey]
102
+
103
+ // TODO: other fields might use 'random' too
104
+ const { dbField } = list.fields.id
105
+
106
+ if ('default' in dbField && dbField.default?.kind === 'random') {
107
+ const { bytes, encoding } = dbField.default
108
+
109
+ prismaClient = (prismaClient as any).$extends({
110
+ query: {
111
+ [list.prisma.listKey]: {
112
+ async create({ args, query }: any) {
113
+ return query({
114
+ ...args,
115
+ data: {
116
+ ...args.data,
117
+ id: args.data.id ?? randomBytes(bytes).toString(encoding),
118
+ },
119
+ })
120
+ },
121
+ },
122
+ },
123
+ })
124
+ }
125
+ }
126
+
127
+ return prismaClient
128
+ }
129
+
130
+ function formatUrl(provider: NixxieConfig['db']['provider'], url: string) {
131
+ if (url.startsWith('file:')) {
132
+ const parsed = new URL(url)
133
+ if (provider === 'sqlite' && !parsed.searchParams.get('connection_limit')) {
134
+ // https://github.com/prisma/prisma/issues/9562
135
+ // https://github.com/prisma/prisma/issues/10403
136
+ // https://github.com/prisma/prisma/issues/11789
137
+ parsed.searchParams.set('connection_limit', '1')
138
+
139
+ const [uri] = url.split('?')
140
+ return `${uri}?${parsed.search}`
141
+ }
142
+ }
143
+
144
+ return url
145
+ }
146
+
147
+ export function createSystem(config: NixxieConfig) {
148
+ const lists = initialiseLists(config)
149
+ const adminMeta = createAdminMeta(config, lists)
150
+ const graphQLSchemas = {
151
+ public: createGraphQLSchema(config, lists, adminMeta, false),
152
+ internal: getInternalGraphQLSchema(config),
153
+ }
154
+
155
+ return {
156
+ config,
157
+ graphql: {
158
+ schemas: graphQLSchemas,
159
+ },
160
+ adminMeta,
161
+ lists,
162
+ getPaths: (cwd: string) => getSystemPaths(cwd, config),
163
+
164
+ getNixxie: (PM: any) => {
165
+ const prePrismaClient = new PM.PrismaClient({
166
+ datasourceUrl: formatUrl(config.db.provider, config.db.url),
167
+ log: config.db.enableLogging,
168
+ })
169
+
170
+ const prismaClient = config.db.extendPrismaClient(injectNewDefaults(prePrismaClient, lists))
171
+ const context = createContext({
172
+ config,
173
+ lists,
174
+ graphQLSchemas,
175
+ prismaClient,
176
+ prismaTypes: {
177
+ DbNull: PM.Prisma.DbNull,
178
+ JsonNull: PM.Prisma.JsonNull,
179
+ },
180
+ })
181
+
182
+ return {
183
+ // TODO: replace with server.onStart, remove in breaking change
184
+ async connect() {
185
+ await (prismaClient as any).$connect()
186
+ // Give database-backed service stores access to the context before anything
187
+ // else runs (see NixxieInitableService).
188
+ const sudoContext = context.sudo()
189
+ for (const [name, service] of Object.entries(context.services)) {
190
+ if (!service || typeof (service as any).init !== 'function') continue
191
+ try {
192
+ await (service as any).init(sudoContext)
193
+ } catch (err) {
194
+ throw new Error(
195
+ `Failed to initialise the "${name}" service: ${err instanceof Error ? err.message : err}`,
196
+ { cause: err }
197
+ )
198
+ }
199
+ }
200
+ await config.db.onConnect?.(context)
201
+ },
202
+ // TODO: only used by tests, remove in breaking change
203
+ async disconnect() {
204
+ await (prismaClient as any).$disconnect()
205
+ },
206
+ context,
207
+ }
208
+ },
209
+ }
210
+ }
211
+
212
+ export type System = ReturnType<typeof createSystem>
213
+
214
+ export function createNixxieContext<TypeInfo extends BaseNixxieTypeInfo>(
215
+ config: NixxieConfig<TypeInfo>,
216
+ PrismaModule: unknown
217
+ ): NixxieContext<TypeInfo> {
218
+ const system = createSystem(config)
219
+ const { context } = system.getNixxie(PrismaModule)
220
+ return context
221
+ }