@tinacms/app 1.2.7 → 1.2.9
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 +18 -0
- package/package.json +6 -7
- package/src/lib/build-form.ts +49 -0
- package/src/lib/expand-query.ts +231 -0
- package/src/lib/graphql-reducer.ts +753 -0
- package/src/lib/types.ts +48 -0
- package/src/preview.tsx +3 -105
- package/src/assets/react.svg +0 -1
- package/src/lib/formify/index.ts +0 -325
- package/src/lib/machines/document-machine.ts +0 -338
- package/src/lib/machines/query-machine.ts +0 -701
- package/src/lib/machines/util.ts +0 -196
|
@@ -1,701 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
|
|
3
|
-
*/
|
|
4
|
-
import { assign, createMachine, spawn } from 'xstate'
|
|
5
|
-
import {
|
|
6
|
-
Form,
|
|
7
|
-
TinaCMS,
|
|
8
|
-
NAMER,
|
|
9
|
-
TinaFieldEnriched,
|
|
10
|
-
TinaCollection,
|
|
11
|
-
TinaSchema,
|
|
12
|
-
GlobalFormPlugin,
|
|
13
|
-
Client,
|
|
14
|
-
} from 'tinacms'
|
|
15
|
-
import * as G from 'graphql'
|
|
16
|
-
import { formify } from '../formify'
|
|
17
|
-
import { Data, documentMachine } from './document-machine'
|
|
18
|
-
import type { ActorRefFrom } from 'xstate'
|
|
19
|
-
import { getIn } from 'final-form'
|
|
20
|
-
|
|
21
|
-
export type DataType = Record<string, unknown>
|
|
22
|
-
type DocumentInfo = {
|
|
23
|
-
ref: ActorRefFrom<typeof documentMachine>
|
|
24
|
-
}
|
|
25
|
-
type DocumentMap = {
|
|
26
|
-
[documentId: string]: DocumentInfo & {
|
|
27
|
-
/** We don't support nested forms or forms for list queries */
|
|
28
|
-
skipFormRegister: boolean
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
type ContextType = {
|
|
33
|
-
id: null | string
|
|
34
|
-
data: null | DataType
|
|
35
|
-
cms: TinaCMS
|
|
36
|
-
documentNode: G.DocumentNode
|
|
37
|
-
variables: object
|
|
38
|
-
iframe: null | HTMLIFrameElement
|
|
39
|
-
registerSubForms?: boolean
|
|
40
|
-
formifyCallback: (args: any) => Form
|
|
41
|
-
documentMap: DocumentMap
|
|
42
|
-
documents: Data[]
|
|
43
|
-
}
|
|
44
|
-
export const initialContext: Omit<ContextType, 'cms' | 'formifyCallback'> = {
|
|
45
|
-
id: null,
|
|
46
|
-
data: null,
|
|
47
|
-
variables: {},
|
|
48
|
-
documentMap: {},
|
|
49
|
-
documents: [],
|
|
50
|
-
documentNode: { kind: 'Document', definitions: [] },
|
|
51
|
-
iframe: null,
|
|
52
|
-
}
|
|
53
|
-
export const queryMachine =
|
|
54
|
-
/** @xstate-layout N4IgpgJg5mDOIC5QAoC2BDAxgCwJYDswBKAOgFcAnAGxNwirAGIBlAUQBUB9AVQCUAZRKAAOAe1i4ALrlH4hIAB6IAjAAYALCXXqAnACZVAVmUAOAGxmAzMp1mANCACeidXpMkdnnSZvLDh1X11AF9ghzQsPEJSSho6BhYOTgBJADkABW4uPkEkEDEJaVl5JQQAdh0SMtVqvTqassN1M0MHZwRLdWUPLx8dPwCg0PCMHAJicmpaeiY2LgARZOZ0-gBBAE0eAXkCqRk5PNKyvRJVM-OL8+U2xD0mns8+gcC9ELCQCLHoybiZxm50vNVuxWFtciJxHtiodECZKqoLOpVMpjiYTGUyp0bghlGYyg9vL5-C83iNIuNSMJcMIwFRxtMEqt5vNOABFbisXjrHaQooHUBHPSWEiGSw6VSWUWYvRWHTYsro07nSzIpE+CrDD6jKITKk0umEBmzbgAIXmAHkAMLcACyrFS7GYPMK+xKiGOwtF4sllmlsuxOlcIquJlUJm04pMms+Osp1Np9PiTGSADFeKs7ZwbebuA7WPNnVD+YpEKKzCKFWG0dZkSZWk5YRKlWdDMc8f5SVryd89QnDQQ9ug6QAvAhQRgQWRgWj4ABuogA1tOYxSSL2DdOB9Ih7hR-goAgCPPMOg+QBtVQAXULfLdOLU+Neej8yksNdM9faCs0l3FrleorRtqq7rom+CDiOY6MGAFAUKIFBrlQp4AGbwagJArj28YbjOEG7mOh5zqIJ7nleN6ujC941CQT4vm+agftiZg6Piv4aHU6iAe8mG6th9IAO7oHs+6MBa1p2g6nC8KwTLcnkuy3pRygPjRrh0e+dbYspujNmcf4cU0QHdrx+r0jS+AQFBk79kRS4YcBWGmYa5mWfuhHHqe+wXte8m8hRAoqKYmiSn4Mr0bWn4qJYipeJ4gSto0zRGV8Jl9tOLlQTBcEIcISGSKhFDoTxcZOelYAWQRR7EZ5sjeeR0IBVRj5qcY4WMQ2OIIicbH-pxnbFWufGGhQYDoBAjj-ICwKgjk9XFqUlh4iQnS+kiOgJU09gdfo5YXCqyhqiiOjJbGg2lSQI1jRN0nZgAaqC7KcnJEIug1JYdEtK1lGtG3NNi6LuBc6gmHUophmUJ0gUN06XeNiT8KwloLFatr2uwc13sphgijYooJcpdRmCYWnKeWsXiutGKbZDjlpRdo1wymySsPwLKWgAEqsqQAOKsBjlGLfiX0-VTf0dcoMruIYoTvPgogQHA8gDbERr841ynuDK63WHclyWNihgGASTzEvoNMTLEavvcDumXBcB0Btj5PxaLZh6ObJV00mVulE02Nu7oROSs+XR6P9626a2MqNIZ3EOalOFbrgO57lAPuIH+VQ+M+0UaZF5SGLt5xR+2sdkilns4YJwlp75r3zRn3inDovoaAq6LSiTb4kCYb4qvoBh4sdcfGZXZnla5tcvUWmO+t0am99WDGaeL1juAqraGCDRg2O7I8V2ddOw+00+KY1GJaQY7jO5TiVmB7h84Vl8Hpzic9aM+i+98v+dlGoBIU1+vffep1QKEFfqKJidxdKmDDBGBEHtX6vFtnbM4DsOpCgAeTXowDQhAA */
|
|
55
|
-
createMachine(
|
|
56
|
-
{
|
|
57
|
-
tsTypes: {} as import('./query-machine.typegen').Typegen0,
|
|
58
|
-
schema: {
|
|
59
|
-
context: {} as ContextType,
|
|
60
|
-
services: {} as {
|
|
61
|
-
initializer: {
|
|
62
|
-
data: {
|
|
63
|
-
data: DataType
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
setter: {
|
|
67
|
-
data: { data: DataType }
|
|
68
|
-
}
|
|
69
|
-
subDocumentResolver: {
|
|
70
|
-
data: {
|
|
71
|
-
id: string
|
|
72
|
-
location: string
|
|
73
|
-
}[]
|
|
74
|
-
}
|
|
75
|
-
onChangeCallback: {
|
|
76
|
-
data: undefined
|
|
77
|
-
}
|
|
78
|
-
},
|
|
79
|
-
events: {} as
|
|
80
|
-
| {
|
|
81
|
-
type: 'IFRAME_MOUNTED'
|
|
82
|
-
value: HTMLIFrameElement
|
|
83
|
-
}
|
|
84
|
-
| {
|
|
85
|
-
type: 'SELECT_DOCUMENT'
|
|
86
|
-
value: string
|
|
87
|
-
}
|
|
88
|
-
| {
|
|
89
|
-
type: 'DOCUMENT_READY'
|
|
90
|
-
value: string
|
|
91
|
-
}
|
|
92
|
-
| {
|
|
93
|
-
type: 'NAVIGATE'
|
|
94
|
-
}
|
|
95
|
-
| {
|
|
96
|
-
type: 'ADD_QUERY'
|
|
97
|
-
value: {
|
|
98
|
-
id: string
|
|
99
|
-
type: 'open' | 'close'
|
|
100
|
-
query: string
|
|
101
|
-
data: object
|
|
102
|
-
variables: object
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
| {
|
|
106
|
-
type: 'REMOVE_QUERY'
|
|
107
|
-
value: string
|
|
108
|
-
}
|
|
109
|
-
| {
|
|
110
|
-
type: 'SUBDOCUMENTS'
|
|
111
|
-
value: { id: string; location: string }[]
|
|
112
|
-
}
|
|
113
|
-
| {
|
|
114
|
-
type: 'FIELD_CHANGE'
|
|
115
|
-
}
|
|
116
|
-
| {
|
|
117
|
-
type: 'EDIT_MODE'
|
|
118
|
-
},
|
|
119
|
-
},
|
|
120
|
-
id: '(machine)',
|
|
121
|
-
type: 'parallel',
|
|
122
|
-
states: {
|
|
123
|
-
pipeline: {
|
|
124
|
-
initial: 'idle',
|
|
125
|
-
states: {
|
|
126
|
-
idle: {
|
|
127
|
-
entry: 'clear',
|
|
128
|
-
on: {
|
|
129
|
-
ADD_QUERY: {
|
|
130
|
-
target: 'initializing',
|
|
131
|
-
},
|
|
132
|
-
SUBDOCUMENTS: {
|
|
133
|
-
target: 'pending',
|
|
134
|
-
},
|
|
135
|
-
IFRAME_MOUNTED: {
|
|
136
|
-
actions: 'setIframe',
|
|
137
|
-
},
|
|
138
|
-
},
|
|
139
|
-
},
|
|
140
|
-
initializing: {
|
|
141
|
-
invoke: {
|
|
142
|
-
src: 'initializer',
|
|
143
|
-
onDone: [
|
|
144
|
-
{
|
|
145
|
-
actions: 'storeInitialValues',
|
|
146
|
-
target: 'pending',
|
|
147
|
-
},
|
|
148
|
-
],
|
|
149
|
-
onError: [
|
|
150
|
-
{
|
|
151
|
-
actions: 'handleError',
|
|
152
|
-
target: 'error',
|
|
153
|
-
},
|
|
154
|
-
],
|
|
155
|
-
},
|
|
156
|
-
},
|
|
157
|
-
waiting: {
|
|
158
|
-
on: {
|
|
159
|
-
DOCUMENT_READY: {
|
|
160
|
-
target: 'pending',
|
|
161
|
-
},
|
|
162
|
-
},
|
|
163
|
-
},
|
|
164
|
-
pending: {
|
|
165
|
-
invoke: {
|
|
166
|
-
src: 'setter',
|
|
167
|
-
onDone: [
|
|
168
|
-
{
|
|
169
|
-
target: 'ready',
|
|
170
|
-
},
|
|
171
|
-
],
|
|
172
|
-
onError: [
|
|
173
|
-
{
|
|
174
|
-
actions: 'handleMissingDocument',
|
|
175
|
-
target: 'waiting',
|
|
176
|
-
},
|
|
177
|
-
],
|
|
178
|
-
},
|
|
179
|
-
},
|
|
180
|
-
ready: {
|
|
181
|
-
entry: 'resolveData',
|
|
182
|
-
invoke: {
|
|
183
|
-
src: 'onChangeCallback',
|
|
184
|
-
},
|
|
185
|
-
on: {
|
|
186
|
-
NAVIGATE: {
|
|
187
|
-
target: 'idle',
|
|
188
|
-
},
|
|
189
|
-
REMOVE_QUERY: {
|
|
190
|
-
target: 'idle',
|
|
191
|
-
},
|
|
192
|
-
FIELD_CHANGE: {
|
|
193
|
-
target: 'pending',
|
|
194
|
-
},
|
|
195
|
-
},
|
|
196
|
-
},
|
|
197
|
-
error: {},
|
|
198
|
-
},
|
|
199
|
-
},
|
|
200
|
-
},
|
|
201
|
-
},
|
|
202
|
-
{
|
|
203
|
-
actions: {
|
|
204
|
-
handleError: (_context, event) => console.error(event.data),
|
|
205
|
-
handleMissingDocument: assign((context, event) => {
|
|
206
|
-
if (event.data instanceof QueryError) {
|
|
207
|
-
if (context.documentMap[event.data.id]) {
|
|
208
|
-
// Already exists
|
|
209
|
-
return context
|
|
210
|
-
}
|
|
211
|
-
if (!event.data.id) {
|
|
212
|
-
return context
|
|
213
|
-
}
|
|
214
|
-
const existingData = context.documents.find(
|
|
215
|
-
(doc) => doc._internalSys.path === event.data.id
|
|
216
|
-
)
|
|
217
|
-
const doc = {
|
|
218
|
-
ref: spawn(
|
|
219
|
-
documentMachine.withContext({
|
|
220
|
-
id: event.data.id,
|
|
221
|
-
cms: context.cms,
|
|
222
|
-
formifyCallback: context.formifyCallback,
|
|
223
|
-
form: null,
|
|
224
|
-
data: existingData || null,
|
|
225
|
-
})
|
|
226
|
-
),
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
return {
|
|
230
|
-
...context,
|
|
231
|
-
documentMap: {
|
|
232
|
-
...context.documentMap,
|
|
233
|
-
[event.data.id]: {
|
|
234
|
-
...doc,
|
|
235
|
-
skipFormRegister: event.data.skipFormRegister,
|
|
236
|
-
},
|
|
237
|
-
},
|
|
238
|
-
}
|
|
239
|
-
} else {
|
|
240
|
-
console.error(event.data)
|
|
241
|
-
return context
|
|
242
|
-
}
|
|
243
|
-
}),
|
|
244
|
-
clear: assign((context) => {
|
|
245
|
-
context.cms.forms.all().forEach((form) => {
|
|
246
|
-
context.cms.forms.remove(form.id)
|
|
247
|
-
})
|
|
248
|
-
return {
|
|
249
|
-
...initialContext,
|
|
250
|
-
formifyCallback: context.formifyCallback,
|
|
251
|
-
cms: context.cms,
|
|
252
|
-
// documentMap: context.documentMap, // to preserve docs across pages
|
|
253
|
-
iframe: context.iframe,
|
|
254
|
-
}
|
|
255
|
-
}),
|
|
256
|
-
storeInitialValues: assign((context, event) => {
|
|
257
|
-
return {
|
|
258
|
-
...context,
|
|
259
|
-
...event.data,
|
|
260
|
-
}
|
|
261
|
-
}),
|
|
262
|
-
setIframe: assign((context, event) => {
|
|
263
|
-
return {
|
|
264
|
-
...context,
|
|
265
|
-
iframe: event.value,
|
|
266
|
-
}
|
|
267
|
-
}),
|
|
268
|
-
resolveData: assign((context, event) => {
|
|
269
|
-
if (context.iframe) {
|
|
270
|
-
context.iframe?.contentWindow?.postMessage({
|
|
271
|
-
type: 'updateData',
|
|
272
|
-
id: context.id,
|
|
273
|
-
data: event.data.data,
|
|
274
|
-
})
|
|
275
|
-
}
|
|
276
|
-
return {
|
|
277
|
-
...context,
|
|
278
|
-
data: event.data.data,
|
|
279
|
-
}
|
|
280
|
-
}),
|
|
281
|
-
},
|
|
282
|
-
services: {
|
|
283
|
-
setter: async (context) => {
|
|
284
|
-
const tinaSchema = context.cms.api.tina.schema as TinaSchema
|
|
285
|
-
const gqlSchema = context.cms.api.tina.gqlSchema
|
|
286
|
-
const missingForms: { id: string; skipFormRegister: boolean }[] = []
|
|
287
|
-
const newData = await G.graphql({
|
|
288
|
-
schema: gqlSchema,
|
|
289
|
-
source: G.print(context.documentNode),
|
|
290
|
-
rootValue: context.data,
|
|
291
|
-
variableValues: context.variables,
|
|
292
|
-
fieldResolver: (source, args, _context, info) => {
|
|
293
|
-
const fieldName = info.fieldName
|
|
294
|
-
|
|
295
|
-
/**
|
|
296
|
-
* Since the `source` for this resolver is the query that
|
|
297
|
-
* ran before passing it into `useTina`, we need to take aliases
|
|
298
|
-
* into consideration, so if an alias is provided we try to
|
|
299
|
-
* see if that has the value we're looking for. This isn't a perfect
|
|
300
|
-
* solution as the `value` gets overwritten depending on the alias
|
|
301
|
-
* query.
|
|
302
|
-
*/
|
|
303
|
-
const aliases: string[] = []
|
|
304
|
-
info.fieldNodes.forEach((fieldNode) => {
|
|
305
|
-
if (fieldNode.alias) {
|
|
306
|
-
aliases.push(fieldNode.alias.value)
|
|
307
|
-
}
|
|
308
|
-
})
|
|
309
|
-
let value = source[fieldName]
|
|
310
|
-
if (!value) {
|
|
311
|
-
aliases.forEach((alias) => {
|
|
312
|
-
const aliasValue = source[alias]
|
|
313
|
-
if (aliasValue) {
|
|
314
|
-
value = aliasValue
|
|
315
|
-
}
|
|
316
|
-
})
|
|
317
|
-
}
|
|
318
|
-
/**
|
|
319
|
-
* Formify adds `_internalSys` and `_internalValues` to the query
|
|
320
|
-
* and a user's query might also include `_values` or `_sys`, but
|
|
321
|
-
* it may not contain all of the info we need, so the actual
|
|
322
|
-
* source of truth for these values is our alias ones, which are
|
|
323
|
-
* also guaranteed to include all of the values another `_sys` query
|
|
324
|
-
* might include
|
|
325
|
-
*/
|
|
326
|
-
if (fieldName === '_sys') {
|
|
327
|
-
return source._internalSys
|
|
328
|
-
}
|
|
329
|
-
if (fieldName === '_values') {
|
|
330
|
-
return source._internalValues
|
|
331
|
-
}
|
|
332
|
-
if (isNodeType(info.returnType)) {
|
|
333
|
-
const existingValue = source[fieldName]
|
|
334
|
-
let skipFormRegister = false
|
|
335
|
-
if (!existingValue) {
|
|
336
|
-
return null
|
|
337
|
-
}
|
|
338
|
-
let path: string = ''
|
|
339
|
-
if (typeof existingValue === 'string') {
|
|
340
|
-
// this is a reference value (eg. post.author)
|
|
341
|
-
skipFormRegister = true
|
|
342
|
-
path = existingValue
|
|
343
|
-
} else {
|
|
344
|
-
path = existingValue._internalSys.path
|
|
345
|
-
}
|
|
346
|
-
if (context.documentMap[path]) {
|
|
347
|
-
const documentMachine = context.documentMap[path].ref
|
|
348
|
-
const documentContext = documentMachine.getSnapshot()?.context
|
|
349
|
-
if (!documentContext) {
|
|
350
|
-
throw new Error(
|
|
351
|
-
`Document not set up properly for id: ${path}`
|
|
352
|
-
)
|
|
353
|
-
}
|
|
354
|
-
const { data, form } = documentContext
|
|
355
|
-
const values = form?.values
|
|
356
|
-
if (!data || !form || !values) {
|
|
357
|
-
throw new Error(
|
|
358
|
-
`Document not set up properly for id: ${path}`
|
|
359
|
-
)
|
|
360
|
-
}
|
|
361
|
-
const collectionName = data._internalSys.collection.name
|
|
362
|
-
const extraValues = documentContext.data
|
|
363
|
-
const formVal = resolveFormValue({
|
|
364
|
-
fields: form.fields,
|
|
365
|
-
values: values,
|
|
366
|
-
tinaSchema,
|
|
367
|
-
})
|
|
368
|
-
const template = tinaSchema.getTemplateForData({
|
|
369
|
-
data: form.values,
|
|
370
|
-
collection: tinaSchema.getCollection(collectionName),
|
|
371
|
-
})
|
|
372
|
-
return {
|
|
373
|
-
...extraValues,
|
|
374
|
-
...formVal,
|
|
375
|
-
_sys: data._internalSys,
|
|
376
|
-
id: path,
|
|
377
|
-
__typename: NAMER.dataTypeName(template.namespace),
|
|
378
|
-
}
|
|
379
|
-
} else {
|
|
380
|
-
// TODO: when we support forms in lists, remove this check
|
|
381
|
-
// This checks that we're at least 2 levels deep, meaning top-level
|
|
382
|
-
// queries list page(relativePath: '...') will be registered, but
|
|
383
|
-
// not connection nodes like pageConnection.edges.node
|
|
384
|
-
if (info.path?.prev?.prev) {
|
|
385
|
-
skipFormRegister = true
|
|
386
|
-
}
|
|
387
|
-
missingForms.push({ id: path, skipFormRegister })
|
|
388
|
-
return null
|
|
389
|
-
}
|
|
390
|
-
}
|
|
391
|
-
return value
|
|
392
|
-
},
|
|
393
|
-
})
|
|
394
|
-
if (missingForms.length > 0) {
|
|
395
|
-
// Only run this one at a time
|
|
396
|
-
const missingForm = missingForms[0]
|
|
397
|
-
throw new QueryError(
|
|
398
|
-
`Unable to resolve form for initial document`,
|
|
399
|
-
missingForm.id,
|
|
400
|
-
missingForm.skipFormRegister
|
|
401
|
-
)
|
|
402
|
-
}
|
|
403
|
-
// Populate __meta__ property for use
|
|
404
|
-
// in active field indicator utility
|
|
405
|
-
const META_KEY = '__meta__'
|
|
406
|
-
function* traverse(o) {
|
|
407
|
-
const memory = new Set()
|
|
408
|
-
function* innerTraversal(o, path = []) {
|
|
409
|
-
if (memory.has(o)) {
|
|
410
|
-
// we've seen this object before don't iterate it
|
|
411
|
-
return
|
|
412
|
-
}
|
|
413
|
-
// add the new object to our memory.
|
|
414
|
-
memory.add(o)
|
|
415
|
-
for (let i of Object.keys(o)) {
|
|
416
|
-
const itemPath = path.concat(i)
|
|
417
|
-
yield [i, o[i], itemPath, o]
|
|
418
|
-
if (o[i] !== null && typeof o[i] == 'object') {
|
|
419
|
-
if (
|
|
420
|
-
[
|
|
421
|
-
'_internalSys',
|
|
422
|
-
'_internalValues',
|
|
423
|
-
'_sys',
|
|
424
|
-
META_KEY,
|
|
425
|
-
].includes(i)
|
|
426
|
-
) {
|
|
427
|
-
//going one step down in the object tree!!
|
|
428
|
-
yield* innerTraversal(o[i], itemPath)
|
|
429
|
-
}
|
|
430
|
-
}
|
|
431
|
-
}
|
|
432
|
-
}
|
|
433
|
-
yield* innerTraversal(o)
|
|
434
|
-
}
|
|
435
|
-
const nodePaths = []
|
|
436
|
-
for (let [key, value, path, parent] of traverse(newData.data)) {
|
|
437
|
-
if (value?._internalSys) {
|
|
438
|
-
nodePaths.push(path)
|
|
439
|
-
const fields = {}
|
|
440
|
-
const parents = nodePaths.filter((nodePath) => {
|
|
441
|
-
return path.join('.').startsWith(nodePath.join('.'))
|
|
442
|
-
})
|
|
443
|
-
|
|
444
|
-
const nearestParent = parents.reduce(function (a, b) {
|
|
445
|
-
return a.length < b.length ? a : b
|
|
446
|
-
})
|
|
447
|
-
Object.keys(value).map((key) => {
|
|
448
|
-
if (
|
|
449
|
-
[
|
|
450
|
-
'__typename',
|
|
451
|
-
'_internalSys',
|
|
452
|
-
'_internalValues',
|
|
453
|
-
'_sys',
|
|
454
|
-
].includes(key)
|
|
455
|
-
) {
|
|
456
|
-
return false
|
|
457
|
-
}
|
|
458
|
-
fields[key] = [
|
|
459
|
-
...path.slice(nearestParent.length + 1),
|
|
460
|
-
key,
|
|
461
|
-
].join('.')
|
|
462
|
-
})
|
|
463
|
-
value[META_KEY] = {
|
|
464
|
-
id: value?._internalSys.path,
|
|
465
|
-
name: path.slice(nearestParent.length).join('.'),
|
|
466
|
-
fields,
|
|
467
|
-
}
|
|
468
|
-
} else if (
|
|
469
|
-
typeof value === 'object' &&
|
|
470
|
-
!Array.isArray(value) &&
|
|
471
|
-
value !== null
|
|
472
|
-
) {
|
|
473
|
-
if (key !== META_KEY) {
|
|
474
|
-
const parents = nodePaths.filter((nodePath) => {
|
|
475
|
-
return path.join('.').startsWith(nodePath.join('.'))
|
|
476
|
-
})
|
|
477
|
-
if (parents.length) {
|
|
478
|
-
const nearestParent = parents.reduce(function (a, b) {
|
|
479
|
-
return a.length < b.length ? a : b
|
|
480
|
-
})
|
|
481
|
-
const parent = getIn(newData.data, nearestParent.join('.'))
|
|
482
|
-
const id = parent._internalSys.path
|
|
483
|
-
const fields = {}
|
|
484
|
-
Object.keys(value).map((key) => {
|
|
485
|
-
if (
|
|
486
|
-
[
|
|
487
|
-
'__typename',
|
|
488
|
-
'_internalSys',
|
|
489
|
-
'_internalValues',
|
|
490
|
-
'_sys',
|
|
491
|
-
].includes(key)
|
|
492
|
-
) {
|
|
493
|
-
return false
|
|
494
|
-
}
|
|
495
|
-
|
|
496
|
-
fields[key] = [
|
|
497
|
-
...path.slice(nearestParent.length),
|
|
498
|
-
key,
|
|
499
|
-
].join('.')
|
|
500
|
-
})
|
|
501
|
-
value[META_KEY] = {
|
|
502
|
-
id,
|
|
503
|
-
name: path.slice(nearestParent.length).join('.'),
|
|
504
|
-
fields,
|
|
505
|
-
}
|
|
506
|
-
}
|
|
507
|
-
}
|
|
508
|
-
}
|
|
509
|
-
}
|
|
510
|
-
return { data: newData.data }
|
|
511
|
-
},
|
|
512
|
-
initializer: async (context, event) => {
|
|
513
|
-
const tina = context.cms.api.tina as Client
|
|
514
|
-
const schema = await tina.getSchema()
|
|
515
|
-
const documentNode = G.parse(event.value.query)
|
|
516
|
-
const { formifiedQuery } = await formify({
|
|
517
|
-
schema,
|
|
518
|
-
optimizedDocumentNode: documentNode,
|
|
519
|
-
})
|
|
520
|
-
const data = (await context.cms.api.tina.request(
|
|
521
|
-
G.print(formifiedQuery),
|
|
522
|
-
{
|
|
523
|
-
variables: event.value.variables,
|
|
524
|
-
}
|
|
525
|
-
)) as DataType
|
|
526
|
-
const documents: Data[] = []
|
|
527
|
-
// step through every value in the payload to find the documents
|
|
528
|
-
JSON.stringify(data, (key, value) => {
|
|
529
|
-
if (value?._internalValues) {
|
|
530
|
-
documents.push(value)
|
|
531
|
-
}
|
|
532
|
-
return value
|
|
533
|
-
})
|
|
534
|
-
return {
|
|
535
|
-
data,
|
|
536
|
-
documents,
|
|
537
|
-
variables: event.value.variables,
|
|
538
|
-
documentNode: formifiedQuery,
|
|
539
|
-
id: event.value.id,
|
|
540
|
-
}
|
|
541
|
-
},
|
|
542
|
-
onChangeCallback: (context) => (callback, _onReceive) => {
|
|
543
|
-
const schema = context.cms.api.tina.schema as TinaSchema
|
|
544
|
-
Object.values(context.documentMap).forEach((documentMachine) => {
|
|
545
|
-
if (!context.registerSubForms) {
|
|
546
|
-
if (documentMachine.skipFormRegister) {
|
|
547
|
-
return
|
|
548
|
-
}
|
|
549
|
-
}
|
|
550
|
-
const documentContext = documentMachine.ref.getSnapshot()?.context
|
|
551
|
-
const collectionName =
|
|
552
|
-
documentContext?.data?._internalSys.collection.name
|
|
553
|
-
|
|
554
|
-
const collection = schema.getCollection(
|
|
555
|
-
collectionName || ''
|
|
556
|
-
) as TinaCollection
|
|
557
|
-
if (documentContext?.form) {
|
|
558
|
-
if (collection.ui?.global) {
|
|
559
|
-
context.cms.plugins.add(
|
|
560
|
-
new GlobalFormPlugin(documentContext.form)
|
|
561
|
-
)
|
|
562
|
-
} else {
|
|
563
|
-
context.cms.forms.add(documentContext.form)
|
|
564
|
-
}
|
|
565
|
-
}
|
|
566
|
-
})
|
|
567
|
-
},
|
|
568
|
-
},
|
|
569
|
-
}
|
|
570
|
-
)
|
|
571
|
-
class QueryError extends Error {
|
|
572
|
-
public id: string
|
|
573
|
-
public skipFormRegister: boolean
|
|
574
|
-
constructor(message: string, id: string, skipFormRegister: boolean) {
|
|
575
|
-
super(message) // (1)
|
|
576
|
-
this.name = 'QueryError' // (2)
|
|
577
|
-
this.id = id
|
|
578
|
-
this.skipFormRegister = skipFormRegister
|
|
579
|
-
}
|
|
580
|
-
}
|
|
581
|
-
|
|
582
|
-
const isNodeType = (type: G.GraphQLOutputType) => {
|
|
583
|
-
const namedType = G.getNamedType(type)
|
|
584
|
-
if (G.isInterfaceType(namedType)) {
|
|
585
|
-
if (namedType.name === 'Node') {
|
|
586
|
-
return true
|
|
587
|
-
}
|
|
588
|
-
}
|
|
589
|
-
if (G.isUnionType(namedType)) {
|
|
590
|
-
const types = namedType.getTypes()
|
|
591
|
-
if (
|
|
592
|
-
types.every((type) => {
|
|
593
|
-
return type.getInterfaces().some((intfc) => intfc.name === 'Node')
|
|
594
|
-
})
|
|
595
|
-
) {
|
|
596
|
-
return true
|
|
597
|
-
}
|
|
598
|
-
}
|
|
599
|
-
if (G.isObjectType(namedType)) {
|
|
600
|
-
if (namedType.getInterfaces().some((intfc) => intfc.name === 'Node')) {
|
|
601
|
-
return true
|
|
602
|
-
}
|
|
603
|
-
}
|
|
604
|
-
}
|
|
605
|
-
|
|
606
|
-
const resolveFormValue = <T extends Record<string, unknown>>({
|
|
607
|
-
fields,
|
|
608
|
-
values,
|
|
609
|
-
tinaSchema,
|
|
610
|
-
}: {
|
|
611
|
-
fields: TinaFieldEnriched[]
|
|
612
|
-
values: T
|
|
613
|
-
tinaSchema: TinaSchema
|
|
614
|
-
}): T & { __typename?: string } => {
|
|
615
|
-
const accum: Record<string, unknown> = {}
|
|
616
|
-
fields.forEach((field) => {
|
|
617
|
-
const v = values[field.name]
|
|
618
|
-
if (typeof v === 'undefined') {
|
|
619
|
-
return
|
|
620
|
-
}
|
|
621
|
-
if (v === null) {
|
|
622
|
-
return
|
|
623
|
-
}
|
|
624
|
-
accum[field.name] = resolveFieldValue({
|
|
625
|
-
field,
|
|
626
|
-
value: v,
|
|
627
|
-
tinaSchema,
|
|
628
|
-
})
|
|
629
|
-
})
|
|
630
|
-
return accum as T & { __typename?: string }
|
|
631
|
-
}
|
|
632
|
-
const resolveFieldValue = ({
|
|
633
|
-
field,
|
|
634
|
-
value,
|
|
635
|
-
tinaSchema,
|
|
636
|
-
}: {
|
|
637
|
-
field: TinaFieldEnriched
|
|
638
|
-
value: unknown
|
|
639
|
-
tinaSchema: TinaSchema
|
|
640
|
-
}) => {
|
|
641
|
-
switch (field.type) {
|
|
642
|
-
case 'object': {
|
|
643
|
-
if (field.templates) {
|
|
644
|
-
if (field.list) {
|
|
645
|
-
if (Array.isArray(value)) {
|
|
646
|
-
return value.map((item) => {
|
|
647
|
-
const template = field.templates[item._template]
|
|
648
|
-
if (typeof template === 'string') {
|
|
649
|
-
throw new Error('Global templates not supported')
|
|
650
|
-
}
|
|
651
|
-
return {
|
|
652
|
-
__typename: NAMER.dataTypeName(template.namespace),
|
|
653
|
-
...resolveFormValue({
|
|
654
|
-
fields: template.fields,
|
|
655
|
-
values: item,
|
|
656
|
-
tinaSchema,
|
|
657
|
-
}),
|
|
658
|
-
}
|
|
659
|
-
})
|
|
660
|
-
}
|
|
661
|
-
} else {
|
|
662
|
-
// not implemented
|
|
663
|
-
}
|
|
664
|
-
}
|
|
665
|
-
|
|
666
|
-
const templateFields = field.fields
|
|
667
|
-
if (typeof templateFields === 'string') {
|
|
668
|
-
throw new Error('Global templates not supported')
|
|
669
|
-
}
|
|
670
|
-
if (!templateFields) {
|
|
671
|
-
throw new Error(`Expected to find sub-fields on field ${field.name}`)
|
|
672
|
-
}
|
|
673
|
-
if (field.list) {
|
|
674
|
-
if (Array.isArray(value)) {
|
|
675
|
-
return value.map((item) => {
|
|
676
|
-
return {
|
|
677
|
-
__typename: NAMER.dataTypeName(field.namespace),
|
|
678
|
-
...resolveFormValue({
|
|
679
|
-
fields: templateFields,
|
|
680
|
-
values: item,
|
|
681
|
-
tinaSchema,
|
|
682
|
-
}),
|
|
683
|
-
}
|
|
684
|
-
})
|
|
685
|
-
}
|
|
686
|
-
} else {
|
|
687
|
-
return {
|
|
688
|
-
__typename: NAMER.dataTypeName(field.namespace),
|
|
689
|
-
...resolveFormValue({
|
|
690
|
-
fields: templateFields,
|
|
691
|
-
values: value as any,
|
|
692
|
-
tinaSchema,
|
|
693
|
-
}),
|
|
694
|
-
}
|
|
695
|
-
}
|
|
696
|
-
}
|
|
697
|
-
default: {
|
|
698
|
-
return value
|
|
699
|
-
}
|
|
700
|
-
}
|
|
701
|
-
}
|