@sanity/assist 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +205 -0
- package/dist/index.d.ts +52 -0
- package/dist/index.esm.js +2341 -0
- package/dist/index.esm.js.map +1 -0
- package/dist/index.js +2341 -0
- package/dist/index.js.map +1 -0
- package/package.json +98 -0
- package/sanity.json +8 -0
- package/src/_lib/connector/ConnectFromRegion.tsx +24 -0
- package/src/_lib/connector/ConnectToRegion.tsx +22 -0
- package/src/_lib/connector/ConnectorRegion.tsx +23 -0
- package/src/_lib/connector/ConnectorsProvider.tsx +19 -0
- package/src/_lib/connector/ConnectorsStore.ts +122 -0
- package/src/_lib/connector/ConnectorsStoreContext.ts +4 -0
- package/src/_lib/connector/helpers.ts +5 -0
- package/src/_lib/connector/index.ts +9 -0
- package/src/_lib/connector/mapConnectorToLine.ts +83 -0
- package/src/_lib/connector/types.ts +56 -0
- package/src/_lib/connector/useConnectorsStore.ts +13 -0
- package/src/_lib/connector/useRegionRects.ts +141 -0
- package/src/_lib/fixedListenQuery.ts +101 -0
- package/src/_lib/form/DocumentForm.tsx +197 -0
- package/src/_lib/form/helpers.ts +31 -0
- package/src/_lib/form/index.ts +1 -0
- package/src/_lib/randomKey.ts +29 -0
- package/src/_lib/useListeningQuery.ts +61 -0
- package/src/_lib/usePrevious.ts +9 -0
- package/src/assistConnectors/AssistConnectorsOverlay.tsx +132 -0
- package/src/assistConnectors/ConnectorPath.tsx +62 -0
- package/src/assistConnectors/draw/arrowPath.ts +9 -0
- package/src/assistConnectors/draw/connectorPath.ts +142 -0
- package/src/assistConnectors/index.ts +1 -0
- package/src/assistDocument/AssistDocumentContext.tsx +31 -0
- package/src/assistDocument/AssistDocumentContextProvider.tsx +17 -0
- package/src/assistDocument/AssistDocumentInput.tsx +46 -0
- package/src/assistDocument/RequestRunInstructionProvider.tsx +50 -0
- package/src/assistDocument/components/AssistDocumentForm.tsx +188 -0
- package/src/assistDocument/components/FieldRefPreview.tsx +27 -0
- package/src/assistDocument/components/InstructionsArrayField.tsx +8 -0
- package/src/assistDocument/components/InstructionsArrayInput.tsx +26 -0
- package/src/assistDocument/components/SelectedFieldContext.tsx +10 -0
- package/src/assistDocument/components/generic/HiddenFieldTitle.tsx +5 -0
- package/src/assistDocument/components/helpers.ts +21 -0
- package/src/assistDocument/components/instruction/BackToInstructionsLink.tsx +31 -0
- package/src/assistDocument/components/instruction/FieldRefInput.tsx +33 -0
- package/src/assistDocument/components/instruction/InstructionInput.tsx +87 -0
- package/src/assistDocument/components/instruction/PromptInput.tsx +52 -0
- package/src/assistDocument/components/instruction/appearance/IconInput.tsx +46 -0
- package/src/assistDocument/components/instruction/appearance/InstructionVisibility.tsx +37 -0
- package/src/assistDocument/hooks/useAssistDocumentContextValue.tsx +68 -0
- package/src/assistDocument/hooks/useDocumentState.ts +6 -0
- package/src/assistDocument/hooks/useInstructionToaster.tsx +74 -0
- package/src/assistDocument/hooks/useStudioAssistDocument.ts +119 -0
- package/src/assistDocument/index.ts +1 -0
- package/src/assistFormComponents/AssistField.tsx +51 -0
- package/src/assistFormComponents/AssistFormBlock.tsx +31 -0
- package/src/assistFormComponents/AssistInlineFormBlock.tsx +14 -0
- package/src/assistFormComponents/AssistItem.tsx +20 -0
- package/src/assistFormComponents/validation/listItem.tsx +63 -0
- package/src/assistFormComponents/validation/validationList.tsx +89 -0
- package/src/assistInspector/AssistInspector.tsx +379 -0
- package/src/assistInspector/FieldAutocomplete.tsx +119 -0
- package/src/assistInspector/InstructionTaskHistoryButton.tsx +261 -0
- package/src/assistInspector/constants.ts +1 -0
- package/src/assistInspector/helpers.ts +125 -0
- package/src/assistInspector/index.ts +26 -0
- package/src/assistLayout/AiAssistanceConfigContext.tsx +81 -0
- package/src/assistLayout/AlphaMigration.tsx +311 -0
- package/src/assistLayout/AssistLayout.tsx +38 -0
- package/src/assistLayout/RunInstructionProvider.tsx +222 -0
- package/src/components/AssistFeatureBadge.tsx +9 -0
- package/src/components/Delay.tsx +25 -0
- package/src/components/HideReferenceChangedBannerInput.tsx +25 -0
- package/src/components/SafeValueInput.tsx +73 -0
- package/src/components/TimeAgo.tsx +18 -0
- package/src/constants.ts +20 -0
- package/src/fieldActions/PrivateIcon.tsx +20 -0
- package/src/fieldActions/assistFieldActions.tsx +230 -0
- package/src/globals.d.ts +4 -0
- package/src/helpers/assistSupported.ts +44 -0
- package/src/helpers/ids.ts +19 -0
- package/src/helpers/misc.ts +16 -0
- package/src/helpers/typeUtils.ts +15 -0
- package/src/helpers/useAssistSupported.ts +10 -0
- package/src/index.ts +6 -0
- package/src/legacy-types.ts +72 -0
- package/src/onboarding/FieldActionsOnboarding.tsx +90 -0
- package/src/onboarding/FirstAssistedPathProvider.tsx +29 -0
- package/src/onboarding/InspectorOnboarding.tsx +46 -0
- package/src/onboarding/onboardingStore.ts +33 -0
- package/src/plugin.tsx +80 -0
- package/src/presence/AiFieldPresence.tsx +28 -0
- package/src/presence/AssistAvatar.tsx +96 -0
- package/src/presence/AssistDocumentPresence.tsx +58 -0
- package/src/presence/useAssistPresence.ts +61 -0
- package/src/schemas/assistDocumentSchema.tsx +450 -0
- package/src/schemas/contextDocumentSchema.tsx +56 -0
- package/src/schemas/index.ts +25 -0
- package/src/schemas/serialize/SchemTypeTool.tsx +102 -0
- package/src/schemas/serialize/schemaUtils.ts +37 -0
- package/src/schemas/serialize/serializeSchema.test.ts +382 -0
- package/src/schemas/serialize/serializeSchema.ts +162 -0
- package/src/schemas/serializedSchemaTypeSchema.ts +59 -0
- package/src/schemas/typeDefExtensions.ts +30 -0
- package/src/types.ts +167 -0
- package/src/useApiClient.ts +140 -0
- package/src/vite.config.ts +9 -0
- package/v2-incompatible.js +11 -0
|
@@ -0,0 +1,382 @@
|
|
|
1
|
+
import {describe, expect, test} from 'vitest'
|
|
2
|
+
import {Schema} from '@sanity/schema'
|
|
3
|
+
import {serializeSchema} from './serializeSchema'
|
|
4
|
+
import {defineArrayMember, defineField, defineType} from 'sanity'
|
|
5
|
+
import {AssistOptions} from '../typeDefExtensions'
|
|
6
|
+
|
|
7
|
+
const mockStudioTypes = [
|
|
8
|
+
defineField({
|
|
9
|
+
type: 'object',
|
|
10
|
+
name: 'slug',
|
|
11
|
+
fields: [{type: 'string', name: '_ref'}],
|
|
12
|
+
}),
|
|
13
|
+
defineField({
|
|
14
|
+
type: 'object',
|
|
15
|
+
name: 'sanity.imageHotspot',
|
|
16
|
+
fields: [{type: 'string', name: 'whatever'}],
|
|
17
|
+
}),
|
|
18
|
+
defineField({
|
|
19
|
+
type: 'object',
|
|
20
|
+
name: 'sanity.imageCrop',
|
|
21
|
+
fields: [{type: 'string', name: 'whatever'}],
|
|
22
|
+
}),
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
describe('serializeSchema', () => {
|
|
26
|
+
test('should serialize simple schema', () => {
|
|
27
|
+
const schema = Schema.compile({
|
|
28
|
+
name: 'test',
|
|
29
|
+
types: [
|
|
30
|
+
{
|
|
31
|
+
type: 'document',
|
|
32
|
+
name: 'article',
|
|
33
|
+
fields: [
|
|
34
|
+
{type: 'string', name: 'title'},
|
|
35
|
+
{type: 'some', name: 'some'},
|
|
36
|
+
{
|
|
37
|
+
name: 'array',
|
|
38
|
+
of: [{type: 'some'}],
|
|
39
|
+
type: 'array',
|
|
40
|
+
},
|
|
41
|
+
],
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
type: 'object',
|
|
45
|
+
name: 'some',
|
|
46
|
+
fields: [{type: 'string', name: 'title'}],
|
|
47
|
+
},
|
|
48
|
+
],
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
const serializedTypes = serializeSchema(schema, {leanFormat: true})
|
|
52
|
+
|
|
53
|
+
expect(serializedTypes).toEqual([
|
|
54
|
+
{
|
|
55
|
+
fields: [
|
|
56
|
+
{name: 'title', title: 'Title', type: 'string'},
|
|
57
|
+
{name: 'some', title: 'Some', type: 'some'},
|
|
58
|
+
{
|
|
59
|
+
name: 'array',
|
|
60
|
+
of: [{name: 'some', title: 'Some', type: 'some'}],
|
|
61
|
+
title: 'Array',
|
|
62
|
+
type: 'array',
|
|
63
|
+
},
|
|
64
|
+
],
|
|
65
|
+
name: 'article',
|
|
66
|
+
title: 'Article',
|
|
67
|
+
type: 'document',
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
fields: [{name: 'title', title: 'Title', type: 'string'}],
|
|
71
|
+
name: 'some',
|
|
72
|
+
title: 'Some',
|
|
73
|
+
type: 'object',
|
|
74
|
+
},
|
|
75
|
+
])
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
test('should not serialize excluded fields or types or types with every member excluded', () => {
|
|
79
|
+
const options: AssistOptions = {aiWritingAssistance: {exclude: true}}
|
|
80
|
+
|
|
81
|
+
const schema = Schema.compile({
|
|
82
|
+
name: 'test',
|
|
83
|
+
types: [
|
|
84
|
+
{
|
|
85
|
+
type: 'document',
|
|
86
|
+
name: 'allFieldsExcluded',
|
|
87
|
+
fields: [
|
|
88
|
+
defineField({type: 'string', name: 'title', options}),
|
|
89
|
+
defineField({
|
|
90
|
+
type: 'object',
|
|
91
|
+
name: 'excludedObject',
|
|
92
|
+
// all fields excluded should exclude field
|
|
93
|
+
fields: [{type: 'string', name: 'title', options}],
|
|
94
|
+
options: {aiWritingAssistance: {exclude: true}},
|
|
95
|
+
}),
|
|
96
|
+
defineField({
|
|
97
|
+
type: 'array',
|
|
98
|
+
name: 'excludedArray',
|
|
99
|
+
// all items excluded should exclude field
|
|
100
|
+
of: [{type: 'object', name: 'remove', options}, {type: 'excluded'}],
|
|
101
|
+
options: {aiWritingAssistance: {exclude: true}},
|
|
102
|
+
}),
|
|
103
|
+
//image without extra fields should be excluded
|
|
104
|
+
defineField({type: 'image', name: 'image'}),
|
|
105
|
+
// unsupported types
|
|
106
|
+
defineField({type: 'number', name: 'number'}),
|
|
107
|
+
defineField({type: 'slug', name: 'slug'}),
|
|
108
|
+
defineField({type: 'url', name: 'url'}),
|
|
109
|
+
defineField({type: 'datetime', name: 'datetime'}),
|
|
110
|
+
defineField({type: 'file', name: 'file'}),
|
|
111
|
+
defineField({type: 'reference', name: 'reference', to: [{type: 'excluded'}]}),
|
|
112
|
+
defineField({
|
|
113
|
+
type: 'crossDatasetReference',
|
|
114
|
+
name: 'crossDatasetReference',
|
|
115
|
+
to: [{type: 'excluded'}],
|
|
116
|
+
dataset: 'x',
|
|
117
|
+
projectId: 'y',
|
|
118
|
+
}),
|
|
119
|
+
],
|
|
120
|
+
},
|
|
121
|
+
defineField({
|
|
122
|
+
type: 'object',
|
|
123
|
+
name: 'excluded',
|
|
124
|
+
fields: [{type: 'string', name: 'title', options}],
|
|
125
|
+
options: {aiWritingAssistance: {exclude: true}},
|
|
126
|
+
}),
|
|
127
|
+
...mockStudioTypes,
|
|
128
|
+
],
|
|
129
|
+
})
|
|
130
|
+
|
|
131
|
+
const serializedTypes = serializeSchema(schema, {leanFormat: true})
|
|
132
|
+
|
|
133
|
+
//everything excluded directly or indirectly
|
|
134
|
+
expect(serializedTypes).toEqual([])
|
|
135
|
+
})
|
|
136
|
+
|
|
137
|
+
test('should serialize opt-in inline object using custom typeName', () => {
|
|
138
|
+
const schema = Schema.compile({
|
|
139
|
+
name: 'test',
|
|
140
|
+
types: [
|
|
141
|
+
{
|
|
142
|
+
type: 'document',
|
|
143
|
+
name: 'article',
|
|
144
|
+
fields: [
|
|
145
|
+
{type: 'string', name: 'title'},
|
|
146
|
+
defineField({
|
|
147
|
+
type: 'object',
|
|
148
|
+
name: 'some',
|
|
149
|
+
fields: [{type: 'string', name: 'title'}],
|
|
150
|
+
}),
|
|
151
|
+
defineField({
|
|
152
|
+
type: 'array',
|
|
153
|
+
name: 'array',
|
|
154
|
+
of: [
|
|
155
|
+
defineArrayMember({
|
|
156
|
+
type: 'object',
|
|
157
|
+
name: 'inlineArrayMember',
|
|
158
|
+
fields: [{type: 'string', name: 'title'}],
|
|
159
|
+
}),
|
|
160
|
+
],
|
|
161
|
+
}),
|
|
162
|
+
],
|
|
163
|
+
},
|
|
164
|
+
],
|
|
165
|
+
})
|
|
166
|
+
|
|
167
|
+
const serializedTypes = serializeSchema(schema, {leanFormat: true})
|
|
168
|
+
|
|
169
|
+
expect(serializedTypes).toEqual([
|
|
170
|
+
{
|
|
171
|
+
fields: [
|
|
172
|
+
{name: 'title', title: 'Title', type: 'string'},
|
|
173
|
+
{
|
|
174
|
+
name: 'some',
|
|
175
|
+
title: 'Some',
|
|
176
|
+
type: 'object',
|
|
177
|
+
fields: [{name: 'title', title: 'Title', type: 'string'}],
|
|
178
|
+
},
|
|
179
|
+
{
|
|
180
|
+
name: 'array',
|
|
181
|
+
of: [
|
|
182
|
+
{
|
|
183
|
+
type: 'object',
|
|
184
|
+
name: 'inlineArrayMember',
|
|
185
|
+
title: 'Inline Array Member',
|
|
186
|
+
fields: [{name: 'title', title: 'Title', type: 'string'}],
|
|
187
|
+
},
|
|
188
|
+
],
|
|
189
|
+
title: 'Array',
|
|
190
|
+
type: 'array',
|
|
191
|
+
},
|
|
192
|
+
],
|
|
193
|
+
name: 'article',
|
|
194
|
+
title: 'Article',
|
|
195
|
+
type: 'document',
|
|
196
|
+
},
|
|
197
|
+
])
|
|
198
|
+
})
|
|
199
|
+
|
|
200
|
+
test('should serialize inline object', () => {
|
|
201
|
+
const schema = Schema.compile({
|
|
202
|
+
name: 'test',
|
|
203
|
+
types: [
|
|
204
|
+
{
|
|
205
|
+
type: 'document',
|
|
206
|
+
name: 'article',
|
|
207
|
+
fields: [
|
|
208
|
+
{type: 'string', name: 'title'},
|
|
209
|
+
defineField({
|
|
210
|
+
type: 'object',
|
|
211
|
+
name: 'someObject',
|
|
212
|
+
fields: [{type: 'string', name: 'title'}],
|
|
213
|
+
}),
|
|
214
|
+
defineField({
|
|
215
|
+
type: 'file',
|
|
216
|
+
name: 'someFile',
|
|
217
|
+
fields: [{type: 'string', name: 'title'}],
|
|
218
|
+
}),
|
|
219
|
+
defineField({
|
|
220
|
+
type: 'image',
|
|
221
|
+
name: 'someImage',
|
|
222
|
+
fields: [{type: 'string', name: 'title'}],
|
|
223
|
+
}),
|
|
224
|
+
],
|
|
225
|
+
},
|
|
226
|
+
...mockStudioTypes,
|
|
227
|
+
],
|
|
228
|
+
})
|
|
229
|
+
|
|
230
|
+
const serializedTypes = serializeSchema(schema, {leanFormat: true})
|
|
231
|
+
|
|
232
|
+
expect(serializedTypes).toEqual([
|
|
233
|
+
{
|
|
234
|
+
fields: [
|
|
235
|
+
{name: 'title', title: 'Title', type: 'string'},
|
|
236
|
+
{
|
|
237
|
+
name: 'someObject',
|
|
238
|
+
title: 'Some Object',
|
|
239
|
+
type: 'object',
|
|
240
|
+
fields: [{name: 'title', title: 'Title', type: 'string'}],
|
|
241
|
+
},
|
|
242
|
+
{
|
|
243
|
+
name: 'someImage',
|
|
244
|
+
title: 'Some Image',
|
|
245
|
+
type: 'image',
|
|
246
|
+
fields: [{name: 'title', title: 'Title', type: 'string'}],
|
|
247
|
+
},
|
|
248
|
+
],
|
|
249
|
+
name: 'article',
|
|
250
|
+
title: 'Article',
|
|
251
|
+
type: 'document',
|
|
252
|
+
},
|
|
253
|
+
])
|
|
254
|
+
})
|
|
255
|
+
|
|
256
|
+
test('should serialize inline, anonymous array object type', () => {
|
|
257
|
+
const schema = Schema.compile({
|
|
258
|
+
name: 'test',
|
|
259
|
+
types: [
|
|
260
|
+
{
|
|
261
|
+
type: 'document',
|
|
262
|
+
name: 'article',
|
|
263
|
+
fields: [
|
|
264
|
+
{type: 'string', name: 'title'},
|
|
265
|
+
defineField({
|
|
266
|
+
type: 'array',
|
|
267
|
+
name: 'array',
|
|
268
|
+
of: [
|
|
269
|
+
defineArrayMember({
|
|
270
|
+
type: 'object',
|
|
271
|
+
title: 'Nameless item',
|
|
272
|
+
fields: [{type: 'string', name: 'title'}],
|
|
273
|
+
}),
|
|
274
|
+
],
|
|
275
|
+
}),
|
|
276
|
+
],
|
|
277
|
+
},
|
|
278
|
+
{
|
|
279
|
+
fields: [{name: 'title', title: 'Title', type: 'string'}],
|
|
280
|
+
name: 'item',
|
|
281
|
+
title: 'Item',
|
|
282
|
+
type: 'object',
|
|
283
|
+
},
|
|
284
|
+
],
|
|
285
|
+
})
|
|
286
|
+
|
|
287
|
+
const serializedTypes = serializeSchema(schema, {leanFormat: true})
|
|
288
|
+
|
|
289
|
+
expect(serializedTypes).toEqual([
|
|
290
|
+
{
|
|
291
|
+
fields: [
|
|
292
|
+
{name: 'title', title: 'Title', type: 'string'},
|
|
293
|
+
{
|
|
294
|
+
name: 'array',
|
|
295
|
+
of: [
|
|
296
|
+
{
|
|
297
|
+
name: 'object',
|
|
298
|
+
title: 'Nameless item',
|
|
299
|
+
type: 'object',
|
|
300
|
+
fields: [{name: 'title', title: 'Title', type: 'string'}],
|
|
301
|
+
},
|
|
302
|
+
],
|
|
303
|
+
title: 'Array',
|
|
304
|
+
type: 'array',
|
|
305
|
+
},
|
|
306
|
+
],
|
|
307
|
+
name: 'article',
|
|
308
|
+
title: 'Article',
|
|
309
|
+
type: 'document',
|
|
310
|
+
},
|
|
311
|
+
{
|
|
312
|
+
fields: [{name: 'title', title: 'Title', type: 'string'}],
|
|
313
|
+
name: 'item',
|
|
314
|
+
title: 'Item',
|
|
315
|
+
type: 'object',
|
|
316
|
+
},
|
|
317
|
+
])
|
|
318
|
+
})
|
|
319
|
+
|
|
320
|
+
test('should serialize nested inline objects', () => {
|
|
321
|
+
const schema = Schema.compile({
|
|
322
|
+
name: 'test',
|
|
323
|
+
types: [
|
|
324
|
+
{
|
|
325
|
+
type: 'document',
|
|
326
|
+
name: 'article',
|
|
327
|
+
fields: [
|
|
328
|
+
defineField({
|
|
329
|
+
type: 'object',
|
|
330
|
+
name: 'outer',
|
|
331
|
+
fields: [
|
|
332
|
+
defineField({
|
|
333
|
+
type: 'object',
|
|
334
|
+
name: 'inner',
|
|
335
|
+
fields: [{type: 'string', name: 'title'}],
|
|
336
|
+
}),
|
|
337
|
+
],
|
|
338
|
+
}),
|
|
339
|
+
],
|
|
340
|
+
},
|
|
341
|
+
],
|
|
342
|
+
})
|
|
343
|
+
|
|
344
|
+
const serializedTypes = serializeSchema(schema, {leanFormat: true})
|
|
345
|
+
|
|
346
|
+
expect(serializedTypes).toEqual([
|
|
347
|
+
{
|
|
348
|
+
name: 'article',
|
|
349
|
+
title: 'Article',
|
|
350
|
+
type: 'document',
|
|
351
|
+
fields: [
|
|
352
|
+
{
|
|
353
|
+
fields: [
|
|
354
|
+
{
|
|
355
|
+
type: 'object',
|
|
356
|
+
name: 'inner',
|
|
357
|
+
title: 'Inner',
|
|
358
|
+
fields: [{name: 'title', title: 'Title', type: 'string'}],
|
|
359
|
+
},
|
|
360
|
+
],
|
|
361
|
+
type: 'object',
|
|
362
|
+
name: 'outer',
|
|
363
|
+
title: 'Outer',
|
|
364
|
+
},
|
|
365
|
+
],
|
|
366
|
+
},
|
|
367
|
+
])
|
|
368
|
+
})
|
|
369
|
+
|
|
370
|
+
test('should serialize list values', () => {
|
|
371
|
+
const schema = Schema.compile({
|
|
372
|
+
name: 'test',
|
|
373
|
+
types: [defineType({type: 'string', name: 'list', options: {list: ['a', 'b']}})],
|
|
374
|
+
})
|
|
375
|
+
|
|
376
|
+
const serializedTypes = serializeSchema(schema, {leanFormat: true})
|
|
377
|
+
|
|
378
|
+
expect(serializedTypes).toEqual([
|
|
379
|
+
{name: 'list', title: 'String', type: 'string', values: ['a', 'b']},
|
|
380
|
+
])
|
|
381
|
+
})
|
|
382
|
+
})
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ArraySchemaType,
|
|
3
|
+
ImageOptions,
|
|
4
|
+
ObjectSchemaType,
|
|
5
|
+
ReferenceSchemaType,
|
|
6
|
+
Schema,
|
|
7
|
+
SchemaType,
|
|
8
|
+
typed,
|
|
9
|
+
} from 'sanity'
|
|
10
|
+
import {
|
|
11
|
+
assistSerializedFieldTypeName,
|
|
12
|
+
assistSerializedTypeName,
|
|
13
|
+
SerializedSchemaMember,
|
|
14
|
+
SerializedSchemaType,
|
|
15
|
+
assistSchemaIdPrefix,
|
|
16
|
+
} from '../../types'
|
|
17
|
+
import {hiddenTypes} from './schemaUtils'
|
|
18
|
+
import {isAssistSupported} from '../../helpers/assistSupported'
|
|
19
|
+
|
|
20
|
+
interface Options {
|
|
21
|
+
leanFormat?: boolean
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const inlineTypes = ['document', 'object', 'image', 'file']
|
|
25
|
+
|
|
26
|
+
export function serializeSchema(schema: Schema, options?: Options): SerializedSchemaType[] {
|
|
27
|
+
const list = schema
|
|
28
|
+
.getTypeNames()
|
|
29
|
+
.filter((t) => !(hiddenTypes.includes(t) || t.startsWith('sanity.')))
|
|
30
|
+
.map((t) => schema.get(t))
|
|
31
|
+
.filter((t): t is SchemaType => !!t)
|
|
32
|
+
.map((t) => getSchemaStub(t, schema, options))
|
|
33
|
+
.filter((t) => {
|
|
34
|
+
if ('to' in t && t.to && !t.to.length) {
|
|
35
|
+
return false
|
|
36
|
+
}
|
|
37
|
+
if ('of' in t && t.of && !t.of.length) {
|
|
38
|
+
return false
|
|
39
|
+
}
|
|
40
|
+
if ('fields' in t && t.fields && !t.fields.length) {
|
|
41
|
+
return false
|
|
42
|
+
}
|
|
43
|
+
return true
|
|
44
|
+
})
|
|
45
|
+
list.sort((a, b) => (a?.name ?? '').localeCompare(b?.name ?? ''))
|
|
46
|
+
return list
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function getSchemaStub(
|
|
50
|
+
schemaType: SchemaType,
|
|
51
|
+
schema: Schema,
|
|
52
|
+
options?: Options
|
|
53
|
+
): SerializedSchemaType {
|
|
54
|
+
if (!schemaType.type?.name) {
|
|
55
|
+
console.error('Missing type name', schemaType.type)
|
|
56
|
+
throw new Error('Type is missing name!')
|
|
57
|
+
}
|
|
58
|
+
const baseSchema: SerializedSchemaType = {
|
|
59
|
+
// we dont need type or id when we send using POST, so leave these out to save bandwidth
|
|
60
|
+
...(options?.leanFormat
|
|
61
|
+
? {}
|
|
62
|
+
: {_id: `${assistSchemaIdPrefix}${schemaType.name}`, _type: assistSerializedTypeName}),
|
|
63
|
+
name: schemaType.name,
|
|
64
|
+
title: schemaType.title,
|
|
65
|
+
type: schemaType.type.name,
|
|
66
|
+
...getBaseFields(schema, schemaType, schemaType.type.name, options),
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return removeUndef(baseSchema)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function getBaseFields(
|
|
73
|
+
schema: Schema,
|
|
74
|
+
type: SchemaType,
|
|
75
|
+
typeName: string,
|
|
76
|
+
options: Options | undefined
|
|
77
|
+
) {
|
|
78
|
+
const imagePromptField = (type.options as ImageOptions)?.imagePromptField
|
|
79
|
+
return removeUndef({
|
|
80
|
+
options: imagePromptField
|
|
81
|
+
? {
|
|
82
|
+
imagePromptField: imagePromptField,
|
|
83
|
+
}
|
|
84
|
+
: undefined,
|
|
85
|
+
values:
|
|
86
|
+
type.jsonType === 'string' && type?.options?.list
|
|
87
|
+
? type?.options?.list.map((v) => (typeof v === 'string' ? v : v.value ?? `${v.title}`))
|
|
88
|
+
: undefined,
|
|
89
|
+
of: 'of' in type && typeName === 'array' ? arrayOf(type, schema, options) : undefined,
|
|
90
|
+
to:
|
|
91
|
+
'to' in type && typeName === 'reference'
|
|
92
|
+
? refToTypeNames(type as ReferenceSchemaType)
|
|
93
|
+
: undefined,
|
|
94
|
+
fields:
|
|
95
|
+
'fields' in type && inlineTypes.includes(typeName)
|
|
96
|
+
? serializeFields(schema, type, options)
|
|
97
|
+
: undefined,
|
|
98
|
+
})
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function serializeFields(
|
|
102
|
+
schema: Schema,
|
|
103
|
+
schemaType: ObjectSchemaType,
|
|
104
|
+
options: Options | undefined
|
|
105
|
+
) {
|
|
106
|
+
return schemaType.fields
|
|
107
|
+
.filter((f) => !['sanity.imageHotspot', 'sanity.imageCrop'].includes(f.type?.name ?? ''))
|
|
108
|
+
.filter((f) => isAssistSupported(f.type))
|
|
109
|
+
.map((field) => serializeMember(schema, field.type, field.name, options))
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function serializeMember(
|
|
113
|
+
schema: Schema,
|
|
114
|
+
type: SchemaType,
|
|
115
|
+
name: string,
|
|
116
|
+
options: Options | undefined
|
|
117
|
+
): SerializedSchemaMember {
|
|
118
|
+
const typeNameExists = !!schema.get(type?.name)
|
|
119
|
+
const typeName = typeNameExists ? type.name : type.type?.name ?? ''
|
|
120
|
+
return removeUndef({
|
|
121
|
+
...(options?.leanFormat ? {} : {_type: assistSerializedFieldTypeName}),
|
|
122
|
+
name: name,
|
|
123
|
+
type: typeName,
|
|
124
|
+
title: type.title,
|
|
125
|
+
values:
|
|
126
|
+
type.jsonType === 'string' && type?.options?.list
|
|
127
|
+
? type?.options?.list.map((v) => (typeof v === 'string' ? v : v.value ?? `${v.title}`))
|
|
128
|
+
: undefined,
|
|
129
|
+
of: 'of' in type && type.name === 'array' ? arrayOf(type, schema, options) : undefined,
|
|
130
|
+
to:
|
|
131
|
+
'to' in type && type.name === 'reference'
|
|
132
|
+
? refToTypeNames(type as ReferenceSchemaType)
|
|
133
|
+
: undefined,
|
|
134
|
+
fields:
|
|
135
|
+
'fields' in type && inlineTypes.includes(typeName)
|
|
136
|
+
? serializeFields(schema, type, options)
|
|
137
|
+
: undefined,
|
|
138
|
+
})
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function arrayOf(
|
|
142
|
+
arrayType: ArraySchemaType,
|
|
143
|
+
schema: Schema,
|
|
144
|
+
options: Options | undefined
|
|
145
|
+
): SerializedSchemaMember[] {
|
|
146
|
+
return arrayType.of
|
|
147
|
+
.filter((type) => isAssistSupported(type))
|
|
148
|
+
.map((t) => {
|
|
149
|
+
return serializeMember(schema, t, t.name, options)
|
|
150
|
+
})
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function refToTypeNames(type: ReferenceSchemaType) {
|
|
154
|
+
return type.to.map((t) => ({
|
|
155
|
+
type: typed<string>(t.name),
|
|
156
|
+
}))
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function removeUndef<T extends Record<string, any>>(obj: T): T {
|
|
160
|
+
Object.keys(obj).forEach((key) => (obj[key] === undefined ? delete obj[key] : {}))
|
|
161
|
+
return obj
|
|
162
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import {defineField, defineType} from 'sanity'
|
|
2
|
+
import {assistSerializedFieldTypeName, assistSerializedTypeName} from '../types'
|
|
3
|
+
import {CodeIcon, StarIcon} from '@sanity/icons'
|
|
4
|
+
|
|
5
|
+
export const serializedSchemaField = defineType({
|
|
6
|
+
type: 'object',
|
|
7
|
+
name: assistSerializedFieldTypeName,
|
|
8
|
+
title: 'Field',
|
|
9
|
+
icon: CodeIcon,
|
|
10
|
+
fields: [
|
|
11
|
+
defineField({
|
|
12
|
+
name: 'name',
|
|
13
|
+
title: 'Name',
|
|
14
|
+
type: 'string',
|
|
15
|
+
}),
|
|
16
|
+
defineField({
|
|
17
|
+
name: 'type',
|
|
18
|
+
title: 'Type',
|
|
19
|
+
type: 'string',
|
|
20
|
+
}),
|
|
21
|
+
],
|
|
22
|
+
preview: {
|
|
23
|
+
select: {
|
|
24
|
+
title: 'title',
|
|
25
|
+
subtitle: 'type',
|
|
26
|
+
},
|
|
27
|
+
prepare: ({title, subtitle}) => {
|
|
28
|
+
return {
|
|
29
|
+
title,
|
|
30
|
+
subtitle,
|
|
31
|
+
media: CodeIcon,
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
export const serializedSchemaType = defineType({
|
|
38
|
+
name: assistSerializedTypeName,
|
|
39
|
+
type: 'document',
|
|
40
|
+
title: 'Meta schema',
|
|
41
|
+
icon: StarIcon,
|
|
42
|
+
fields: [
|
|
43
|
+
defineField({
|
|
44
|
+
name: 'name',
|
|
45
|
+
title: 'Name',
|
|
46
|
+
type: 'string',
|
|
47
|
+
}),
|
|
48
|
+
defineField({
|
|
49
|
+
name: 'type',
|
|
50
|
+
title: 'Type',
|
|
51
|
+
type: 'string',
|
|
52
|
+
}),
|
|
53
|
+
defineField({
|
|
54
|
+
name: 'fields',
|
|
55
|
+
type: 'array',
|
|
56
|
+
of: [{type: serializedSchemaField.name}],
|
|
57
|
+
}),
|
|
58
|
+
],
|
|
59
|
+
})
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/* eslint-disable no-unused-vars */
|
|
2
|
+
export interface AssistOptions {
|
|
3
|
+
aiWritingAssistance?: {
|
|
4
|
+
/** Set to true to disable assistance for this field or type */
|
|
5
|
+
exclude?: boolean
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
declare module 'sanity' {
|
|
10
|
+
interface ArrayOptions extends AssistOptions {}
|
|
11
|
+
interface BlockOptions extends AssistOptions {}
|
|
12
|
+
interface BooleanOptions extends AssistOptions {}
|
|
13
|
+
interface CrossDatasetReferenceOptions extends AssistOptions {}
|
|
14
|
+
interface DateOptions extends AssistOptions {}
|
|
15
|
+
interface DatetimeOptions extends AssistOptions {}
|
|
16
|
+
interface DocumentOptions extends AssistOptions {}
|
|
17
|
+
interface FileOptions extends AssistOptions {}
|
|
18
|
+
interface GeopointOptions extends AssistOptions {}
|
|
19
|
+
interface ImageOptions extends AssistOptions {
|
|
20
|
+
imagePromptField?: string
|
|
21
|
+
}
|
|
22
|
+
interface NumberOptions extends AssistOptions {}
|
|
23
|
+
interface ObjectOptions extends AssistOptions {}
|
|
24
|
+
interface ReferenceBaseOptions extends AssistOptions {}
|
|
25
|
+
interface SlugOptions extends AssistOptions {}
|
|
26
|
+
interface StringOptions extends AssistOptions {}
|
|
27
|
+
interface TextOptions extends AssistOptions {}
|
|
28
|
+
interface UrlOptions extends AssistOptions {}
|
|
29
|
+
interface EmailOptions extends AssistOptions {}
|
|
30
|
+
}
|