@seshuk/payload-plugin-openapi 0.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.
- package/LICENSE +21 -0
- package/README.md +595 -0
- package/dist/bin/generateSpec.d.ts +25 -0
- package/dist/bin/generateSpec.js +61 -0
- package/dist/constants.d.ts +1 -0
- package/dist/constants.js +2 -0
- package/dist/endpoints/interactiveAuth.d.ts +4 -0
- package/dist/endpoints/interactiveAuth.js +27 -0
- package/dist/endpoints/spec.d.ts +5 -0
- package/dist/endpoints/spec.js +39 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +67 -0
- package/dist/options.d.ts +2 -0
- package/dist/options.js +44 -0
- package/dist/spec/build.d.ts +14 -0
- package/dist/spec/build.js +36 -0
- package/dist/spec/buildDocument.d.ts +15 -0
- package/dist/spec/buildDocument.js +234 -0
- package/dist/spec/components.d.ts +46 -0
- package/dist/spec/components.js +280 -0
- package/dist/spec/downconvert.d.ts +3 -0
- package/dist/spec/downconvert.js +55 -0
- package/dist/spec/entitySchemas.d.ts +15 -0
- package/dist/spec/entitySchemas.js +246 -0
- package/dist/spec/fields.d.ts +5 -0
- package/dist/spec/fields.js +44 -0
- package/dist/spec/filters.d.ts +11 -0
- package/dist/spec/filters.js +74 -0
- package/dist/spec/names.d.ts +14 -0
- package/dist/spec/names.js +37 -0
- package/dist/spec/params.d.ts +47 -0
- package/dist/spec/params.js +344 -0
- package/dist/spec/paths/auth.d.ts +9 -0
- package/dist/spec/paths/auth.js +285 -0
- package/dist/spec/paths/collections.d.ts +7 -0
- package/dist/spec/paths/collections.js +261 -0
- package/dist/spec/paths/custom.d.ts +9 -0
- package/dist/spec/paths/custom.js +41 -0
- package/dist/spec/paths/globals.d.ts +7 -0
- package/dist/spec/paths/globals.js +68 -0
- package/dist/spec/paths/jobs.d.ts +7 -0
- package/dist/spec/paths/jobs.js +130 -0
- package/dist/spec/paths/versions.d.ts +18 -0
- package/dist/spec/paths/versions.js +243 -0
- package/dist/spec/tags.d.ts +27 -0
- package/dist/spec/tags.js +79 -0
- package/dist/translations/index.d.ts +6 -0
- package/dist/translations/index.js +96 -0
- package/dist/translations/locales/ar.d.ts +2 -0
- package/dist/translations/locales/ar.js +76 -0
- package/dist/translations/locales/az.d.ts +2 -0
- package/dist/translations/locales/az.js +76 -0
- package/dist/translations/locales/bg.d.ts +2 -0
- package/dist/translations/locales/bg.js +76 -0
- package/dist/translations/locales/bnBd.d.ts +2 -0
- package/dist/translations/locales/bnBd.js +76 -0
- package/dist/translations/locales/bnIn.d.ts +2 -0
- package/dist/translations/locales/bnIn.js +76 -0
- package/dist/translations/locales/ca.d.ts +2 -0
- package/dist/translations/locales/ca.js +76 -0
- package/dist/translations/locales/cs.d.ts +2 -0
- package/dist/translations/locales/cs.js +76 -0
- package/dist/translations/locales/da.d.ts +2 -0
- package/dist/translations/locales/da.js +76 -0
- package/dist/translations/locales/de.d.ts +2 -0
- package/dist/translations/locales/de.js +76 -0
- package/dist/translations/locales/en.d.ts +2 -0
- package/dist/translations/locales/en.js +76 -0
- package/dist/translations/locales/es.d.ts +2 -0
- package/dist/translations/locales/es.js +76 -0
- package/dist/translations/locales/et.d.ts +2 -0
- package/dist/translations/locales/et.js +76 -0
- package/dist/translations/locales/fa.d.ts +2 -0
- package/dist/translations/locales/fa.js +76 -0
- package/dist/translations/locales/fr.d.ts +2 -0
- package/dist/translations/locales/fr.js +76 -0
- package/dist/translations/locales/he.d.ts +2 -0
- package/dist/translations/locales/he.js +76 -0
- package/dist/translations/locales/hr.d.ts +2 -0
- package/dist/translations/locales/hr.js +76 -0
- package/dist/translations/locales/hu.d.ts +2 -0
- package/dist/translations/locales/hu.js +76 -0
- package/dist/translations/locales/hy.d.ts +2 -0
- package/dist/translations/locales/hy.js +76 -0
- package/dist/translations/locales/id.d.ts +2 -0
- package/dist/translations/locales/id.js +76 -0
- package/dist/translations/locales/is.d.ts +2 -0
- package/dist/translations/locales/is.js +76 -0
- package/dist/translations/locales/it.d.ts +2 -0
- package/dist/translations/locales/it.js +76 -0
- package/dist/translations/locales/ja.d.ts +2 -0
- package/dist/translations/locales/ja.js +76 -0
- package/dist/translations/locales/ko.d.ts +2 -0
- package/dist/translations/locales/ko.js +76 -0
- package/dist/translations/locales/lt.d.ts +2 -0
- package/dist/translations/locales/lt.js +76 -0
- package/dist/translations/locales/lv.d.ts +2 -0
- package/dist/translations/locales/lv.js +76 -0
- package/dist/translations/locales/my.d.ts +2 -0
- package/dist/translations/locales/my.js +76 -0
- package/dist/translations/locales/nb.d.ts +2 -0
- package/dist/translations/locales/nb.js +76 -0
- package/dist/translations/locales/nl.d.ts +2 -0
- package/dist/translations/locales/nl.js +76 -0
- package/dist/translations/locales/pl.d.ts +2 -0
- package/dist/translations/locales/pl.js +76 -0
- package/dist/translations/locales/pt.d.ts +2 -0
- package/dist/translations/locales/pt.js +76 -0
- package/dist/translations/locales/ro.d.ts +2 -0
- package/dist/translations/locales/ro.js +76 -0
- package/dist/translations/locales/rs.d.ts +2 -0
- package/dist/translations/locales/rs.js +76 -0
- package/dist/translations/locales/rsLatin.d.ts +2 -0
- package/dist/translations/locales/rsLatin.js +76 -0
- package/dist/translations/locales/ru.d.ts +2 -0
- package/dist/translations/locales/ru.js +76 -0
- package/dist/translations/locales/sk.d.ts +2 -0
- package/dist/translations/locales/sk.js +76 -0
- package/dist/translations/locales/sl.d.ts +2 -0
- package/dist/translations/locales/sl.js +76 -0
- package/dist/translations/locales/sv.d.ts +2 -0
- package/dist/translations/locales/sv.js +76 -0
- package/dist/translations/locales/ta.d.ts +2 -0
- package/dist/translations/locales/ta.js +76 -0
- package/dist/translations/locales/th.d.ts +2 -0
- package/dist/translations/locales/th.js +76 -0
- package/dist/translations/locales/tr.d.ts +2 -0
- package/dist/translations/locales/tr.js +76 -0
- package/dist/translations/locales/uk.d.ts +2 -0
- package/dist/translations/locales/uk.js +76 -0
- package/dist/translations/locales/vi.d.ts +2 -0
- package/dist/translations/locales/vi.js +76 -0
- package/dist/translations/locales/zh.d.ts +2 -0
- package/dist/translations/locales/zh.js +76 -0
- package/dist/translations/locales/zhTw.d.ts +2 -0
- package/dist/translations/locales/zhTw.js +76 -0
- package/dist/translations/types.d.ts +82 -0
- package/dist/translations/types.js +1 -0
- package/dist/types.d.ts +183 -0
- package/dist/types.js +1 -0
- package/dist/ui/createUiPlugin.d.ts +14 -0
- package/dist/ui/createUiPlugin.js +28 -0
- package/dist/ui/html.d.ts +8 -0
- package/dist/ui/html.js +8 -0
- package/dist/ui/scalar.d.ts +1 -0
- package/dist/ui/scalar.js +12 -0
- package/dist/ui/swagger.d.ts +1 -0
- package/dist/ui/swagger.js +12 -0
- package/dist/utils.d.ts +3 -0
- package/dist/utils.js +13 -0
- package/package.json +84 -0
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
import { refTo } from "./names.js";
|
|
2
|
+
const nullableType = (type)=>({
|
|
3
|
+
type: [
|
|
4
|
+
type,
|
|
5
|
+
'null'
|
|
6
|
+
]
|
|
7
|
+
});
|
|
8
|
+
const jsonResponse = (description, schema)=>({
|
|
9
|
+
description,
|
|
10
|
+
content: {
|
|
11
|
+
'application/json': {
|
|
12
|
+
schema
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
const jsonOk = (description, schema)=>({
|
|
17
|
+
200: jsonResponse(description, schema)
|
|
18
|
+
});
|
|
19
|
+
const jsonBody = (schema)=>({
|
|
20
|
+
required: true,
|
|
21
|
+
content: {
|
|
22
|
+
'application/json': {
|
|
23
|
+
schema
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
const messageResponse = (description)=>jsonOk(description, {
|
|
28
|
+
type: 'object',
|
|
29
|
+
properties: {
|
|
30
|
+
message: {
|
|
31
|
+
type: 'string'
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
const ERROR_SCHEMA_NAME = 'ErrorResponse';
|
|
36
|
+
const ERROR_KEYS = {
|
|
37
|
+
400: 'error400',
|
|
38
|
+
401: 'error401',
|
|
39
|
+
403: 'error403',
|
|
40
|
+
404: 'error404',
|
|
41
|
+
500: 'error500'
|
|
42
|
+
};
|
|
43
|
+
const ERRORS = {
|
|
44
|
+
list: [
|
|
45
|
+
'400',
|
|
46
|
+
'403',
|
|
47
|
+
'500'
|
|
48
|
+
],
|
|
49
|
+
read: [
|
|
50
|
+
'403',
|
|
51
|
+
'404',
|
|
52
|
+
'500'
|
|
53
|
+
],
|
|
54
|
+
create: [
|
|
55
|
+
'400',
|
|
56
|
+
'403',
|
|
57
|
+
'500'
|
|
58
|
+
],
|
|
59
|
+
update: [
|
|
60
|
+
'400',
|
|
61
|
+
'403',
|
|
62
|
+
'404',
|
|
63
|
+
'500'
|
|
64
|
+
],
|
|
65
|
+
delete: [
|
|
66
|
+
'403',
|
|
67
|
+
'404',
|
|
68
|
+
'500'
|
|
69
|
+
],
|
|
70
|
+
globalRead: [
|
|
71
|
+
'403',
|
|
72
|
+
'500'
|
|
73
|
+
],
|
|
74
|
+
globalUpdate: [
|
|
75
|
+
'400',
|
|
76
|
+
'403',
|
|
77
|
+
'500'
|
|
78
|
+
],
|
|
79
|
+
login: [
|
|
80
|
+
'400',
|
|
81
|
+
'401',
|
|
82
|
+
'403',
|
|
83
|
+
'500'
|
|
84
|
+
],
|
|
85
|
+
session: [
|
|
86
|
+
'401',
|
|
87
|
+
'500'
|
|
88
|
+
],
|
|
89
|
+
authAction: [
|
|
90
|
+
'400',
|
|
91
|
+
'403',
|
|
92
|
+
'500'
|
|
93
|
+
],
|
|
94
|
+
versionList: [
|
|
95
|
+
'400',
|
|
96
|
+
'403',
|
|
97
|
+
'404',
|
|
98
|
+
'500'
|
|
99
|
+
],
|
|
100
|
+
versionRead: [
|
|
101
|
+
'403',
|
|
102
|
+
'404',
|
|
103
|
+
'500'
|
|
104
|
+
],
|
|
105
|
+
versionRestore: [
|
|
106
|
+
'403',
|
|
107
|
+
'404',
|
|
108
|
+
'500'
|
|
109
|
+
]
|
|
110
|
+
};
|
|
111
|
+
const buildErrorResponseSchema = ()=>({
|
|
112
|
+
type: 'object',
|
|
113
|
+
properties: {
|
|
114
|
+
errors: {
|
|
115
|
+
type: 'array',
|
|
116
|
+
items: {
|
|
117
|
+
type: 'object',
|
|
118
|
+
properties: {
|
|
119
|
+
message: {
|
|
120
|
+
type: 'string'
|
|
121
|
+
},
|
|
122
|
+
name: {
|
|
123
|
+
type: 'string'
|
|
124
|
+
},
|
|
125
|
+
field: {
|
|
126
|
+
type: 'string'
|
|
127
|
+
},
|
|
128
|
+
data: {
|
|
129
|
+
type: 'object',
|
|
130
|
+
additionalProperties: true
|
|
131
|
+
}
|
|
132
|
+
},
|
|
133
|
+
required: [
|
|
134
|
+
'message'
|
|
135
|
+
]
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
},
|
|
139
|
+
required: [
|
|
140
|
+
'errors'
|
|
141
|
+
]
|
|
142
|
+
});
|
|
143
|
+
const errorRef = {
|
|
144
|
+
$ref: refTo(ERROR_SCHEMA_NAME)
|
|
145
|
+
};
|
|
146
|
+
const errorContent = (description)=>({
|
|
147
|
+
description,
|
|
148
|
+
content: {
|
|
149
|
+
'application/json': {
|
|
150
|
+
schema: errorRef
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
const errorResponses = (codes, t)=>{
|
|
155
|
+
const responses = {};
|
|
156
|
+
for (const code of codes)responses[code] = errorContent(t(ERROR_KEYS[code]));
|
|
157
|
+
return responses;
|
|
158
|
+
};
|
|
159
|
+
const buildListEnvelopeSchema = (base)=>({
|
|
160
|
+
type: 'object',
|
|
161
|
+
properties: {
|
|
162
|
+
docs: {
|
|
163
|
+
type: 'array',
|
|
164
|
+
items: {
|
|
165
|
+
$ref: refTo(base)
|
|
166
|
+
}
|
|
167
|
+
},
|
|
168
|
+
totalDocs: {
|
|
169
|
+
type: 'integer'
|
|
170
|
+
},
|
|
171
|
+
limit: {
|
|
172
|
+
type: 'integer'
|
|
173
|
+
},
|
|
174
|
+
totalPages: {
|
|
175
|
+
type: 'integer'
|
|
176
|
+
},
|
|
177
|
+
page: {
|
|
178
|
+
type: 'integer'
|
|
179
|
+
},
|
|
180
|
+
pagingCounter: {
|
|
181
|
+
type: 'integer'
|
|
182
|
+
},
|
|
183
|
+
hasPrevPage: {
|
|
184
|
+
type: 'boolean'
|
|
185
|
+
},
|
|
186
|
+
hasNextPage: {
|
|
187
|
+
type: 'boolean'
|
|
188
|
+
},
|
|
189
|
+
prevPage: nullableType('integer'),
|
|
190
|
+
nextPage: nullableType('integer')
|
|
191
|
+
},
|
|
192
|
+
required: [
|
|
193
|
+
'docs',
|
|
194
|
+
'totalDocs',
|
|
195
|
+
'limit',
|
|
196
|
+
'totalPages',
|
|
197
|
+
'page'
|
|
198
|
+
]
|
|
199
|
+
});
|
|
200
|
+
const uploadRequestBody = ({ bodyRef, fileRequired, t })=>{
|
|
201
|
+
const fieldsSchemaName = bodyRef.$ref.split('/').pop() ?? 'the document';
|
|
202
|
+
return {
|
|
203
|
+
required: fileRequired,
|
|
204
|
+
description: t('uploadBody'),
|
|
205
|
+
content: {
|
|
206
|
+
'multipart/form-data': {
|
|
207
|
+
schema: {
|
|
208
|
+
type: 'object',
|
|
209
|
+
properties: {
|
|
210
|
+
file: {
|
|
211
|
+
type: 'string',
|
|
212
|
+
format: 'binary',
|
|
213
|
+
description: t('uploadFile')
|
|
214
|
+
},
|
|
215
|
+
_payload: {
|
|
216
|
+
allOf: [
|
|
217
|
+
bodyRef
|
|
218
|
+
],
|
|
219
|
+
description: t('uploadPayloadField', {
|
|
220
|
+
schema: fieldsSchemaName
|
|
221
|
+
})
|
|
222
|
+
}
|
|
223
|
+
},
|
|
224
|
+
...fileRequired ? {
|
|
225
|
+
required: [
|
|
226
|
+
'file'
|
|
227
|
+
]
|
|
228
|
+
} : {}
|
|
229
|
+
},
|
|
230
|
+
encoding: {
|
|
231
|
+
file: {
|
|
232
|
+
contentType: 'application/octet-stream'
|
|
233
|
+
},
|
|
234
|
+
_payload: {
|
|
235
|
+
contentType: 'application/json'
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
},
|
|
239
|
+
'application/json': {
|
|
240
|
+
schema: bodyRef
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
};
|
|
244
|
+
};
|
|
245
|
+
const SECURITY_SCHEME_NAME = 'PayloadToken';
|
|
246
|
+
const INTERACTIVE_SCHEME_NAME = 'PayloadLogin';
|
|
247
|
+
const interactiveSecurityScheme = ({ tokenUrl, t })=>({
|
|
248
|
+
type: 'oauth2',
|
|
249
|
+
description: t('securityInteractive'),
|
|
250
|
+
flows: {
|
|
251
|
+
password: {
|
|
252
|
+
tokenUrl,
|
|
253
|
+
scopes: {}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
});
|
|
257
|
+
const securityScheme = ({ cookiePrefix, t })=>({
|
|
258
|
+
type: 'http',
|
|
259
|
+
scheme: 'bearer',
|
|
260
|
+
bearerFormat: 'JWT',
|
|
261
|
+
description: t('securityBearer', {
|
|
262
|
+
cookiePrefix
|
|
263
|
+
})
|
|
264
|
+
});
|
|
265
|
+
const isOpenToPublic = (access)=>{
|
|
266
|
+
if (!access) return true;
|
|
267
|
+
const trap = new Proxy({}, {
|
|
268
|
+
get () {
|
|
269
|
+
throw new Error('req accessed');
|
|
270
|
+
}
|
|
271
|
+
});
|
|
272
|
+
try {
|
|
273
|
+
return true === access({
|
|
274
|
+
req: trap
|
|
275
|
+
});
|
|
276
|
+
} catch {
|
|
277
|
+
return false;
|
|
278
|
+
}
|
|
279
|
+
};
|
|
280
|
+
export { ERRORS, ERROR_SCHEMA_NAME, INTERACTIVE_SCHEME_NAME, SECURITY_SCHEME_NAME, buildErrorResponseSchema, buildListEnvelopeSchema, errorResponses, interactiveSecurityScheme, isOpenToPublic, jsonBody, jsonOk, jsonResponse, messageResponse, nullableType, securityScheme, uploadRequestBody };
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
const NULL = 'null';
|
|
2
|
+
const UNSUPPORTED_KEYWORDS = [
|
|
3
|
+
'$schema',
|
|
4
|
+
'$id',
|
|
5
|
+
'$comment',
|
|
6
|
+
'unevaluatedProperties',
|
|
7
|
+
'patternProperties',
|
|
8
|
+
'propertyNames'
|
|
9
|
+
];
|
|
10
|
+
const isStringType = (type)=>'string' === type || Array.isArray(type) && type.includes('string');
|
|
11
|
+
const downconvertTo30 = (node)=>{
|
|
12
|
+
if (Array.isArray(node)) return node.map(downconvertTo30);
|
|
13
|
+
if (!node || 'object' != typeof node) return node;
|
|
14
|
+
const out = {};
|
|
15
|
+
for (const [key, value] of Object.entries(node))out[key] = downconvertTo30(value);
|
|
16
|
+
if (isStringType(out.type) && 'application/octet-stream' === out.contentMediaType) {
|
|
17
|
+
if (void 0 === out.format) out.format = 'binary';
|
|
18
|
+
delete out.contentMediaType;
|
|
19
|
+
}
|
|
20
|
+
if (isStringType(out.type) && 'base64' === out.contentEncoding) {
|
|
21
|
+
if (void 0 === out.format) out.format = 'byte';
|
|
22
|
+
delete out.contentEncoding;
|
|
23
|
+
}
|
|
24
|
+
if (Array.isArray(out.type)) {
|
|
25
|
+
const types = out.type;
|
|
26
|
+
if (types.includes(NULL)) out.nullable = true;
|
|
27
|
+
const rest = types.filter((t)=>t !== NULL);
|
|
28
|
+
out.type = 1 === rest.length ? rest[0] : rest;
|
|
29
|
+
}
|
|
30
|
+
if ('const' in out) {
|
|
31
|
+
out.enum = [
|
|
32
|
+
out.const
|
|
33
|
+
];
|
|
34
|
+
delete out.const;
|
|
35
|
+
}
|
|
36
|
+
if (Array.isArray(out.examples)) {
|
|
37
|
+
const examples = out.examples;
|
|
38
|
+
if (examples.length > 0) out.example = examples[0];
|
|
39
|
+
if (examples.length > 1) out['x-examples'] = examples;
|
|
40
|
+
delete out.examples;
|
|
41
|
+
}
|
|
42
|
+
for (const keyword of UNSUPPORTED_KEYWORDS)delete out[keyword];
|
|
43
|
+
return out;
|
|
44
|
+
};
|
|
45
|
+
const toOpenApi30 = (doc)=>{
|
|
46
|
+
const converted = downconvertTo30(doc);
|
|
47
|
+
converted.openapi = '3.0.4';
|
|
48
|
+
return converted;
|
|
49
|
+
};
|
|
50
|
+
const toOpenApi31 = (doc)=>({
|
|
51
|
+
...doc,
|
|
52
|
+
openapi: '3.1.2',
|
|
53
|
+
tags: doc.tags?.map(({ kind: _kind, parent: _parent, ...rest })=>rest)
|
|
54
|
+
});
|
|
55
|
+
export { toOpenApi30, toOpenApi31 };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Block, SanitizedConfig } from 'payload';
|
|
2
|
+
import type { SchemaObject } from '@scalar/openapi-types/3.2';
|
|
3
|
+
import type { BuildContext, Entity } from '../types.js';
|
|
4
|
+
export declare const resolveLocalizedStrings: (value: unknown, ctx: BuildContext) => unknown;
|
|
5
|
+
export interface EntitySchemas {
|
|
6
|
+
read: SchemaObject;
|
|
7
|
+
create: SchemaObject;
|
|
8
|
+
update: SchemaObject;
|
|
9
|
+
}
|
|
10
|
+
export declare const buildEntitySchemas: ({ entity, config, ctx, }: {
|
|
11
|
+
entity: Entity;
|
|
12
|
+
config: SanitizedConfig;
|
|
13
|
+
ctx: BuildContext;
|
|
14
|
+
}) => Promise<EntitySchemas>;
|
|
15
|
+
export declare const buildBlockSchema: (block: Block, config: SanitizedConfig) => SchemaObject;
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
import { entityToJSONSchema } from "payload";
|
|
2
|
+
import { getTranslation } from "@payloadcms/translations";
|
|
3
|
+
import { deepMerge, isPlainObject } from "../utils.js";
|
|
4
|
+
import { makeT } from "../translations/index.js";
|
|
5
|
+
import { AUTH_FIELDS, flattenFields } from "./fields.js";
|
|
6
|
+
import { rewriteRefs } from "./names.js";
|
|
7
|
+
const READ_HIDDEN_FIELDS = new Set([
|
|
8
|
+
...AUTH_FIELDS,
|
|
9
|
+
'collection'
|
|
10
|
+
]);
|
|
11
|
+
const SERVER_FIELDS = [
|
|
12
|
+
'id',
|
|
13
|
+
'createdAt',
|
|
14
|
+
'updatedAt'
|
|
15
|
+
];
|
|
16
|
+
const stripInterfaceName = (entity)=>{
|
|
17
|
+
const { interfaceName: _omit, ...rest } = entity;
|
|
18
|
+
return rest;
|
|
19
|
+
};
|
|
20
|
+
const toComponentSchema = ({ config, source, idType })=>{
|
|
21
|
+
const jsonSchema = entityToJSONSchema(config, source, new Map(), idType);
|
|
22
|
+
const blockSlugs = new Set((config.blocks ?? []).map((b)=>b.slug));
|
|
23
|
+
const schema = rewriteRefs(jsonSchema, blockSlugs);
|
|
24
|
+
delete schema.title;
|
|
25
|
+
return schema;
|
|
26
|
+
};
|
|
27
|
+
const fieldOpenapi = (field)=>{
|
|
28
|
+
const meta = field.custom?.openapi;
|
|
29
|
+
return isPlainObject(meta) ? meta : void 0;
|
|
30
|
+
};
|
|
31
|
+
const isLocaleKeyed = (value, ctx)=>{
|
|
32
|
+
if (!isPlainObject(value)) return false;
|
|
33
|
+
const keys = Object.keys(value);
|
|
34
|
+
return keys.length > 0 && keys.every((k)=>ctx.locales.includes(k)) && Object.values(value).every((v)=>'string' == typeof v);
|
|
35
|
+
};
|
|
36
|
+
const LOCALIZABLE_KEYS = new Set([
|
|
37
|
+
"description",
|
|
38
|
+
'title',
|
|
39
|
+
'summary'
|
|
40
|
+
]);
|
|
41
|
+
const resolveTextValue = (value, ctx)=>{
|
|
42
|
+
if ('function' == typeof value) try {
|
|
43
|
+
const result = value({
|
|
44
|
+
t: ctx.i18n.t,
|
|
45
|
+
i18n: ctx.i18n
|
|
46
|
+
});
|
|
47
|
+
return 'string' == typeof result && result.length > 0 ? result : void 0;
|
|
48
|
+
} catch {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
if (isLocaleKeyed(value, ctx)) return getTranslation(value, ctx.i18n);
|
|
52
|
+
};
|
|
53
|
+
const resolveLocalizedStrings = (value, ctx)=>{
|
|
54
|
+
if (!isPlainObject(value)) return value;
|
|
55
|
+
const out = {};
|
|
56
|
+
for (const [k, v] of Object.entries(value)){
|
|
57
|
+
if (LOCALIZABLE_KEYS.has(k)) {
|
|
58
|
+
const resolved = resolveTextValue(v, ctx);
|
|
59
|
+
if (void 0 !== resolved) out[k] = resolved;
|
|
60
|
+
else if ('function' != typeof v) out[k] = resolveLocalizedStrings(v, ctx);
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
out[k] = resolveLocalizedStrings(v, ctx);
|
|
64
|
+
}
|
|
65
|
+
return out;
|
|
66
|
+
};
|
|
67
|
+
const applyFieldOverrides = ({ schema, fields, ctx })=>{
|
|
68
|
+
if (!schema.properties) return;
|
|
69
|
+
for (const field of flattenFields(fields)){
|
|
70
|
+
const name = 'name' in field ? field.name : void 0;
|
|
71
|
+
const override = fieldOpenapi(field);
|
|
72
|
+
if (!name || !override) continue;
|
|
73
|
+
const current = schema.properties[name];
|
|
74
|
+
if (isPlainObject(current)) {
|
|
75
|
+
const resolved = resolveLocalizedStrings(override, ctx);
|
|
76
|
+
schema.properties[name] = deepMerge(current, resolved);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
const isRef = (value)=>isPlainObject(value) && 'string' == typeof value.$ref;
|
|
81
|
+
const isBlockBranch = (branch)=>{
|
|
82
|
+
if (isRef(branch)) return branch.$ref.startsWith('#/components/schemas/Block');
|
|
83
|
+
return isPlainObject(branch) && isPlainObject(branch.properties) && 'blockType' in branch.properties;
|
|
84
|
+
};
|
|
85
|
+
const addBlockDiscriminators = (node)=>{
|
|
86
|
+
if (Array.isArray(node)) {
|
|
87
|
+
for (const item of node)addBlockDiscriminators(item);
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
if (!isPlainObject(node)) return;
|
|
91
|
+
const oneOf = node.oneOf;
|
|
92
|
+
if (Array.isArray(oneOf) && oneOf.length > 0 && oneOf.every(isBlockBranch)) node.discriminator = {
|
|
93
|
+
propertyName: 'blockType'
|
|
94
|
+
};
|
|
95
|
+
for (const value of Object.values(node))addBlockDiscriminators(value);
|
|
96
|
+
};
|
|
97
|
+
const markLocalizedFields = ({ schema, fields, ctx })=>{
|
|
98
|
+
if (!schema.properties || 0 === ctx.locales.length) return;
|
|
99
|
+
for (const field of flattenFields(fields)){
|
|
100
|
+
if (!('localized' in field) || !field.localized) continue;
|
|
101
|
+
const name = 'name' in field ? field.name : void 0;
|
|
102
|
+
if (!name || !(name in schema.properties)) continue;
|
|
103
|
+
const single = schema.properties[name];
|
|
104
|
+
schema.properties[name] = {
|
|
105
|
+
oneOf: [
|
|
106
|
+
single,
|
|
107
|
+
{
|
|
108
|
+
type: 'object',
|
|
109
|
+
description: makeT(ctx.i18n)('schemaPerLocale'),
|
|
110
|
+
properties: Object.fromEntries(ctx.locales.map((l)=>[
|
|
111
|
+
l,
|
|
112
|
+
single
|
|
113
|
+
]))
|
|
114
|
+
}
|
|
115
|
+
]
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
const removeProps = (schema, names)=>{
|
|
120
|
+
if (!schema.properties) return;
|
|
121
|
+
for (const name of names)delete schema.properties[name];
|
|
122
|
+
if (Array.isArray(schema.required)) {
|
|
123
|
+
const drop = new Set(names);
|
|
124
|
+
schema.required = schema.required.filter((r)=>!drop.has(r));
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
const buildBaseSchema = ({ entity, config, ctx })=>{
|
|
128
|
+
const schema = toComponentSchema({
|
|
129
|
+
config,
|
|
130
|
+
source: stripInterfaceName(entity),
|
|
131
|
+
idType: ctx.defaultIDType
|
|
132
|
+
});
|
|
133
|
+
const isAuth = 'auth' in entity && Boolean(entity.auth);
|
|
134
|
+
if (isAuth) removeProps(schema, READ_HIDDEN_FIELDS);
|
|
135
|
+
applyFieldOverrides({
|
|
136
|
+
schema,
|
|
137
|
+
fields: entity.fields,
|
|
138
|
+
ctx
|
|
139
|
+
});
|
|
140
|
+
addBlockDiscriminators(schema);
|
|
141
|
+
return schema;
|
|
142
|
+
};
|
|
143
|
+
const cloneSchema = (schema)=>({
|
|
144
|
+
...schema,
|
|
145
|
+
properties: {
|
|
146
|
+
...schema.properties
|
|
147
|
+
},
|
|
148
|
+
required: Array.isArray(schema.required) ? [
|
|
149
|
+
...schema.required
|
|
150
|
+
] : schema.required
|
|
151
|
+
});
|
|
152
|
+
const flattenRelationships = ({ schema, fields, idType })=>{
|
|
153
|
+
if (!schema.properties) return;
|
|
154
|
+
const idSchema = {
|
|
155
|
+
type: 'number' === idType ? 'integer' : 'string'
|
|
156
|
+
};
|
|
157
|
+
for (const field of flattenFields(fields)){
|
|
158
|
+
if ('relationship' !== field.type && 'upload' !== field.type) continue;
|
|
159
|
+
const name = 'name' in field ? field.name : void 0;
|
|
160
|
+
if (name && name in schema.properties) schema.properties[name] = 'hasMany' in field && field.hasMany ? {
|
|
161
|
+
type: 'array',
|
|
162
|
+
items: idSchema
|
|
163
|
+
} : idSchema;
|
|
164
|
+
}
|
|
165
|
+
};
|
|
166
|
+
const buildEntitySchemas = async ({ entity, config, ctx })=>{
|
|
167
|
+
const read = buildBaseSchema({
|
|
168
|
+
entity,
|
|
169
|
+
config,
|
|
170
|
+
ctx
|
|
171
|
+
});
|
|
172
|
+
const isAuth = 'auth' in entity && Boolean(entity.auth);
|
|
173
|
+
const idType = ctx.defaultIDType;
|
|
174
|
+
const create = cloneSchema(read);
|
|
175
|
+
if (Array.isArray(create.required)) {
|
|
176
|
+
const optional = new Set(SERVER_FIELDS);
|
|
177
|
+
create.required = create.required.filter((r)=>!optional.has(r));
|
|
178
|
+
}
|
|
179
|
+
flattenRelationships({
|
|
180
|
+
schema: create,
|
|
181
|
+
fields: entity.fields,
|
|
182
|
+
idType
|
|
183
|
+
});
|
|
184
|
+
if (isAuth && create.properties) {
|
|
185
|
+
create.properties.password = {
|
|
186
|
+
type: 'string'
|
|
187
|
+
};
|
|
188
|
+
create.required = [
|
|
189
|
+
...new Set([
|
|
190
|
+
...create.required ?? [],
|
|
191
|
+
'password'
|
|
192
|
+
])
|
|
193
|
+
];
|
|
194
|
+
}
|
|
195
|
+
const update = cloneSchema(read);
|
|
196
|
+
delete update.required;
|
|
197
|
+
if (update.properties) delete update.properties.id;
|
|
198
|
+
flattenRelationships({
|
|
199
|
+
schema: update,
|
|
200
|
+
fields: entity.fields,
|
|
201
|
+
idType
|
|
202
|
+
});
|
|
203
|
+
if (isAuth && update.properties) update.properties.password = {
|
|
204
|
+
type: 'string'
|
|
205
|
+
};
|
|
206
|
+
markLocalizedFields({
|
|
207
|
+
schema: read,
|
|
208
|
+
fields: entity.fields,
|
|
209
|
+
ctx
|
|
210
|
+
});
|
|
211
|
+
return {
|
|
212
|
+
read,
|
|
213
|
+
create,
|
|
214
|
+
update
|
|
215
|
+
};
|
|
216
|
+
};
|
|
217
|
+
const buildBlockSchema = (block, config)=>{
|
|
218
|
+
const schema = toComponentSchema({
|
|
219
|
+
config,
|
|
220
|
+
source: stripInterfaceName(block),
|
|
221
|
+
idType: 'text'
|
|
222
|
+
});
|
|
223
|
+
const blockType = {
|
|
224
|
+
type: 'string',
|
|
225
|
+
enum: [
|
|
226
|
+
block.slug
|
|
227
|
+
]
|
|
228
|
+
};
|
|
229
|
+
const properties = {
|
|
230
|
+
...schema.properties,
|
|
231
|
+
blockType
|
|
232
|
+
};
|
|
233
|
+
const required = [
|
|
234
|
+
...new Set([
|
|
235
|
+
'blockType',
|
|
236
|
+
...schema.required ?? []
|
|
237
|
+
])
|
|
238
|
+
];
|
|
239
|
+
return {
|
|
240
|
+
...schema,
|
|
241
|
+
type: 'object',
|
|
242
|
+
properties,
|
|
243
|
+
required
|
|
244
|
+
};
|
|
245
|
+
};
|
|
246
|
+
export { buildBlockSchema, buildEntitySchemas, resolveLocalizedStrings };
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { Field } from 'payload';
|
|
2
|
+
export declare const isHiddenField: (field: Field) => boolean;
|
|
3
|
+
export declare const AUTH_FIELDS: Set<string>;
|
|
4
|
+
export declare const flattenFields: (fields: Field[]) => Field[];
|
|
5
|
+
export declare const exposableFields: (fields: Field[]) => Field[];
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
const isHiddenField = (field)=>'hidden' in field && true === field.hidden;
|
|
2
|
+
const AUTH_FIELDS = new Set([
|
|
3
|
+
'salt',
|
|
4
|
+
'hash',
|
|
5
|
+
'password',
|
|
6
|
+
'resetPasswordToken',
|
|
7
|
+
'resetPasswordExpiration',
|
|
8
|
+
'loginAttempts',
|
|
9
|
+
'lockUntil',
|
|
10
|
+
'sessions'
|
|
11
|
+
]);
|
|
12
|
+
const hasName = (field)=>'name' in field && 'string' == typeof field.name;
|
|
13
|
+
const flattenFields = (fields)=>{
|
|
14
|
+
const out = [];
|
|
15
|
+
for (const field of fields)if ('ui' !== field.type) {
|
|
16
|
+
if ('row' === field.type || 'collapsible' === field.type) {
|
|
17
|
+
out.push(...flattenFields(field.fields));
|
|
18
|
+
continue;
|
|
19
|
+
}
|
|
20
|
+
if ('tabs' === field.type) {
|
|
21
|
+
for (const tab of field.tabs){
|
|
22
|
+
const tabName = hasName(tab) ? tab.name : void 0;
|
|
23
|
+
if (tabName) out.push({
|
|
24
|
+
type: 'group',
|
|
25
|
+
name: tabName,
|
|
26
|
+
fields: tab.fields
|
|
27
|
+
});
|
|
28
|
+
else out.push(...flattenFields(tab.fields));
|
|
29
|
+
}
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
if ('group' === field.type && !hasName(field)) {
|
|
33
|
+
out.push(...flattenFields(field.fields));
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
out.push(field);
|
|
37
|
+
}
|
|
38
|
+
return out;
|
|
39
|
+
};
|
|
40
|
+
const exposableFields = (fields)=>flattenFields(fields).filter((field)=>{
|
|
41
|
+
const name = 'name' in field ? field.name : void 0;
|
|
42
|
+
return Boolean(name) && !isHiddenField(field) && !AUTH_FIELDS.has(name);
|
|
43
|
+
});
|
|
44
|
+
export { AUTH_FIELDS, exposableFields, flattenFields, isHiddenField };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { SanitizedCollectionConfig, SanitizedGlobalConfig } from 'payload';
|
|
2
|
+
import type { PathsObject } from '@scalar/openapi-types/3.2';
|
|
3
|
+
import type { EntityKind, ResolvedFilters } from '../types.js';
|
|
4
|
+
export declare const shouldIncludeCollection: (collection: SanitizedCollectionConfig, filters: ResolvedFilters) => boolean;
|
|
5
|
+
export declare const shouldIncludeGlobal: (global: SanitizedGlobalConfig, filters: ResolvedFilters) => boolean;
|
|
6
|
+
export declare const filterOperations: ({ paths, slug, kind, filters, }: {
|
|
7
|
+
paths: PathsObject;
|
|
8
|
+
slug: string;
|
|
9
|
+
kind: EntityKind;
|
|
10
|
+
filters: ResolvedFilters;
|
|
11
|
+
}) => PathsObject;
|