@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.
Files changed (151) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +595 -0
  3. package/dist/bin/generateSpec.d.ts +25 -0
  4. package/dist/bin/generateSpec.js +61 -0
  5. package/dist/constants.d.ts +1 -0
  6. package/dist/constants.js +2 -0
  7. package/dist/endpoints/interactiveAuth.d.ts +4 -0
  8. package/dist/endpoints/interactiveAuth.js +27 -0
  9. package/dist/endpoints/spec.d.ts +5 -0
  10. package/dist/endpoints/spec.js +39 -0
  11. package/dist/index.d.ts +9 -0
  12. package/dist/index.js +67 -0
  13. package/dist/options.d.ts +2 -0
  14. package/dist/options.js +44 -0
  15. package/dist/spec/build.d.ts +14 -0
  16. package/dist/spec/build.js +36 -0
  17. package/dist/spec/buildDocument.d.ts +15 -0
  18. package/dist/spec/buildDocument.js +234 -0
  19. package/dist/spec/components.d.ts +46 -0
  20. package/dist/spec/components.js +280 -0
  21. package/dist/spec/downconvert.d.ts +3 -0
  22. package/dist/spec/downconvert.js +55 -0
  23. package/dist/spec/entitySchemas.d.ts +15 -0
  24. package/dist/spec/entitySchemas.js +246 -0
  25. package/dist/spec/fields.d.ts +5 -0
  26. package/dist/spec/fields.js +44 -0
  27. package/dist/spec/filters.d.ts +11 -0
  28. package/dist/spec/filters.js +74 -0
  29. package/dist/spec/names.d.ts +14 -0
  30. package/dist/spec/names.js +37 -0
  31. package/dist/spec/params.d.ts +47 -0
  32. package/dist/spec/params.js +344 -0
  33. package/dist/spec/paths/auth.d.ts +9 -0
  34. package/dist/spec/paths/auth.js +285 -0
  35. package/dist/spec/paths/collections.d.ts +7 -0
  36. package/dist/spec/paths/collections.js +261 -0
  37. package/dist/spec/paths/custom.d.ts +9 -0
  38. package/dist/spec/paths/custom.js +41 -0
  39. package/dist/spec/paths/globals.d.ts +7 -0
  40. package/dist/spec/paths/globals.js +68 -0
  41. package/dist/spec/paths/jobs.d.ts +7 -0
  42. package/dist/spec/paths/jobs.js +130 -0
  43. package/dist/spec/paths/versions.d.ts +18 -0
  44. package/dist/spec/paths/versions.js +243 -0
  45. package/dist/spec/tags.d.ts +27 -0
  46. package/dist/spec/tags.js +79 -0
  47. package/dist/translations/index.d.ts +6 -0
  48. package/dist/translations/index.js +96 -0
  49. package/dist/translations/locales/ar.d.ts +2 -0
  50. package/dist/translations/locales/ar.js +76 -0
  51. package/dist/translations/locales/az.d.ts +2 -0
  52. package/dist/translations/locales/az.js +76 -0
  53. package/dist/translations/locales/bg.d.ts +2 -0
  54. package/dist/translations/locales/bg.js +76 -0
  55. package/dist/translations/locales/bnBd.d.ts +2 -0
  56. package/dist/translations/locales/bnBd.js +76 -0
  57. package/dist/translations/locales/bnIn.d.ts +2 -0
  58. package/dist/translations/locales/bnIn.js +76 -0
  59. package/dist/translations/locales/ca.d.ts +2 -0
  60. package/dist/translations/locales/ca.js +76 -0
  61. package/dist/translations/locales/cs.d.ts +2 -0
  62. package/dist/translations/locales/cs.js +76 -0
  63. package/dist/translations/locales/da.d.ts +2 -0
  64. package/dist/translations/locales/da.js +76 -0
  65. package/dist/translations/locales/de.d.ts +2 -0
  66. package/dist/translations/locales/de.js +76 -0
  67. package/dist/translations/locales/en.d.ts +2 -0
  68. package/dist/translations/locales/en.js +76 -0
  69. package/dist/translations/locales/es.d.ts +2 -0
  70. package/dist/translations/locales/es.js +76 -0
  71. package/dist/translations/locales/et.d.ts +2 -0
  72. package/dist/translations/locales/et.js +76 -0
  73. package/dist/translations/locales/fa.d.ts +2 -0
  74. package/dist/translations/locales/fa.js +76 -0
  75. package/dist/translations/locales/fr.d.ts +2 -0
  76. package/dist/translations/locales/fr.js +76 -0
  77. package/dist/translations/locales/he.d.ts +2 -0
  78. package/dist/translations/locales/he.js +76 -0
  79. package/dist/translations/locales/hr.d.ts +2 -0
  80. package/dist/translations/locales/hr.js +76 -0
  81. package/dist/translations/locales/hu.d.ts +2 -0
  82. package/dist/translations/locales/hu.js +76 -0
  83. package/dist/translations/locales/hy.d.ts +2 -0
  84. package/dist/translations/locales/hy.js +76 -0
  85. package/dist/translations/locales/id.d.ts +2 -0
  86. package/dist/translations/locales/id.js +76 -0
  87. package/dist/translations/locales/is.d.ts +2 -0
  88. package/dist/translations/locales/is.js +76 -0
  89. package/dist/translations/locales/it.d.ts +2 -0
  90. package/dist/translations/locales/it.js +76 -0
  91. package/dist/translations/locales/ja.d.ts +2 -0
  92. package/dist/translations/locales/ja.js +76 -0
  93. package/dist/translations/locales/ko.d.ts +2 -0
  94. package/dist/translations/locales/ko.js +76 -0
  95. package/dist/translations/locales/lt.d.ts +2 -0
  96. package/dist/translations/locales/lt.js +76 -0
  97. package/dist/translations/locales/lv.d.ts +2 -0
  98. package/dist/translations/locales/lv.js +76 -0
  99. package/dist/translations/locales/my.d.ts +2 -0
  100. package/dist/translations/locales/my.js +76 -0
  101. package/dist/translations/locales/nb.d.ts +2 -0
  102. package/dist/translations/locales/nb.js +76 -0
  103. package/dist/translations/locales/nl.d.ts +2 -0
  104. package/dist/translations/locales/nl.js +76 -0
  105. package/dist/translations/locales/pl.d.ts +2 -0
  106. package/dist/translations/locales/pl.js +76 -0
  107. package/dist/translations/locales/pt.d.ts +2 -0
  108. package/dist/translations/locales/pt.js +76 -0
  109. package/dist/translations/locales/ro.d.ts +2 -0
  110. package/dist/translations/locales/ro.js +76 -0
  111. package/dist/translations/locales/rs.d.ts +2 -0
  112. package/dist/translations/locales/rs.js +76 -0
  113. package/dist/translations/locales/rsLatin.d.ts +2 -0
  114. package/dist/translations/locales/rsLatin.js +76 -0
  115. package/dist/translations/locales/ru.d.ts +2 -0
  116. package/dist/translations/locales/ru.js +76 -0
  117. package/dist/translations/locales/sk.d.ts +2 -0
  118. package/dist/translations/locales/sk.js +76 -0
  119. package/dist/translations/locales/sl.d.ts +2 -0
  120. package/dist/translations/locales/sl.js +76 -0
  121. package/dist/translations/locales/sv.d.ts +2 -0
  122. package/dist/translations/locales/sv.js +76 -0
  123. package/dist/translations/locales/ta.d.ts +2 -0
  124. package/dist/translations/locales/ta.js +76 -0
  125. package/dist/translations/locales/th.d.ts +2 -0
  126. package/dist/translations/locales/th.js +76 -0
  127. package/dist/translations/locales/tr.d.ts +2 -0
  128. package/dist/translations/locales/tr.js +76 -0
  129. package/dist/translations/locales/uk.d.ts +2 -0
  130. package/dist/translations/locales/uk.js +76 -0
  131. package/dist/translations/locales/vi.d.ts +2 -0
  132. package/dist/translations/locales/vi.js +76 -0
  133. package/dist/translations/locales/zh.d.ts +2 -0
  134. package/dist/translations/locales/zh.js +76 -0
  135. package/dist/translations/locales/zhTw.d.ts +2 -0
  136. package/dist/translations/locales/zhTw.js +76 -0
  137. package/dist/translations/types.d.ts +82 -0
  138. package/dist/translations/types.js +1 -0
  139. package/dist/types.d.ts +183 -0
  140. package/dist/types.js +1 -0
  141. package/dist/ui/createUiPlugin.d.ts +14 -0
  142. package/dist/ui/createUiPlugin.js +28 -0
  143. package/dist/ui/html.d.ts +8 -0
  144. package/dist/ui/html.js +8 -0
  145. package/dist/ui/scalar.d.ts +1 -0
  146. package/dist/ui/scalar.js +12 -0
  147. package/dist/ui/swagger.d.ts +1 -0
  148. package/dist/ui/swagger.js +12 -0
  149. package/dist/utils.d.ts +3 -0
  150. package/dist/utils.js +13 -0
  151. 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,3 @@
1
+ import type { Document } from '@scalar/openapi-types/3.2';
2
+ export declare const toOpenApi30: (doc: Document) => Document;
3
+ export declare const toOpenApi31: (doc: Document) => Document;
@@ -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;