@nixxie-cms/core 1.0.3 → 1.1.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 (202) hide show
  1. package/CHANGES-1.1.md +134 -0
  2. package/context/dist/nixxie-cms-core-context.cjs.js +4 -3
  3. package/context/dist/nixxie-cms-core-context.esm.js +3 -2
  4. package/dist/declarations/src/access.d.ts +2 -2
  5. package/dist/declarations/src/access.d.ts.map +1 -1
  6. package/dist/declarations/src/admin-ui/components/Navigation.d.ts +2 -2
  7. package/dist/declarations/src/admin-ui/components/Navigation.d.ts.map +1 -1
  8. package/dist/declarations/src/admin-ui/context.d.ts +6 -6
  9. package/dist/declarations/src/admin-ui/context.d.ts.map +1 -1
  10. package/dist/declarations/src/admin-ui/utils/Fields.d.ts +3 -3
  11. package/dist/declarations/src/admin-ui/utils/Fields.d.ts.map +1 -1
  12. package/dist/declarations/src/admin-ui/utils/filters.d.ts +5 -5
  13. package/dist/declarations/src/admin-ui/utils/filters.d.ts.map +1 -1
  14. package/dist/declarations/src/admin-ui/utils/useCreateItem.d.ts +3 -3
  15. package/dist/declarations/src/admin-ui/utils/useCreateItem.d.ts.map +1 -1
  16. package/dist/declarations/src/admin-ui/utils/utils.d.ts +2 -2
  17. package/dist/declarations/src/admin-ui/utils/utils.d.ts.map +1 -1
  18. package/dist/declarations/src/context.d.ts +1 -1
  19. package/dist/declarations/src/context.d.ts.map +1 -1
  20. package/dist/declarations/src/fields/types/bigInt/index.d.ts +3 -3
  21. package/dist/declarations/src/fields/types/bigInt/index.d.ts.map +1 -1
  22. package/dist/declarations/src/fields/types/bytes/index.d.ts +3 -3
  23. package/dist/declarations/src/fields/types/bytes/index.d.ts.map +1 -1
  24. package/dist/declarations/src/fields/types/calendarDay/index.d.ts +3 -3
  25. package/dist/declarations/src/fields/types/calendarDay/index.d.ts.map +1 -1
  26. package/dist/declarations/src/fields/types/checkbox/index.d.ts +3 -3
  27. package/dist/declarations/src/fields/types/checkbox/index.d.ts.map +1 -1
  28. package/dist/declarations/src/fields/types/decimal/index.d.ts +3 -3
  29. package/dist/declarations/src/fields/types/decimal/index.d.ts.map +1 -1
  30. package/dist/declarations/src/fields/types/file/index.d.ts +4 -4
  31. package/dist/declarations/src/fields/types/file/index.d.ts.map +1 -1
  32. package/dist/declarations/src/fields/types/float/index.d.ts +3 -3
  33. package/dist/declarations/src/fields/types/float/index.d.ts.map +1 -1
  34. package/dist/declarations/src/fields/types/image/index.d.ts +4 -4
  35. package/dist/declarations/src/fields/types/image/index.d.ts.map +1 -1
  36. package/dist/declarations/src/fields/types/integer/index.d.ts +3 -3
  37. package/dist/declarations/src/fields/types/integer/index.d.ts.map +1 -1
  38. package/dist/declarations/src/fields/types/json/index.d.ts +3 -3
  39. package/dist/declarations/src/fields/types/json/index.d.ts.map +1 -1
  40. package/dist/declarations/src/fields/types/multiselect/index.d.ts +3 -3
  41. package/dist/declarations/src/fields/types/multiselect/index.d.ts.map +1 -1
  42. package/dist/declarations/src/fields/types/multiselect/views/index.d.ts.map +1 -1
  43. package/dist/declarations/src/fields/types/password/index.d.ts +3 -3
  44. package/dist/declarations/src/fields/types/password/index.d.ts.map +1 -1
  45. package/dist/declarations/src/fields/types/relationship/index.d.ts +8 -8
  46. package/dist/declarations/src/fields/types/relationship/index.d.ts.map +1 -1
  47. package/dist/declarations/src/fields/types/relationship/views/ComboboxMany.d.ts +3 -3
  48. package/dist/declarations/src/fields/types/relationship/views/ComboboxMany.d.ts.map +1 -1
  49. package/dist/declarations/src/fields/types/relationship/views/ComboboxSingle.d.ts +3 -3
  50. package/dist/declarations/src/fields/types/relationship/views/ComboboxSingle.d.ts.map +1 -1
  51. package/dist/declarations/src/fields/types/relationship/views/index.d.ts +3 -3
  52. package/dist/declarations/src/fields/types/relationship/views/index.d.ts.map +1 -1
  53. package/dist/declarations/src/fields/types/relationship/views/types.d.ts +3 -3
  54. package/dist/declarations/src/fields/types/relationship/views/types.d.ts.map +1 -1
  55. package/dist/declarations/src/fields/types/select/index.d.ts +3 -3
  56. package/dist/declarations/src/fields/types/select/index.d.ts.map +1 -1
  57. package/dist/declarations/src/fields/types/text/index.d.ts +3 -3
  58. package/dist/declarations/src/fields/types/text/index.d.ts.map +1 -1
  59. package/dist/declarations/src/fields/types/timestamp/index.d.ts +3 -3
  60. package/dist/declarations/src/fields/types/timestamp/index.d.ts.map +1 -1
  61. package/dist/declarations/src/fields/types/virtual/index.d.ts +7 -7
  62. package/dist/declarations/src/fields/types/virtual/index.d.ts.map +1 -1
  63. package/dist/declarations/src/helpers.d.ts +249 -13
  64. package/dist/declarations/src/helpers.d.ts.map +1 -1
  65. package/dist/declarations/src/index.d.ts +9 -4
  66. package/dist/declarations/src/index.d.ts.map +1 -1
  67. package/dist/declarations/src/internal-unstable/admin-ui/pages/ListPage/index.d.ts.map +1 -1
  68. package/dist/declarations/src/lib/admin-meta.d.ts +11 -11
  69. package/dist/declarations/src/lib/admin-meta.d.ts.map +1 -1
  70. package/dist/declarations/src/lib/core/access-control.d.ts +18 -18
  71. package/dist/declarations/src/lib/core/access-control.d.ts.map +1 -1
  72. package/dist/declarations/src/lib/core/cascade.d.ts +47 -0
  73. package/dist/declarations/src/lib/core/cascade.d.ts.map +1 -0
  74. package/dist/declarations/src/lib/core/initialise-lists.d.ts +27 -24
  75. package/dist/declarations/src/lib/core/initialise-lists.d.ts.map +1 -1
  76. package/dist/declarations/src/lib/env.d.ts +9 -0
  77. package/dist/declarations/src/lib/env.d.ts.map +1 -0
  78. package/dist/declarations/src/lib/system.d.ts +1 -1
  79. package/dist/declarations/src/lib/system.d.ts.map +1 -1
  80. package/dist/declarations/src/list-features.d.ts +162 -0
  81. package/dist/declarations/src/list-features.d.ts.map +1 -0
  82. package/dist/declarations/src/schema.d.ts +24 -23
  83. package/dist/declarations/src/schema.d.ts.map +1 -1
  84. package/dist/declarations/src/session.d.ts +75 -0
  85. package/dist/declarations/src/session.d.ts.map +1 -1
  86. package/dist/declarations/src/types/admin-meta.d.ts +11 -11
  87. package/dist/declarations/src/types/admin-meta.d.ts.map +1 -1
  88. package/dist/declarations/src/types/config/access-control.d.ts +42 -42
  89. package/dist/declarations/src/types/config/access-control.d.ts.map +1 -1
  90. package/dist/declarations/src/types/config/fields.d.ts +19 -19
  91. package/dist/declarations/src/types/config/fields.d.ts.map +1 -1
  92. package/dist/declarations/src/types/config/hooks.d.ts +131 -131
  93. package/dist/declarations/src/types/config/hooks.d.ts.map +1 -1
  94. package/dist/declarations/src/types/config/index.d.ts +171 -8
  95. package/dist/declarations/src/types/config/index.d.ts.map +1 -1
  96. package/dist/declarations/src/types/config/lists.d.ts +146 -108
  97. package/dist/declarations/src/types/config/lists.d.ts.map +1 -1
  98. package/dist/declarations/src/types/context.d.ts +349 -47
  99. package/dist/declarations/src/types/context.d.ts.map +1 -1
  100. package/dist/declarations/src/types/next-fields.d.ts +28 -28
  101. package/dist/declarations/src/types/next-fields.d.ts.map +1 -1
  102. package/dist/declarations/src/types/type-info.d.ts +3 -3
  103. package/dist/declarations/src/types/type-info.d.ts.map +1 -1
  104. package/dist/{express-7559ca2d.esm.js → express-0abbce07.esm.js} +6 -6
  105. package/dist/{express-455ae20c.cjs.js → express-7ca6f76a.cjs.js} +6 -6
  106. package/dist/{index-15c8f81e.esm.js → index-5d8b0b4e.esm.js} +363 -183
  107. package/dist/index-6055753b.cjs.js +393 -0
  108. package/dist/{index-42045902.cjs.js → index-ac29f382.cjs.js} +363 -185
  109. package/dist/index-f1703b7b.esm.js +386 -0
  110. package/dist/nixxie-cms-core.cjs.js +1387 -30
  111. package/dist/nixxie-cms-core.esm.js +1361 -24
  112. package/dist/{non-null-graphql-add6bb3d.cjs.js → non-null-graphql-4a44c122.cjs.js} +1 -1
  113. package/dist/{non-null-graphql-a84ed64d.esm.js → non-null-graphql-8c5feaae.esm.js} +1 -1
  114. package/dist/{resolve-hooks-165a9ce2.cjs.js → resolve-hooks-10a5f84c.cjs.js} +240 -6
  115. package/dist/{resolve-hooks-6813a045.esm.js → resolve-hooks-9e676794.esm.js} +238 -7
  116. package/dist/{system-03e49e4f.esm.js → system-4d2a2648.esm.js} +32 -7
  117. package/dist/{system-a321642d.cjs.js → system-69e1a285.cjs.js} +32 -7
  118. package/fields/dist/nixxie-cms-core-fields.cjs.js +29 -576
  119. package/fields/dist/nixxie-cms-core-fields.esm.js +18 -565
  120. package/fields/types/bytes/dist/nixxie-cms-core-fields-types-bytes.cjs.js +4 -2
  121. package/fields/types/bytes/dist/nixxie-cms-core-fields-types-bytes.esm.js +4 -2
  122. package/fields/types/multiselect/views/dist/nixxie-cms-core-fields-types-multiselect-views.cjs.js +1 -6
  123. package/fields/types/multiselect/views/dist/nixxie-cms-core-fields-types-multiselect-views.esm.js +1 -6
  124. package/fields/types/password/dist/nixxie-cms-core-fields-types-password.cjs.js +4 -2
  125. package/fields/types/password/dist/nixxie-cms-core-fields-types-password.esm.js +4 -2
  126. package/internal-unstable/artifacts/dist/nixxie-cms-core-internal-unstable-artifacts.cjs.js +4 -3
  127. package/internal-unstable/artifacts/dist/nixxie-cms-core-internal-unstable-artifacts.esm.js +4 -3
  128. package/package.json +4 -4
  129. package/scripts/cli/dist/nixxie-cms-core-scripts-cli.cjs.js +4 -3
  130. package/scripts/cli/dist/nixxie-cms-core-scripts-cli.esm.js +4 -3
  131. package/scripts/dist/nixxie-cms-core-scripts.cjs.js +4 -3
  132. package/scripts/dist/nixxie-cms-core-scripts.esm.js +4 -3
  133. package/session/dist/nixxie-cms-core-session.cjs.js +286 -0
  134. package/session/dist/nixxie-cms-core-session.esm.js +279 -1
  135. package/src/access.ts +25 -25
  136. package/src/admin-ui/admin-meta-graphql.ts +5 -5
  137. package/src/admin-ui/components/CreateButtonLink.tsx +46 -46
  138. package/src/admin-ui/components/Navigation.tsx +3 -3
  139. package/src/admin-ui/context.tsx +6 -6
  140. package/src/admin-ui/utils/Fields.tsx +241 -241
  141. package/src/admin-ui/utils/actionData.ts +36 -36
  142. package/src/admin-ui/utils/filters.ts +148 -148
  143. package/src/admin-ui/utils/useCreateItem.ts +171 -171
  144. package/src/admin-ui/utils/utils.tsx +127 -127
  145. package/src/context.ts +1 -1
  146. package/src/fields/non-null-graphql.ts +115 -115
  147. package/src/fields/types/bigInt/index.ts +6 -6
  148. package/src/fields/types/bytes/index.ts +6 -6
  149. package/src/fields/types/calendarDay/index.ts +18 -19
  150. package/src/fields/types/checkbox/index.ts +6 -6
  151. package/src/fields/types/decimal/index.ts +6 -6
  152. package/src/fields/types/file/index.ts +8 -8
  153. package/src/fields/types/float/index.ts +6 -6
  154. package/src/fields/types/image/index.ts +8 -8
  155. package/src/fields/types/integer/index.ts +6 -6
  156. package/src/fields/types/json/index.ts +5 -5
  157. package/src/fields/types/multiselect/index.ts +7 -7
  158. package/src/fields/types/multiselect/views/index.tsx +149 -151
  159. package/src/fields/types/password/index.ts +6 -6
  160. package/src/fields/types/relationship/index.ts +13 -13
  161. package/src/fields/types/relationship/views/ComboboxMany.tsx +110 -110
  162. package/src/fields/types/relationship/views/ComboboxSingle.tsx +115 -115
  163. package/src/fields/types/relationship/views/ContextualActions.tsx +139 -139
  164. package/src/fields/types/relationship/views/index.tsx +492 -492
  165. package/src/fields/types/relationship/views/types.ts +46 -46
  166. package/src/fields/types/relationship/views/useApolloQuery.ts +185 -185
  167. package/src/fields/types/relationship/views/useFilter.tsx +109 -109
  168. package/src/fields/types/select/index.ts +6 -6
  169. package/src/fields/types/text/index.ts +6 -6
  170. package/src/fields/types/timestamp/index.ts +23 -21
  171. package/src/fields/types/virtual/index.ts +11 -11
  172. package/src/helpers.ts +773 -42
  173. package/src/index.ts +66 -24
  174. package/src/internal-unstable/admin-ui/pages/ItemPage/common.tsx +4 -4
  175. package/src/internal-unstable/admin-ui/pages/ItemPage/index.tsx +5 -5
  176. package/src/internal-unstable/admin-ui/pages/ListPage/index.tsx +8 -8
  177. package/src/lib/admin-meta.ts +369 -369
  178. package/src/lib/context/createContext.ts +5 -0
  179. package/src/lib/core/access-control.ts +434 -434
  180. package/src/lib/core/cascade.ts +236 -0
  181. package/src/lib/core/initialise-lists.ts +49 -33
  182. package/src/lib/core/mutations/index.ts +7 -0
  183. package/src/lib/core/mutations/nested-mutation-many-input-resolvers.ts +145 -145
  184. package/src/lib/core/mutations/nested-mutation-one-input-resolvers.ts +71 -71
  185. package/src/lib/core/queries/output-field.ts +178 -178
  186. package/src/lib/env.ts +50 -0
  187. package/src/lib/id-field.ts +2 -2
  188. package/src/lib/system.ts +221 -207
  189. package/src/lib/typescript-schema-printer.ts +227 -227
  190. package/src/list-features.ts +476 -0
  191. package/src/schema.ts +91 -22
  192. package/src/session.ts +225 -0
  193. package/src/types/admin-meta.ts +218 -218
  194. package/src/types/config/access-control.ts +186 -186
  195. package/src/types/config/fields.ts +96 -96
  196. package/src/types/config/hooks.ts +529 -529
  197. package/src/types/config/index.ts +185 -7
  198. package/src/types/config/lists.ts +606 -565
  199. package/src/types/context.ts +426 -55
  200. package/src/types/next-fields.ts +31 -31
  201. package/src/types/type-info.ts +38 -38
  202. 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
+ }