@seshuk/payload-plugin-openapi 0.1.0 → 0.2.1

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 (68) hide show
  1. package/README.md +55 -539
  2. package/dist/index.d.ts +1 -1
  3. package/dist/options.js +1 -0
  4. package/dist/spec/buildDocument.js +39 -5
  5. package/dist/spec/components.d.ts +0 -2
  6. package/dist/spec/components.js +1 -16
  7. package/dist/spec/downconvert.d.ts +1 -1
  8. package/dist/spec/downconvert.js +27 -8
  9. package/dist/spec/entitySchemas.js +36 -32
  10. package/dist/spec/filters.js +12 -3
  11. package/dist/spec/params.d.ts +9 -1
  12. package/dist/spec/params.js +93 -1
  13. package/dist/spec/paths/auth.js +52 -63
  14. package/dist/spec/paths/collections.d.ts +3 -1
  15. package/dist/spec/paths/collections.js +42 -25
  16. package/dist/spec/paths/globals.d.ts +3 -1
  17. package/dist/spec/paths/globals.js +23 -13
  18. package/dist/spec/paths/versions.d.ts +2 -1
  19. package/dist/spec/paths/versions.js +14 -5
  20. package/dist/spec/security.d.ts +19 -0
  21. package/dist/spec/security.js +96 -0
  22. package/dist/translations/locales/ar.js +5 -0
  23. package/dist/translations/locales/az.js +5 -0
  24. package/dist/translations/locales/bg.js +5 -0
  25. package/dist/translations/locales/bnBd.js +5 -0
  26. package/dist/translations/locales/bnIn.js +5 -0
  27. package/dist/translations/locales/ca.js +5 -0
  28. package/dist/translations/locales/cs.js +5 -0
  29. package/dist/translations/locales/da.js +5 -0
  30. package/dist/translations/locales/de.js +5 -0
  31. package/dist/translations/locales/en.js +5 -0
  32. package/dist/translations/locales/es.js +5 -0
  33. package/dist/translations/locales/et.js +5 -0
  34. package/dist/translations/locales/fa.js +5 -0
  35. package/dist/translations/locales/fr.js +5 -0
  36. package/dist/translations/locales/he.js +5 -0
  37. package/dist/translations/locales/hr.js +5 -0
  38. package/dist/translations/locales/hu.js +5 -0
  39. package/dist/translations/locales/hy.js +5 -0
  40. package/dist/translations/locales/id.js +5 -0
  41. package/dist/translations/locales/is.js +5 -0
  42. package/dist/translations/locales/it.js +5 -0
  43. package/dist/translations/locales/ja.js +5 -0
  44. package/dist/translations/locales/ko.js +5 -0
  45. package/dist/translations/locales/lt.js +5 -0
  46. package/dist/translations/locales/lv.js +5 -0
  47. package/dist/translations/locales/my.js +5 -0
  48. package/dist/translations/locales/nb.js +5 -0
  49. package/dist/translations/locales/nl.js +5 -0
  50. package/dist/translations/locales/pl.js +5 -0
  51. package/dist/translations/locales/pt.js +5 -0
  52. package/dist/translations/locales/ro.js +5 -0
  53. package/dist/translations/locales/rs.js +5 -0
  54. package/dist/translations/locales/rsLatin.js +5 -0
  55. package/dist/translations/locales/ru.js +5 -0
  56. package/dist/translations/locales/sk.js +5 -0
  57. package/dist/translations/locales/sl.js +5 -0
  58. package/dist/translations/locales/sv.js +5 -0
  59. package/dist/translations/locales/ta.js +5 -0
  60. package/dist/translations/locales/th.js +5 -0
  61. package/dist/translations/locales/tr.js +5 -0
  62. package/dist/translations/locales/uk.js +5 -0
  63. package/dist/translations/locales/vi.js +5 -0
  64. package/dist/translations/locales/zh.js +5 -0
  65. package/dist/translations/locales/zhTw.js +5 -0
  66. package/dist/translations/types.d.ts +5 -0
  67. package/dist/types.d.ts +9 -0
  68. package/package.json +39 -24
package/dist/options.js CHANGED
@@ -37,6 +37,7 @@ const resolveOptions = (options)=>{
37
37
  filters: resolveFilters(options.filters),
38
38
  interactiveAuth: resolveInteractiveAuth(options.interactiveAuth),
39
39
  nestedTags: options.nestedTags ?? false,
40
+ securityWhen: options.securityWhen,
40
41
  cache: options.cache ?? true,
41
42
  extensions: options.extensions ?? []
42
43
  };
@@ -7,6 +7,7 @@ import { buildBlockSchema, buildEntitySchemas } from "./entitySchemas.js";
7
7
  import { filterOperations, shouldIncludeCollection, shouldIncludeGlobal } from "./filters.js";
8
8
  import { blockSchemaName, createSchemaName, globalSchemaName, joinsSchemaName, listSchemaName, populateSchemaName, querySchemaName, schemaName, selectSchemaName, updateSchemaName } from "./names.js";
9
9
  import { buildParamSchemas, buildQueryOperationsSchema, collectionHasFilters } from "./params.js";
10
+ import { applySecurityWhen, resolveEntitySecurity } from "./security.js";
10
11
  import { buildTagHierarchy } from "./tags.js";
11
12
  import { buildAuthPaths } from "./paths/auth.js";
12
13
  import { buildCollectionPaths } from "./paths/collections.js";
@@ -66,10 +67,21 @@ const buildDocument = async (input)=>{
66
67
  ctx
67
68
  });
68
69
  registerParamSchemas(schemas, base, collection.fields, ctx);
70
+ const security = await resolveEntitySecurity({
71
+ entity: collection,
72
+ operations: [
73
+ 'read',
74
+ 'create',
75
+ 'update',
76
+ 'delete'
77
+ ],
78
+ locale: ctx.locales[0]
79
+ });
69
80
  const collectionPaths = {
70
81
  ...buildCollectionPaths({
71
82
  collection,
72
- ctx
83
+ ctx,
84
+ security
73
85
  })
74
86
  };
75
87
  if (filters.includeAuth) Object.assign(collectionPaths, buildAuthPaths({
@@ -94,11 +106,17 @@ const buildDocument = async (input)=>{
94
106
  nestedTags: options.nestedTags
95
107
  }));
96
108
  }
97
- Object.assign(paths, filterOperations({
109
+ const filteredCollectionPaths = filterOperations({
98
110
  paths: collectionPaths,
99
111
  slug: collection.slug,
100
112
  kind: 'collection',
101
113
  filters
114
+ });
115
+ Object.assign(paths, applySecurityWhen({
116
+ paths: filteredCollectionPaths,
117
+ slug: collection.slug,
118
+ kind: 'collection',
119
+ securityWhen: options.securityWhen
102
120
  }));
103
121
  } catch (error) {
104
122
  logger.warn(`${PLUGIN_NAME}: skipped collection "${collection.slug}": ${error.message}`);
@@ -118,10 +136,19 @@ const buildDocument = async (input)=>{
118
136
  schemas[gbase] = read;
119
137
  schemas[updateSchemaName(gbase)] = update;
120
138
  registerParamSchemas(schemas, gbase, global.fields, ctx);
139
+ const security = await resolveEntitySecurity({
140
+ entity: global,
141
+ operations: [
142
+ 'read',
143
+ 'update'
144
+ ],
145
+ locale: ctx.locales[0]
146
+ });
121
147
  const globalPaths = {
122
148
  ...buildGlobalPaths({
123
149
  global,
124
- ctx
150
+ ctx,
151
+ security
125
152
  })
126
153
  };
127
154
  if (filters.includeVersions && global.versions) {
@@ -137,14 +164,21 @@ const buildDocument = async (input)=>{
137
164
  pathBase: `${ctx.apiRoute}/globals/${global.slug}`,
138
165
  ctx,
139
166
  schemaBase: gbase,
140
- nestedTags: options.nestedTags
167
+ nestedTags: options.nestedTags,
168
+ global: true
141
169
  }));
142
170
  }
143
- Object.assign(paths, filterOperations({
171
+ const filteredGlobalPaths = filterOperations({
144
172
  paths: globalPaths,
145
173
  slug: global.slug,
146
174
  kind: 'global',
147
175
  filters
176
+ });
177
+ Object.assign(paths, applySecurityWhen({
178
+ paths: filteredGlobalPaths,
179
+ slug: global.slug,
180
+ kind: 'global',
181
+ securityWhen: options.securityWhen
148
182
  }));
149
183
  } catch (error) {
150
184
  logger.warn(`${PLUGIN_NAME}: skipped global "${global.slug}": ${error.message}`);
@@ -1,4 +1,3 @@
1
- import type { Access } from 'payload';
2
1
  import type { ReferenceObject, RequestBodyObject, ResponseObject, ResponsesObject, SchemaObject, SecuritySchemeObject } from '@scalar/openapi-types/3.2';
3
2
  import type { Translate } from '../translations/types.js';
4
3
  type SchemaOrRef = SchemaObject | ReferenceObject;
@@ -42,5 +41,4 @@ export declare const securityScheme: ({ cookiePrefix, t }: {
42
41
  cookiePrefix: string;
43
42
  t: Translate;
44
43
  }) => SecuritySchemeObject;
45
- export declare const isOpenToPublic: (access: Access | undefined) => boolean;
46
44
  export {};
@@ -262,19 +262,4 @@ const securityScheme = ({ cookiePrefix, t })=>({
262
262
  cookiePrefix
263
263
  })
264
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 };
265
+ export { ERRORS, ERROR_SCHEMA_NAME, INTERACTIVE_SCHEME_NAME, SECURITY_SCHEME_NAME, buildErrorResponseSchema, buildListEnvelopeSchema, errorResponses, interactiveSecurityScheme, jsonBody, jsonOk, jsonResponse, messageResponse, nullableType, securityScheme, uploadRequestBody };
@@ -1,3 +1,3 @@
1
1
  import type { Document } from '@scalar/openapi-types/3.2';
2
- export declare const toOpenApi30: (doc: Document) => Document;
3
2
  export declare const toOpenApi31: (doc: Document) => Document;
3
+ export declare const toOpenApi30: (doc: Document) => Document;
@@ -23,9 +23,27 @@ const downconvertTo30 = (node)=>{
23
23
  }
24
24
  if (Array.isArray(out.type)) {
25
25
  const types = out.type;
26
- if (types.includes(NULL)) out.nullable = true;
26
+ const nullable = types.includes(NULL);
27
27
  const rest = types.filter((t)=>t !== NULL);
28
- out.type = 1 === rest.length ? rest[0] : rest;
28
+ if (1 === rest.length) {
29
+ out.type = rest[0];
30
+ if (nullable) out.nullable = true;
31
+ } else if (rest.length > 1) {
32
+ delete out.type;
33
+ const union = rest.map((type)=>nullable ? {
34
+ type,
35
+ nullable: true
36
+ } : {
37
+ type
38
+ });
39
+ if (out.anyOf) out.allOf = [
40
+ ...out.allOf ?? [],
41
+ {
42
+ anyOf: union
43
+ }
44
+ ];
45
+ else out.anyOf = union;
46
+ }
29
47
  }
30
48
  if ('const' in out) {
31
49
  out.enum = [
@@ -39,17 +57,18 @@ const downconvertTo30 = (node)=>{
39
57
  if (examples.length > 1) out['x-examples'] = examples;
40
58
  delete out.examples;
41
59
  }
60
+ if (Array.isArray(out.required) && 0 === out.required.length) delete out.required;
42
61
  for (const keyword of UNSUPPORTED_KEYWORDS)delete out[keyword];
43
62
  return out;
44
63
  };
45
- const toOpenApi30 = (doc)=>{
46
- const converted = downconvertTo30(doc);
47
- converted.openapi = '3.0.4';
48
- return converted;
49
- };
50
64
  const toOpenApi31 = (doc)=>({
51
65
  ...doc,
52
66
  openapi: '3.1.2',
53
- tags: doc.tags?.map(({ kind: _kind, parent: _parent, ...rest })=>rest)
67
+ tags: doc.tags?.map(({ kind: _kind, parent: _parent, summary: _summary, ...rest })=>rest)
54
68
  });
69
+ const toOpenApi30 = (doc)=>{
70
+ const converted = downconvertTo30(toOpenApi31(doc));
71
+ converted.openapi = '3.0.4';
72
+ return converted;
73
+ };
55
74
  export { toOpenApi30, toOpenApi31 };
@@ -3,7 +3,7 @@ import { getTranslation } from "@payloadcms/translations";
3
3
  import { deepMerge, isPlainObject } from "../utils.js";
4
4
  import { makeT } from "../translations/index.js";
5
5
  import { AUTH_FIELDS, flattenFields } from "./fields.js";
6
- import { rewriteRefs } from "./names.js";
6
+ import { refTo, rewriteRefs, schemaName } from "./names.js";
7
7
  const READ_HIDDEN_FIELDS = new Set([
8
8
  ...AUTH_FIELDS,
9
9
  'collection'
@@ -17,8 +17,10 @@ const stripInterfaceName = (entity)=>{
17
17
  const { interfaceName: _omit, ...rest } = entity;
18
18
  return rest;
19
19
  };
20
- const toComponentSchema = ({ config, source, idType })=>{
21
- const jsonSchema = entityToJSONSchema(config, source, new Map(), idType);
20
+ const toComponentSchema = ({ config, source, idType, write = false })=>{
21
+ const jsonSchema = entityToJSONSchema(config, source, new Map(), idType, void 0, void 0, {
22
+ forceInlineBlocks: write
23
+ });
22
24
  const blockSlugs = new Set((config.blocks ?? []).map((b)=>b.slug));
23
25
  const schema = rewriteRefs(jsonSchema, blockSlugs);
24
26
  delete schema.title;
@@ -124,12 +126,33 @@ const removeProps = (schema, names)=>{
124
126
  schema.required = schema.required.filter((r)=>!drop.has(r));
125
127
  }
126
128
  };
127
- const buildBaseSchema = ({ entity, config, ctx })=>{
129
+ const toIdType = (type)=>Array.isArray(type) ? type.map(toIdType) : 'number' === type ? 'integer' : type;
130
+ const writeRelationshipsAsIds = (node, refs)=>{
131
+ if (Array.isArray(node)) {
132
+ for (const item of node)writeRelationshipsAsIds(item, refs);
133
+ return;
134
+ }
135
+ if (!isPlainObject(node)) return;
136
+ const oneOf = node.oneOf;
137
+ if (Array.isArray(oneOf) && 2 === oneOf.length && isRef(oneOf[1]) && refs.has(oneOf[1].$ref)) {
138
+ delete node.oneOf;
139
+ node.type = toIdType(oneOf[0].type);
140
+ return;
141
+ }
142
+ for (const value of Object.values(node))writeRelationshipsAsIds(value, refs);
143
+ };
144
+ const buildBaseSchema = ({ entity, config, ctx, write = false })=>{
145
+ const idType = ctx.defaultIDType;
128
146
  const schema = toComponentSchema({
129
147
  config,
130
148
  source: stripInterfaceName(entity),
131
- idType: ctx.defaultIDType
149
+ idType,
150
+ write
132
151
  });
152
+ if (write) {
153
+ const refs = new Set((config.collections ?? []).map((c)=>refTo(schemaName(c.slug))));
154
+ writeRelationshipsAsIds(schema, refs);
155
+ }
133
156
  const isAuth = 'auth' in entity && Boolean(entity.auth);
134
157
  if (isAuth) removeProps(schema, READ_HIDDEN_FIELDS);
135
158
  applyFieldOverrides({
@@ -149,38 +172,24 @@ const cloneSchema = (schema)=>({
149
172
  ...schema.required
150
173
  ] : schema.required
151
174
  });
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
175
  const buildEntitySchemas = async ({ entity, config, ctx })=>{
167
176
  const read = buildBaseSchema({
168
177
  entity,
169
178
  config,
170
179
  ctx
171
180
  });
181
+ const write = buildBaseSchema({
182
+ entity,
183
+ config,
184
+ ctx,
185
+ write: true
186
+ });
172
187
  const isAuth = 'auth' in entity && Boolean(entity.auth);
173
- const idType = ctx.defaultIDType;
174
- const create = cloneSchema(read);
188
+ const create = cloneSchema(write);
175
189
  if (Array.isArray(create.required)) {
176
190
  const optional = new Set(SERVER_FIELDS);
177
191
  create.required = create.required.filter((r)=>!optional.has(r));
178
192
  }
179
- flattenRelationships({
180
- schema: create,
181
- fields: entity.fields,
182
- idType
183
- });
184
193
  if (isAuth && create.properties) {
185
194
  create.properties.password = {
186
195
  type: 'string'
@@ -192,14 +201,9 @@ const buildEntitySchemas = async ({ entity, config, ctx })=>{
192
201
  ])
193
202
  ];
194
203
  }
195
- const update = cloneSchema(read);
204
+ const update = cloneSchema(write);
196
205
  delete update.required;
197
206
  if (update.properties) delete update.properties.id;
198
- flattenRelationships({
199
- schema: update,
200
- fields: entity.fields,
201
- idType
202
- });
203
207
  if (isAuth && update.properties) update.properties.password = {
204
208
  type: 'string'
205
209
  };
@@ -3,7 +3,9 @@ const SYSTEM_COLLECTIONS = new Set([
3
3
  'payload-locked-documents',
4
4
  'payload-preferences',
5
5
  'payload-migrations',
6
- 'payload-folders'
6
+ 'payload-folders',
7
+ 'payload-kv',
8
+ 'payload-query-presets'
7
9
  ]);
8
10
  const HTTP_METHODS = [
9
11
  'get',
@@ -20,8 +22,15 @@ const matchesEntity = (matcher, { slug, kind })=>{
20
22
  const anyMatch = (matchers, entity)=>matchers.some((m)=>matchesEntity(m, entity));
21
23
  const isHiddenCollection = (collection)=>{
22
24
  if (true === collection.hidden) return true;
23
- const adminHidden = collection.admin?.hidden;
24
- return true === adminHidden || 'function' == typeof adminHidden;
25
+ const hidden = collection.admin?.hidden;
26
+ if ('function' == typeof hidden) try {
27
+ return true === hidden({
28
+ user: null
29
+ });
30
+ } catch {
31
+ return false;
32
+ }
33
+ return true === hidden;
25
34
  };
26
35
  const passesIncludeExclude = (entity, filters)=>{
27
36
  if (filters.include.length > 0 && !anyMatch(filters.include, entity)) return false;
@@ -1,6 +1,6 @@
1
1
  import type { Field } from 'payload';
2
2
  import type { ParameterObject, SchemaObject } from '@scalar/openapi-types/3.2';
3
- import type { BuildContext } from '../types.js';
3
+ import type { BuildContext, Entity } from '../types.js';
4
4
  export declare const buildSelectSchema: ({ fields, ctx }: {
5
5
  fields: Field[];
6
6
  ctx: BuildContext;
@@ -38,6 +38,14 @@ export declare const commonReadParams: ({ base, ctx, refs, }: {
38
38
  ctx: BuildContext;
39
39
  refs: ReadParamRefs;
40
40
  }) => ParameterObject[];
41
+ export type WriteOperation = 'create' | 'update' | 'updateByID' | 'delete' | 'duplicate' | 'globalUpdate';
42
+ export declare const writeParams: ({ base, entity, ctx, refs, operation, }: {
43
+ base: string;
44
+ entity: Entity;
45
+ ctx: BuildContext;
46
+ refs: ReadParamRefs;
47
+ operation: WriteOperation;
48
+ }) => ParameterObject[];
41
49
  export declare const whereParam: (base: string) => ParameterObject;
42
50
  export declare const buildListParams: ({ base, fields, ctx, refs, }: {
43
51
  base: string;
@@ -300,6 +300,98 @@ const commonReadParams = ({ base, ctx, refs })=>{
300
300
  if (refs.joins) params.push(objectParam('joins', refTo(joinsSchemaName(base))));
301
301
  return params;
302
302
  };
303
+ const SHARED = [
304
+ 'depth',
305
+ 'locale',
306
+ 'fallback-locale',
307
+ 'select',
308
+ 'populate'
309
+ ];
310
+ const ALL_LOCALES = [
311
+ 'publishAllLocales',
312
+ 'unpublishAllLocales'
313
+ ];
314
+ const WRITE_PARAMS = {
315
+ create: new Set([
316
+ ...SHARED,
317
+ 'draft',
318
+ 'autosave',
319
+ 'publishAllLocales'
320
+ ]),
321
+ update: new Set([
322
+ ...SHARED,
323
+ 'draft',
324
+ 'trash',
325
+ 'overrideLock',
326
+ ...ALL_LOCALES
327
+ ]),
328
+ updateByID: new Set([
329
+ ...SHARED,
330
+ 'draft',
331
+ 'trash',
332
+ 'autosave',
333
+ 'overrideLock',
334
+ ...ALL_LOCALES
335
+ ]),
336
+ delete: new Set([
337
+ ...SHARED,
338
+ 'trash',
339
+ 'overrideLock'
340
+ ]),
341
+ duplicate: new Set([
342
+ ...SHARED,
343
+ 'draft',
344
+ 'selectedLocales[]'
345
+ ]),
346
+ globalUpdate: new Set([
347
+ ...SHARED,
348
+ 'draft',
349
+ 'autosave',
350
+ ...ALL_LOCALES
351
+ ])
352
+ };
353
+ const entityWriteParams = (entity, ctx)=>{
354
+ const t = makeT(ctx.i18n);
355
+ const flag = (name, key)=>({
356
+ name,
357
+ in: 'query',
358
+ description: t(key),
359
+ schema: {
360
+ type: 'boolean'
361
+ }
362
+ });
363
+ const drafts = entity.versions ? entity.versions.drafts : false;
364
+ const params = [];
365
+ if (drafts && drafts.autosave) params.push(flag('autosave', 'paramAutosave'));
366
+ if (drafts && drafts.localizeStatus) {
367
+ params.push(flag('publishAllLocales', 'paramPublishAllLocales'));
368
+ params.push(flag('unpublishAllLocales', 'paramUnpublishAllLocales'));
369
+ }
370
+ if (false !== entity.lockDocuments) params.push(flag('overrideLock', 'paramOverrideLock'));
371
+ if (ctx.locales.length > 0) params.push({
372
+ name: 'selectedLocales[]',
373
+ in: 'query',
374
+ description: t('paramSelectedLocales'),
375
+ style: 'form',
376
+ explode: true,
377
+ schema: {
378
+ type: 'array',
379
+ items: {
380
+ type: 'string',
381
+ enum: ctx.locales
382
+ }
383
+ }
384
+ });
385
+ return params;
386
+ };
387
+ const writeParams = ({ base, entity, ctx, refs, operation })=>[
388
+ ...commonReadParams({
389
+ base,
390
+ ctx,
391
+ refs
392
+ }),
393
+ ...entityWriteParams(entity, ctx)
394
+ ].filter((p)=>WRITE_PARAMS[operation].has(p.name));
303
395
  const whereParam = (base)=>objectParam('where', refTo(querySchemaName(base)));
304
396
  const buildListParams = ({ base, fields, ctx, refs })=>{
305
397
  const params = [
@@ -341,4 +433,4 @@ const buildListParams = ({ base, fields, ctx, refs })=>{
341
433
  if (collectionHasFilters(fields)) params.push(whereParam(base));
342
434
  return params;
343
435
  };
344
- export { buildJoinsSchema, buildListParams, buildParamSchemas, buildPopulateSchema, buildQueryOperationsSchema, buildSelectSchema, collectionHasFilters, commonReadParams, whereParam };
436
+ export { buildJoinsSchema, buildListParams, buildParamSchemas, buildPopulateSchema, buildQueryOperationsSchema, buildSelectSchema, collectionHasFilters, commonReadParams, whereParam, writeParams };
@@ -2,40 +2,63 @@ import { makeT } from "../../translations/index.js";
2
2
  import { ERRORS, errorResponses, jsonBody, jsonResponse, messageResponse } from "../components.js";
3
3
  import { refTo, schemaName } from "../names.js";
4
4
  import { authTagName } from "../tags.js";
5
- const identifierProps = (auth)=>{
6
- const loginWithUsername = Boolean(auth?.loginWithUsername);
7
- const usernameOnly = 'object' == typeof auth?.loginWithUsername && false === auth.loginWithUsername.requireEmail;
8
- if (loginWithUsername && usernameOnly) return {
5
+ const identifierBody = (auth, extra = {})=>{
6
+ const email = {
7
+ type: 'string',
8
+ format: 'email'
9
+ };
10
+ const username = {
11
+ type: 'string'
12
+ };
13
+ const extraRequired = Object.keys(extra);
14
+ const loginWithUsername = auth?.loginWithUsername;
15
+ const base = {
16
+ type: 'object',
17
+ additionalProperties: false
18
+ };
19
+ if (!loginWithUsername) return {
20
+ ...base,
9
21
  properties: {
10
- username: {
11
- type: 'string'
12
- }
22
+ email,
23
+ ...extra
13
24
  },
14
25
  required: [
15
- 'username'
26
+ 'email',
27
+ ...extraRequired
16
28
  ]
17
29
  };
18
- if (loginWithUsername) return {
30
+ if ('object' == typeof loginWithUsername && loginWithUsername.allowEmailLogin) return {
31
+ ...base,
19
32
  properties: {
20
- email: {
21
- type: 'string',
22
- format: 'email'
33
+ email,
34
+ username,
35
+ ...extra
36
+ },
37
+ ...extraRequired.length > 0 ? {
38
+ required: extraRequired
39
+ } : {},
40
+ anyOf: [
41
+ {
42
+ required: [
43
+ 'email'
44
+ ]
23
45
  },
24
- username: {
25
- type: 'string'
46
+ {
47
+ required: [
48
+ 'username'
49
+ ]
26
50
  }
27
- },
28
- required: []
51
+ ]
29
52
  };
30
53
  return {
54
+ ...base,
31
55
  properties: {
32
- email: {
33
- type: 'string',
34
- format: 'email'
35
- }
56
+ username,
57
+ ...extra
36
58
  },
37
59
  required: [
38
- 'email'
60
+ 'username',
61
+ ...extraRequired
39
62
  ]
40
63
  };
41
64
  };
@@ -48,7 +71,6 @@ const buildAuthPaths = ({ collection, ctx, includeAdmin, nestedTags })=>{
48
71
  const tag = [
49
72
  nestedTags ? authTagName(name) : name
50
73
  ];
51
- const ident = identifierProps(auth);
52
74
  const userRef = {
53
75
  $ref: refTo(name)
54
76
  };
@@ -64,20 +86,11 @@ const buildAuthPaths = ({ collection, ctx, includeAdmin, nestedTags })=>{
64
86
  const paths = {
65
87
  [`${base}/login`]: {
66
88
  post: op(`login${name}`, ERRORS.login, {
67
- requestBody: jsonBody({
68
- type: 'object',
69
- additionalProperties: false,
70
- properties: {
71
- ...ident.properties,
72
- password: {
73
- type: 'string'
74
- }
75
- },
76
- required: [
77
- ...ident.required,
78
- 'password'
79
- ]
80
- }),
89
+ requestBody: jsonBody(identifierBody(auth, {
90
+ password: {
91
+ type: 'string'
92
+ }
93
+ })),
81
94
  responses: {
82
95
  200: jsonResponse(t('authLogin'), {
83
96
  type: 'object',
@@ -146,19 +159,7 @@ const buildAuthPaths = ({ collection, ctx, includeAdmin, nestedTags })=>{
146
159
  },
147
160
  [`${base}/forgot-password`]: {
148
161
  post: op(`forgotPassword${name}`, ERRORS.authAction, {
149
- requestBody: jsonBody({
150
- type: 'object',
151
- additionalProperties: false,
152
- properties: {
153
- email: {
154
- type: 'string',
155
- format: 'email'
156
- }
157
- },
158
- required: [
159
- 'email'
160
- ]
161
- }),
162
+ requestBody: jsonBody(identifierBody(auth)),
162
163
  responses: messageResponse(t('authForgotPassword'))
163
164
  })
164
165
  },
@@ -247,21 +248,9 @@ const buildAuthPaths = ({ collection, ctx, includeAdmin, nestedTags })=>{
247
248
  })
248
249
  };
249
250
  }
250
- if (auth.maxLoginAttempts && auth.maxLoginAttempts > 0) paths[`${base}/unlock`] = {
251
+ paths[`${base}/unlock`] = {
251
252
  post: op(`unlock${name}`, ERRORS.authAction, {
252
- requestBody: jsonBody({
253
- type: 'object',
254
- additionalProperties: false,
255
- properties: {
256
- email: {
257
- type: 'string',
258
- format: 'email'
259
- }
260
- },
261
- required: [
262
- 'email'
263
- ]
264
- }),
253
+ requestBody: jsonBody(identifierBody(auth)),
265
254
  responses: messageResponse(t('authUnlock'))
266
255
  })
267
256
  };
@@ -1,7 +1,9 @@
1
1
  import type { SanitizedCollectionConfig } from 'payload';
2
2
  import type { PathsObject } from '@scalar/openapi-types/3.2';
3
3
  import type { BuildContext } from '../../types.js';
4
- export declare const buildCollectionPaths: ({ collection, ctx, }: {
4
+ import type { EntitySecurity } from '../security.js';
5
+ export declare const buildCollectionPaths: ({ collection, ctx, security, }: {
5
6
  collection: SanitizedCollectionConfig;
6
7
  ctx: BuildContext;
8
+ security?: EntitySecurity;
7
9
  }) => PathsObject;