@seshuk/payload-plugin-openapi 1.0.0-beta.1 → 1.0.0-beta.2
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/README.md +8 -4
- package/dist/server/spec/buildDocument.js +62 -9
- package/dist/server/spec/buildDocument.js.map +1 -1
- package/dist/server/spec/components.js.map +1 -1
- package/dist/server/spec/entitySchemas.d.ts +5 -6
- package/dist/server/spec/entitySchemas.js.map +1 -1
- package/dist/server/spec/filters.d.ts +5 -1
- package/dist/server/spec/filters.js +21 -10
- package/dist/server/spec/filters.js.map +1 -1
- package/dist/server/spec/hierarchy.js +9 -4
- package/dist/server/spec/hierarchy.js.map +1 -1
- package/dist/server/spec/paths/collections.js +111 -7
- package/dist/server/spec/paths/collections.js.map +1 -1
- package/dist/server/spec/paths/custom.d.ts +4 -2
- package/dist/server/spec/paths/custom.js +1 -1
- package/dist/server/spec/paths/custom.js.map +1 -1
- package/dist/server/spec/paths/system.d.ts +2 -1
- package/dist/server/spec/paths/system.js +67 -1
- package/dist/server/spec/paths/system.js.map +1 -1
- package/dist/server/spec/plugins/ecommerce.d.ts +3 -0
- package/dist/server/spec/plugins/ecommerce.js +270 -0
- package/dist/server/spec/plugins/ecommerce.js.map +1 -0
- package/dist/server/spec/plugins/importExport.d.ts +3 -0
- package/dist/server/spec/plugins/importExport.js +202 -0
- package/dist/server/spec/plugins/importExport.js.map +1 -0
- package/dist/server/spec/plugins/index.d.ts +3 -0
- package/dist/server/spec/plugins/index.js +21 -0
- package/dist/server/spec/plugins/index.js.map +1 -0
- package/dist/server/spec/plugins/mcp.d.ts +3 -0
- package/dist/server/spec/plugins/mcp.js +118 -0
- package/dist/server/spec/plugins/mcp.js.map +1 -0
- package/dist/server/spec/plugins/multiTenant.d.ts +3 -0
- package/dist/server/spec/plugins/multiTenant.js +39 -0
- package/dist/server/spec/plugins/multiTenant.js.map +1 -0
- package/dist/server/spec/plugins/search.d.ts +3 -0
- package/dist/server/spec/plugins/search.js +43 -0
- package/dist/server/spec/plugins/search.js.map +1 -0
- package/dist/server/spec/plugins/seo.d.ts +3 -0
- package/dist/server/spec/plugins/seo.js +83 -0
- package/dist/server/spec/plugins/seo.js.map +1 -0
- package/dist/server/spec/plugins/shared.d.ts +35 -0
- package/dist/server/spec/plugins/shared.js +37 -0
- package/dist/server/spec/plugins/shared.js.map +1 -0
- package/dist/server/spec/plugins/storageR2.d.ts +3 -0
- package/dist/server/spec/plugins/storageR2.js +91 -0
- package/dist/server/spec/plugins/storageR2.js.map +1 -0
- package/dist/server/spec/plugins/stripe.d.ts +10 -0
- package/dist/server/spec/plugins/stripe.js +102 -0
- package/dist/server/spec/plugins/stripe.js.map +1 -0
- package/dist/server/spec/security.d.ts +2 -1
- package/dist/server/spec/security.js +5 -2
- package/dist/server/spec/security.js.map +1 -1
- package/dist/server/spec/tags.d.ts +4 -1
- package/dist/server/spec/tags.js +18 -2
- package/dist/server/spec/tags.js.map +1 -1
- package/dist/server/ui/html.js +2 -2
- package/dist/server/ui/html.js.map +1 -1
- package/dist/shared/translations/locales/ar.js +74 -0
- package/dist/shared/translations/locales/ar.js.map +1 -1
- package/dist/shared/translations/locales/az.js +74 -0
- package/dist/shared/translations/locales/az.js.map +1 -1
- package/dist/shared/translations/locales/bg.js +74 -0
- package/dist/shared/translations/locales/bg.js.map +1 -1
- package/dist/shared/translations/locales/bnBd.js +74 -0
- package/dist/shared/translations/locales/bnBd.js.map +1 -1
- package/dist/shared/translations/locales/bnIn.js +74 -0
- package/dist/shared/translations/locales/bnIn.js.map +1 -1
- package/dist/shared/translations/locales/ca.js +74 -0
- package/dist/shared/translations/locales/ca.js.map +1 -1
- package/dist/shared/translations/locales/cs.js +74 -0
- package/dist/shared/translations/locales/cs.js.map +1 -1
- package/dist/shared/translations/locales/da.js +74 -0
- package/dist/shared/translations/locales/da.js.map +1 -1
- package/dist/shared/translations/locales/de.js +74 -0
- package/dist/shared/translations/locales/de.js.map +1 -1
- package/dist/shared/translations/locales/en.js +74 -0
- package/dist/shared/translations/locales/en.js.map +1 -1
- package/dist/shared/translations/locales/es.js +74 -0
- package/dist/shared/translations/locales/es.js.map +1 -1
- package/dist/shared/translations/locales/et.js +74 -0
- package/dist/shared/translations/locales/et.js.map +1 -1
- package/dist/shared/translations/locales/fa.js +74 -0
- package/dist/shared/translations/locales/fa.js.map +1 -1
- package/dist/shared/translations/locales/fr.js +74 -0
- package/dist/shared/translations/locales/fr.js.map +1 -1
- package/dist/shared/translations/locales/he.js +74 -0
- package/dist/shared/translations/locales/he.js.map +1 -1
- package/dist/shared/translations/locales/hr.js +74 -0
- package/dist/shared/translations/locales/hr.js.map +1 -1
- package/dist/shared/translations/locales/hu.js +74 -0
- package/dist/shared/translations/locales/hu.js.map +1 -1
- package/dist/shared/translations/locales/hy.js +74 -0
- package/dist/shared/translations/locales/hy.js.map +1 -1
- package/dist/shared/translations/locales/id.js +74 -0
- package/dist/shared/translations/locales/id.js.map +1 -1
- package/dist/shared/translations/locales/is.js +74 -0
- package/dist/shared/translations/locales/is.js.map +1 -1
- package/dist/shared/translations/locales/it.js +74 -0
- package/dist/shared/translations/locales/it.js.map +1 -1
- package/dist/shared/translations/locales/ja.js +74 -0
- package/dist/shared/translations/locales/ja.js.map +1 -1
- package/dist/shared/translations/locales/ko.js +74 -0
- package/dist/shared/translations/locales/ko.js.map +1 -1
- package/dist/shared/translations/locales/lt.js +74 -0
- package/dist/shared/translations/locales/lt.js.map +1 -1
- package/dist/shared/translations/locales/lv.js +74 -0
- package/dist/shared/translations/locales/lv.js.map +1 -1
- package/dist/shared/translations/locales/my.js +74 -0
- package/dist/shared/translations/locales/my.js.map +1 -1
- package/dist/shared/translations/locales/nb.js +74 -0
- package/dist/shared/translations/locales/nb.js.map +1 -1
- package/dist/shared/translations/locales/nl.js +74 -0
- package/dist/shared/translations/locales/nl.js.map +1 -1
- package/dist/shared/translations/locales/pl.js +74 -0
- package/dist/shared/translations/locales/pl.js.map +1 -1
- package/dist/shared/translations/locales/pt.js +74 -0
- package/dist/shared/translations/locales/pt.js.map +1 -1
- package/dist/shared/translations/locales/ro.js +74 -0
- package/dist/shared/translations/locales/ro.js.map +1 -1
- package/dist/shared/translations/locales/rs.js +74 -0
- package/dist/shared/translations/locales/rs.js.map +1 -1
- package/dist/shared/translations/locales/rsLatin.js +74 -0
- package/dist/shared/translations/locales/rsLatin.js.map +1 -1
- package/dist/shared/translations/locales/ru.js +74 -0
- package/dist/shared/translations/locales/ru.js.map +1 -1
- package/dist/shared/translations/locales/sk.js +74 -0
- package/dist/shared/translations/locales/sk.js.map +1 -1
- package/dist/shared/translations/locales/sl.js +74 -0
- package/dist/shared/translations/locales/sl.js.map +1 -1
- package/dist/shared/translations/locales/sv.js +74 -0
- package/dist/shared/translations/locales/sv.js.map +1 -1
- package/dist/shared/translations/locales/ta.js +74 -0
- package/dist/shared/translations/locales/ta.js.map +1 -1
- package/dist/shared/translations/locales/th.js +74 -0
- package/dist/shared/translations/locales/th.js.map +1 -1
- package/dist/shared/translations/locales/tr.js +74 -0
- package/dist/shared/translations/locales/tr.js.map +1 -1
- package/dist/shared/translations/locales/uk.js +74 -0
- package/dist/shared/translations/locales/uk.js.map +1 -1
- package/dist/shared/translations/locales/vi.js +74 -0
- package/dist/shared/translations/locales/vi.js.map +1 -1
- package/dist/shared/translations/locales/zh.js +74 -0
- package/dist/shared/translations/locales/zh.js.map +1 -1
- package/dist/shared/translations/locales/zhTw.js +74 -0
- package/dist/shared/translations/locales/zhTw.js.map +1 -1
- package/dist/shared/translations/types.d.ts +74 -0
- package/dist/shared/types/index.d.ts +23 -8
- package/package.json +23 -15
package/README.md
CHANGED
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
|
|
21
21
|
## Features
|
|
22
22
|
|
|
23
|
-
- **Full spec from your config** — collections, globals, auth, versions, uploads, and
|
|
23
|
+
- **Full spec from your config** — collections, globals, auth, versions, uploads, jobs, and the endpoints of official Payload plugins are documented with zero annotation. See the [endpoint list](https://payload-plugin-openapi.seshuk.im/v1/reference/endpoints).
|
|
24
24
|
- **Interactive docs included** — mount Scalar or Swagger UI, or both on different paths.
|
|
25
25
|
- **Native metadata** — document custom endpoints and refine field schemas through Payload's own `custom.openapi` key. No wrapper, no separate registry.
|
|
26
26
|
- **Precise filtering** — choose exactly which entities and operations end up in the spec.
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
|
|
32
32
|
## Quick start
|
|
33
33
|
|
|
34
|
-
Requires **Payload 4.0.0-
|
|
34
|
+
Requires **Payload 4.0.0-beta.0 or later**, **Next.js 16.4 or later** and **Node.js 24.15 or later**. On Payload 3, use `@seshuk/payload-plugin-openapi@0`.
|
|
35
35
|
|
|
36
36
|
### Install
|
|
37
37
|
|
|
@@ -39,15 +39,15 @@ Requires **Payload 4.0.0-canary.38**, **Next.js 16.4 or later** and **Node.js 24
|
|
|
39
39
|
npm install @seshuk/payload-plugin-openapi@beta
|
|
40
40
|
```
|
|
41
41
|
|
|
42
|
-
v1 is in beta under the `beta` npm tag, and Payload 4 is published under `
|
|
42
|
+
v1 is in beta under the `beta` npm tag, and Payload 4 is published under `beta`.
|
|
43
43
|
|
|
44
44
|
### Configure
|
|
45
45
|
|
|
46
46
|
Add the plugin to your Payload config, plus a docs UI renderer:
|
|
47
47
|
|
|
48
48
|
```typescript
|
|
49
|
-
import { buildConfig } from 'payload'
|
|
50
49
|
import { openapi, scalar } from '@seshuk/payload-plugin-openapi'
|
|
50
|
+
import { buildConfig } from 'payload'
|
|
51
51
|
|
|
52
52
|
export default buildConfig({
|
|
53
53
|
plugins: [
|
|
@@ -121,6 +121,10 @@ Full docs are at **<https://payload-plugin-openapi.seshuk.im/>**:
|
|
|
121
121
|
- [CLI — `openapi:generate`](https://payload-plugin-openapi.seshuk.im/v1/cli/generate)
|
|
122
122
|
- [Examples](https://payload-plugin-openapi.seshuk.im/v1/guides/examples)
|
|
123
123
|
|
|
124
|
+
## Official plugins
|
|
125
|
+
|
|
126
|
+
When you install an official Payload plugin, the spec documents the REST endpoints it mounts: `@payloadcms/plugin-ecommerce`, `plugin-stripe`, `plugin-mcp`, `plugin-seo`, `plugin-search`, `plugin-multi-tenant`, `plugin-import-export` and `storage-r2`. There is nothing to configure. Filter them with `excludeOperations: [{ plugin: '@payloadcms/plugin-stripe' }]` or `{ kind: 'plugin' }`. See [Official plugins](https://payload-plugin-openapi.seshuk.im/v1/reference/endpoints#official-plugins).
|
|
127
|
+
|
|
124
128
|
## For plugin authors
|
|
125
129
|
|
|
126
130
|
If you maintain a Payload plugin, attach a `custom.openapi` Operation Object to the endpoints you add and they show up in the generated spec — no dependency on this package, and nothing for your users to wire up. The same key works on fields. If this plugin isn't installed, the metadata is inert. See [For plugin authors](https://payload-plugin-openapi.seshuk.im/v1/guides/plugin-authors).
|
|
@@ -6,17 +6,29 @@ import { PLUGIN_NAME } from "../../shared/constants.js";
|
|
|
6
6
|
import { makeT } from "../../shared/translations/index.js";
|
|
7
7
|
import { buildAuthPaths } from "./paths/auth.js";
|
|
8
8
|
import { buildParamSchemas, buildQueryOperationsSchema, collectionHasFilters } from "./params.js";
|
|
9
|
+
import { filterOperations, shouldIncludeCollection, shouldIncludeGlobal, stripPluginKeys } from "./filters.js";
|
|
10
|
+
import { applySecurity, evaluateAccess, resolveEntitySecurity, securedRequirement } from "./security.js";
|
|
9
11
|
import { buildCollectionPaths } from "./paths/collections.js";
|
|
10
|
-
import { buildCustomEndpointPaths } from "./paths/custom.js";
|
|
12
|
+
import { buildCustomEndpointPaths, getOpenapiMeta } from "./paths/custom.js";
|
|
11
13
|
import { buildGlobalPaths } from "./paths/globals.js";
|
|
12
14
|
import { buildJobsPaths } from "./paths/jobs.js";
|
|
13
15
|
import { buildSystemPaths } from "./paths/system.js";
|
|
14
16
|
import { buildVersionPaths, versionComponentSchemas } from "./paths/versions.js";
|
|
17
|
+
import { OFFICIAL_PLUGINS } from "./plugins/index.js";
|
|
15
18
|
import { buildBlockSchema, buildEntitySchemas } from "./entitySchemas.js";
|
|
16
|
-
import { filterOperations, shouldIncludeCollection, shouldIncludeGlobal } from "./filters.js";
|
|
17
19
|
import { applyHierarchy } from "./hierarchy.js";
|
|
18
|
-
import { applySecurity, evaluateAccess, resolveEntitySecurity, securedRequirement } from "./security.js";
|
|
19
20
|
import { getTranslation } from "@payloadcms/translations";
|
|
21
|
+
const OPERATION_METHODS = /* @__PURE__ */ new Set([
|
|
22
|
+
"get",
|
|
23
|
+
"put",
|
|
24
|
+
"post",
|
|
25
|
+
"delete",
|
|
26
|
+
"options",
|
|
27
|
+
"head",
|
|
28
|
+
"patch",
|
|
29
|
+
"trace",
|
|
30
|
+
"query"
|
|
31
|
+
]);
|
|
20
32
|
const resolveDescription = (description, ctx) => {
|
|
21
33
|
if (description == null) return void 0;
|
|
22
34
|
const resolved = getTranslation(description, ctx.i18n);
|
|
@@ -39,15 +51,17 @@ const buildDocument = async (input) => {
|
|
|
39
51
|
const schemas = {};
|
|
40
52
|
const { filters } = options;
|
|
41
53
|
const login = options.serve && options.interactiveAuth.enabled;
|
|
42
|
-
const finalize = async (group, kind, slug) => applySecurity({
|
|
54
|
+
const finalize = async (group, kind, slug, plugin) => applySecurity({
|
|
43
55
|
paths: await filterOperations({
|
|
44
56
|
paths: group,
|
|
45
57
|
slug,
|
|
46
58
|
kind,
|
|
47
|
-
filters
|
|
59
|
+
filters,
|
|
60
|
+
plugin
|
|
48
61
|
}),
|
|
49
62
|
slug,
|
|
50
63
|
kind,
|
|
64
|
+
plugin,
|
|
51
65
|
security: options.security,
|
|
52
66
|
login
|
|
53
67
|
});
|
|
@@ -192,12 +206,12 @@ const buildDocument = async (input) => {
|
|
|
192
206
|
logger.warn(`${PLUGIN_NAME}: skipped global "${global.slug}": ${error.message}`);
|
|
193
207
|
}
|
|
194
208
|
if (filters.includeCustom) try {
|
|
195
|
-
Object.assign(paths, await finalize(buildCustomEndpointPaths({
|
|
209
|
+
Object.assign(paths, stripPluginKeys(await finalize(buildCustomEndpointPaths({
|
|
196
210
|
config,
|
|
197
211
|
collections,
|
|
198
212
|
globals,
|
|
199
213
|
ctx
|
|
200
|
-
}), "custom"));
|
|
214
|
+
}), "custom")));
|
|
201
215
|
} catch (error) {
|
|
202
216
|
logger.warn(`${PLUGIN_NAME}: failed to collect custom endpoints: ${error.message}`);
|
|
203
217
|
}
|
|
@@ -215,14 +229,43 @@ const buildDocument = async (input) => {
|
|
|
215
229
|
logger.warn(`${PLUGIN_NAME}: failed to add jobs endpoint: ${error.message}`);
|
|
216
230
|
}
|
|
217
231
|
const uploadSlugs = collections.filter((c) => c.upload).map((c) => c.slug);
|
|
232
|
+
const reorderMounted = config.endpoints?.some((e) => e.method === "post" && e.path === "/reorder" && !getOpenapiMeta(e));
|
|
233
|
+
const orderableJoinTargets = new Set(input.collections.flatMap((c) => Object.entries(c.joins ?? {}).filter(([, joins]) => joins.some((j) => j.field.orderable)).map(([slug]) => slug)));
|
|
234
|
+
const reorderSlugs = reorderMounted ? collections.filter((c) => c.orderable || orderableJoinTargets.has(c.slug)).map((c) => c.slug) : [];
|
|
218
235
|
Object.assign(paths, await finalize(buildSystemPaths({
|
|
219
236
|
ctx,
|
|
220
237
|
access,
|
|
221
|
-
uploadSlugs
|
|
238
|
+
uploadSlugs,
|
|
239
|
+
reorderSlugs
|
|
222
240
|
}), "system"));
|
|
241
|
+
const pluginTags = [];
|
|
242
|
+
for (const plugin of OFFICIAL_PLUGINS) try {
|
|
243
|
+
const installed = config.plugins?.find((p) => p.slug === plugin.slug);
|
|
244
|
+
if (!(plugin.installed?.(config) ?? installed)) continue;
|
|
245
|
+
const groups = plugin.build({
|
|
246
|
+
config,
|
|
247
|
+
collections,
|
|
248
|
+
ctx,
|
|
249
|
+
options: installed?.options,
|
|
250
|
+
schemas,
|
|
251
|
+
t
|
|
252
|
+
});
|
|
253
|
+
for (const group of groups) {
|
|
254
|
+
const finalized = await finalize(group.paths, "plugin", group.slug, plugin.slug);
|
|
255
|
+
if (Object.keys(finalized).length === 0) continue;
|
|
256
|
+
for (const [path, item] of Object.entries(finalized)) paths[path] = {
|
|
257
|
+
...paths[path],
|
|
258
|
+
...item
|
|
259
|
+
};
|
|
260
|
+
if (!pluginTags.includes(plugin.tag)) pluginTags.push(plugin.tag);
|
|
261
|
+
}
|
|
262
|
+
} catch (error) {
|
|
263
|
+
logger.warn(`${PLUGIN_NAME}: skipped plugin "${plugin.slug}": ${error.message}`);
|
|
264
|
+
}
|
|
223
265
|
const systemTags = [];
|
|
224
266
|
if (hasJobs) systemTags.push("Jobs");
|
|
225
267
|
if (uploadSlugs.length > 0) systemTags.push("Uploads");
|
|
268
|
+
if (reorderSlugs.length > 0) systemTags.push("Reorder");
|
|
226
269
|
if (access) systemTags.push("Access");
|
|
227
270
|
const supportedTimezones = config.admin?.timezones?.supportedTimezones;
|
|
228
271
|
if (Array.isArray(supportedTimezones) && supportedTimezones.length > 0) schemas[schemaName("supportedTimezones")] = {
|
|
@@ -258,13 +301,23 @@ const buildDocument = async (input) => {
|
|
|
258
301
|
collections: collectionTagInfos,
|
|
259
302
|
globals: globalTagInfos,
|
|
260
303
|
systemTags,
|
|
304
|
+
pluginTags,
|
|
261
305
|
t,
|
|
262
306
|
nested: options.nestedTags
|
|
263
307
|
}),
|
|
264
308
|
"x-doc-languages": ctx.docLanguages
|
|
265
309
|
};
|
|
310
|
+
if (options.extensions.some((ext) => ext.transform)) doc = structuredClone(doc);
|
|
266
311
|
for (const ext of options.extensions) {
|
|
267
|
-
if (ext.paths)
|
|
312
|
+
if (ext.paths) {
|
|
313
|
+
const base = { ...doc.paths };
|
|
314
|
+
for (const [path, item] of Object.entries(ext.paths)) {
|
|
315
|
+
const current = base[path];
|
|
316
|
+
if (!current || !item) continue;
|
|
317
|
+
base[path] = Object.fromEntries(Object.entries(current).filter(([key]) => !(OPERATION_METHODS.has(key) && key in item)));
|
|
318
|
+
}
|
|
319
|
+
doc.paths = deepMerge(base, ext.paths);
|
|
320
|
+
}
|
|
268
321
|
if (ext.components) doc.components = deepMerge(doc.components ?? {}, ext.components);
|
|
269
322
|
if (ext.tags) {
|
|
270
323
|
const seen = new Set((doc.tags ?? []).map((tag) => tag.name));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"buildDocument.js","names":[],"sources":["../../../src/server/spec/buildDocument.ts"],"sourcesContent":["import { getTranslation } from '@payloadcms/translations'\nimport type {\n ComponentsObject,\n Document,\n PathsObject,\n SchemaObject,\n SecuritySchemeObject,\n TagObject,\n} from '@scalar/openapi-types/3.2'\nimport type {\n Access,\n BasePayload,\n Field,\n SanitizedCollectionConfig,\n SanitizedConfig,\n SanitizedGlobalConfig,\n} from 'payload'\n\nimport { buildAuthPaths } from '@/server/spec/paths/auth.js'\nimport { buildCollectionPaths } from '@/server/spec/paths/collections.js'\nimport { buildCustomEndpointPaths } from '@/server/spec/paths/custom.js'\nimport { buildGlobalPaths } from '@/server/spec/paths/globals.js'\nimport { buildJobsPaths } from '@/server/spec/paths/jobs.js'\nimport { buildSystemPaths } from '@/server/spec/paths/system.js'\nimport { buildVersionPaths, versionComponentSchemas } from '@/server/spec/paths/versions.js'\nimport { PLUGIN_NAME } from '@/shared/constants.js'\nimport { makeT } from '@/shared/translations/index.js'\nimport type { BuildContext, OperationKind, ResolvedOptions } from '@/shared/types/index.js'\nimport { deepMerge } from '@/shared/utils.js'\n\nimport {\n ERROR_SCHEMA_NAME,\n INTERACTIVE_SCHEME_NAME,\n SECURITY_SCHEME_NAME,\n buildErrorResponseSchema,\n buildListEnvelopeSchema,\n interactiveSecurityScheme,\n securityScheme,\n} from './components.js'\nimport { buildBlockSchema, buildEntitySchemas } from './entitySchemas.js'\nimport { filterOperations, shouldIncludeCollection, shouldIncludeGlobal } from './filters.js'\nimport { applyHierarchy } from './hierarchy.js'\nimport {\n blockSchemaName,\n createSchemaName,\n globalSchemaName,\n joinsSchemaName,\n listSchemaName,\n populateSchemaName,\n querySchemaName,\n schemaName,\n selectSchemaName,\n updateSchemaName,\n} from './names.js'\nimport { buildParamSchemas, buildQueryOperationsSchema, collectionHasFilters } from './params.js'\nimport { applySecurity, evaluateAccess, resolveEntitySecurity, securedRequirement } from './security.js'\nimport { buildTagHierarchy, type CollectionTagInfo, type GlobalTagInfo, type SystemTag } from './tags.js'\n\nconst resolveDescription = (description: unknown, ctx: BuildContext): string | undefined => {\n if (description == null) return undefined\n const resolved: unknown = getTranslation(description as string, ctx.i18n)\n return typeof resolved === 'string' && resolved.length > 0 ? resolved : undefined\n}\n\nconst registerParamSchemas = (\n schemas: Record<string, SchemaObject>,\n base: string,\n fields: Field[],\n ctx: BuildContext,\n): void => {\n const { select, populate, joins } = buildParamSchemas({ fields, ctx })\n schemas[selectSchemaName(base)] = select\n if (populate) schemas[populateSchemaName(base)] = populate\n if (joins) schemas[joinsSchemaName(base)] = joins\n}\n\nexport interface BuildInput {\n options: ResolvedOptions\n servers: { url: string }[]\n collections: SanitizedCollectionConfig[]\n globals: SanitizedGlobalConfig[]\n config: SanitizedConfig\n ctx: BuildContext\n payload: BasePayload\n}\n\nexport const buildDocument = async (input: BuildInput): Promise<Document> => {\n const { options, ctx, config, payload } = input\n const { logger } = payload\n const t = makeT(ctx.i18n)\n const paths: PathsObject = {}\n const schemas: Record<string, SchemaObject> = {}\n\n const { filters } = options\n const login = options.serve && options.interactiveAuth.enabled\n const finalize = async (group: PathsObject, kind: OperationKind, slug?: string): Promise<PathsObject> =>\n applySecurity({\n paths: await filterOperations({ paths: group, slug, kind, filters }),\n slug,\n kind,\n security: options.security,\n login,\n })\n const access = filters.includeAuth && filters.includeAdminAuth\n const collections = input.collections.filter((c) => shouldIncludeCollection(c, filters))\n const globals = input.globals.filter((g) => shouldIncludeGlobal(g, filters))\n\n const collectionTagInfos: CollectionTagInfo[] = []\n const globalTagInfos: GlobalTagInfo[] = []\n\n for (const block of config.blocks ?? []) {\n try {\n schemas[blockSchemaName(block.slug)] = buildBlockSchema(block, config)\n } catch (error) {\n logger.warn(`${PLUGIN_NAME}: skipped block \"${block.slug}\": ${(error as Error).message}`)\n }\n }\n\n for (const collection of collections) {\n try {\n const { read, create, update } = await buildEntitySchemas({ entity: collection, config, ctx })\n const base = schemaName(collection.slug)\n collectionTagInfos.push({\n base,\n hasAuth: filters.includeAuth && Boolean(collection.auth),\n hasVersions: Boolean(filters.includeVersions && collection.versions),\n description: resolveDescription(collection.admin?.description, ctx),\n })\n schemas[base] = read\n schemas[createSchemaName(base)] = create\n schemas[updateSchemaName(base)] = update\n schemas[listSchemaName(base)] = buildListEnvelopeSchema(base)\n if (collectionHasFilters(collection.fields)) {\n schemas[querySchemaName(base)] = buildQueryOperationsSchema({ base, fields: collection.fields, ctx })\n }\n registerParamSchemas(schemas, base, collection.fields, ctx)\n\n const security = await resolveEntitySecurity({\n entity: collection,\n operations: ['read', 'create', 'update', 'delete', 'validate', 'readVersions'],\n locale: ctx.locales[0],\n baseAccess: config.baseAccess,\n })\n const collectionPaths: PathsObject = { ...buildCollectionPaths({ collection, ctx, security, access }) }\n\n if (filters.includeAuth) {\n Object.assign(\n collectionPaths,\n buildAuthPaths({\n collection,\n ctx,\n includeAdmin: filters.includeAdminAuth,\n nestedTags: options.nestedTags,\n }),\n )\n }\n\n if (filters.includeVersions && collection.versions) {\n const { version, versionList } = versionComponentSchemas({ docBase: base, base, idType: ctx.defaultIDType })\n schemas[`${base}Version`] = version\n schemas[`${base}VersionList`] = versionList\n Object.assign(\n collectionPaths,\n buildVersionPaths({\n entity: collection,\n pathBase: `${ctx.apiRoute}/${collection.slug}`,\n ctx,\n schemaBase: base,\n nestedTags: options.nestedTags,\n security,\n }),\n )\n }\n\n applyHierarchy({ collection, base, paths: collectionPaths, schemas, ctx })\n Object.assign(paths, await finalize(collectionPaths, 'collection', collection.slug))\n } catch (error) {\n logger.warn(`${PLUGIN_NAME}: skipped collection \"${collection.slug}\": ${(error as Error).message}`)\n }\n }\n\n for (const global of globals) {\n try {\n const { read, update } = await buildEntitySchemas({ entity: global, config, ctx })\n const gbase = globalSchemaName(global.slug)\n globalTagInfos.push({\n base: gbase,\n hasVersions: Boolean(filters.includeVersions && global.versions),\n description: resolveDescription(global.admin?.description, ctx),\n })\n schemas[gbase] = read\n schemas[updateSchemaName(gbase)] = update\n registerParamSchemas(schemas, gbase, global.fields, ctx)\n\n const security = await resolveEntitySecurity({\n entity: global,\n operations: ['read', 'update', 'validate', 'readVersions'],\n locale: ctx.locales[0],\n baseAccess: config.baseAccess,\n })\n const globalPaths: PathsObject = { ...buildGlobalPaths({ global, ctx, security, access }) }\n\n if (filters.includeVersions && global.versions) {\n const { version, versionList } = versionComponentSchemas({\n docBase: gbase,\n base: gbase,\n idType: ctx.defaultIDType,\n })\n schemas[`${gbase}Version`] = version\n schemas[`${gbase}VersionList`] = versionList\n Object.assign(\n globalPaths,\n buildVersionPaths({\n entity: global,\n pathBase: `${ctx.apiRoute}/globals/${global.slug}`,\n ctx,\n schemaBase: gbase,\n nestedTags: options.nestedTags,\n security,\n global: true,\n }),\n )\n }\n\n Object.assign(paths, await finalize(globalPaths, 'global', global.slug))\n } catch (error) {\n logger.warn(`${PLUGIN_NAME}: skipped global \"${global.slug}\": ${(error as Error).message}`)\n }\n }\n\n if (filters.includeCustom) {\n try {\n Object.assign(paths, await finalize(buildCustomEndpointPaths({ config, collections, globals, ctx }), 'custom'))\n } catch (error) {\n logger.warn(`${PLUGIN_NAME}: failed to collect custom endpoints: ${(error as Error).message}`)\n }\n }\n\n const jobsConfig = config.jobs as Record<string, unknown> | undefined\n const hasJobs =\n filters.includeJobs &&\n Boolean((jobsConfig?.tasks as unknown[])?.length || (jobsConfig?.workflows as unknown[])?.length)\n\n if (filters.includeJobs) {\n try {\n const runAccess = (jobsConfig?.access as { run?: Access } | undefined)?.run\n const jobsSecurity = (await evaluateAccess(runAccess, { baseAccess: config.baseAccess }))\n ? undefined\n : securedRequirement()\n Object.assign(paths, await finalize(buildJobsPaths({ config, ctx, security: jobsSecurity }), 'jobs'))\n } catch (error) {\n logger.warn(`${PLUGIN_NAME}: failed to add jobs endpoint: ${(error as Error).message}`)\n }\n }\n\n const uploadSlugs = collections.filter((c) => c.upload).map((c) => c.slug)\n Object.assign(paths, await finalize(buildSystemPaths({ ctx, access, uploadSlugs }), 'system'))\n\n const systemTags: SystemTag[] = []\n if (hasJobs) systemTags.push('Jobs')\n if (uploadSlugs.length > 0) systemTags.push('Uploads')\n if (access) systemTags.push('Access')\n\n const supportedTimezones = config.admin?.timezones?.supportedTimezones\n if (Array.isArray(supportedTimezones) && supportedTimezones.length > 0) {\n schemas[schemaName('supportedTimezones')] = {\n type: 'string',\n description: t('schemaSupportedTimezones'),\n enum: supportedTimezones.map((tz) => (typeof tz === 'string' ? tz : tz.value)),\n }\n }\n\n schemas[ERROR_SCHEMA_NAME] = buildErrorResponseSchema()\n\n const cookiePrefix = config.cookiePrefix || 'payload'\n const securitySchemes: Record<string, SecuritySchemeObject> = {\n [SECURITY_SCHEME_NAME]: securityScheme({ cookiePrefix, t }),\n }\n if (login) {\n securitySchemes[INTERACTIVE_SCHEME_NAME] = interactiveSecurityScheme({\n tokenUrl: `${ctx.apiRoute}${options.interactiveAuth.path}`,\n t,\n })\n }\n\n const localeNote =\n ctx.locales.length > 0\n ? `\\n\\n## ${t('localizationHeading')}\\n\\n` +\n t('localizationNote', {\n locales: ctx.locales.map((l) => `\\`${l}\\``).join(', '),\n })\n : ''\n const docLanguagesNote =\n ctx.docLanguages.length > 1\n ? `\\n\\n${t('docLanguagesNote', { languages: ctx.docLanguages.map((l) => `\\`${l}\\``).join(', ') })}`\n : ''\n const info = {\n ...options.info,\n description:\n `${resolveDescription(options.info.description, ctx) ?? ''}${localeNote}${docLanguagesNote}`.trim() || undefined,\n }\n\n let doc = {\n openapi: '3.2.0',\n info,\n servers: input.servers,\n paths,\n components: {\n schemas,\n securitySchemes,\n },\n tags: buildTagHierarchy({\n collections: collectionTagInfos,\n globals: globalTagInfos,\n systemTags,\n t,\n nested: options.nestedTags,\n }),\n 'x-doc-languages': ctx.docLanguages,\n } as Document\n\n for (const ext of options.extensions) {\n if (ext.paths) {\n doc.paths = deepMerge(doc.paths ?? {}, ext.paths) as PathsObject\n }\n if (ext.components) {\n doc.components = deepMerge(\n (doc.components ?? {}) as Record<string, unknown>,\n ext.components as Record<string, unknown>,\n ) as ComponentsObject\n }\n if (ext.tags) {\n const seen = new Set((doc.tags ?? []).map((tag: TagObject) => tag.name))\n doc.tags = [...(doc.tags ?? []), ...ext.tags.filter((tag: TagObject) => !seen.has(tag.name))]\n }\n if (ext.transform) {\n doc = await ext.transform({ ...ctx, doc, payload, options })\n if (typeof doc?.openapi !== 'string' || 'payload' in doc) {\n throw new Error(`[${PLUGIN_NAME}] extensions[].transform must return the document, it gets ({ doc, payload })`)\n }\n }\n }\n\n return doc\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"buildDocument.js","names":[],"sources":["../../../src/server/spec/buildDocument.ts"],"sourcesContent":["import { getTranslation } from '@payloadcms/translations'\nimport type {\n ComponentsObject,\n Document,\n PathsObject,\n SchemaObject,\n SecuritySchemeObject,\n TagObject,\n} from '@scalar/openapi-types/3.2'\nimport type {\n Access,\n BasePayload,\n Field,\n SanitizedCollectionConfig,\n SanitizedConfig,\n SanitizedGlobalConfig,\n} from 'payload'\n\nimport { buildAuthPaths } from '@/server/spec/paths/auth.js'\nimport { buildCollectionPaths } from '@/server/spec/paths/collections.js'\nimport { buildCustomEndpointPaths, getOpenapiMeta } from '@/server/spec/paths/custom.js'\nimport { buildGlobalPaths } from '@/server/spec/paths/globals.js'\nimport { buildJobsPaths } from '@/server/spec/paths/jobs.js'\nimport { buildSystemPaths } from '@/server/spec/paths/system.js'\nimport { buildVersionPaths, versionComponentSchemas } from '@/server/spec/paths/versions.js'\nimport { OFFICIAL_PLUGINS } from '@/server/spec/plugins/index.js'\nimport { PLUGIN_NAME } from '@/shared/constants.js'\nimport { makeT } from '@/shared/translations/index.js'\nimport type { BuildContext, OperationKind, ResolvedOptions } from '@/shared/types/index.js'\nimport { deepMerge } from '@/shared/utils.js'\n\nimport {\n ERROR_SCHEMA_NAME,\n INTERACTIVE_SCHEME_NAME,\n SECURITY_SCHEME_NAME,\n buildErrorResponseSchema,\n buildListEnvelopeSchema,\n interactiveSecurityScheme,\n securityScheme,\n} from './components.js'\nimport { buildBlockSchema, buildEntitySchemas } from './entitySchemas.js'\nimport { filterOperations, shouldIncludeCollection, shouldIncludeGlobal, stripPluginKeys } from './filters.js'\nimport { applyHierarchy } from './hierarchy.js'\nimport {\n blockSchemaName,\n createSchemaName,\n globalSchemaName,\n joinsSchemaName,\n listSchemaName,\n populateSchemaName,\n querySchemaName,\n schemaName,\n selectSchemaName,\n updateSchemaName,\n} from './names.js'\nimport { buildParamSchemas, buildQueryOperationsSchema, collectionHasFilters } from './params.js'\nimport { applySecurity, evaluateAccess, resolveEntitySecurity, securedRequirement } from './security.js'\nimport { buildTagHierarchy, type CollectionTagInfo, type GlobalTagInfo, type SystemTag } from './tags.js'\n\nconst OPERATION_METHODS = new Set(['get', 'put', 'post', 'delete', 'options', 'head', 'patch', 'trace', 'query'])\n\nconst resolveDescription = (description: unknown, ctx: BuildContext): string | undefined => {\n if (description == null) return undefined\n const resolved: unknown = getTranslation(description as string, ctx.i18n)\n return typeof resolved === 'string' && resolved.length > 0 ? resolved : undefined\n}\n\nconst registerParamSchemas = (\n schemas: Record<string, SchemaObject>,\n base: string,\n fields: Field[],\n ctx: BuildContext,\n): void => {\n const { select, populate, joins } = buildParamSchemas({ fields, ctx })\n schemas[selectSchemaName(base)] = select\n if (populate) schemas[populateSchemaName(base)] = populate\n if (joins) schemas[joinsSchemaName(base)] = joins\n}\n\nexport interface BuildInput {\n options: ResolvedOptions\n servers: { url: string }[]\n collections: SanitizedCollectionConfig[]\n globals: SanitizedGlobalConfig[]\n config: SanitizedConfig\n ctx: BuildContext\n payload: BasePayload\n}\n\nexport const buildDocument = async (input: BuildInput): Promise<Document> => {\n const { options, ctx, config, payload } = input\n const { logger } = payload\n const t = makeT(ctx.i18n)\n const paths: PathsObject = {}\n const schemas: Record<string, SchemaObject> = {}\n\n const { filters } = options\n const login = options.serve && options.interactiveAuth.enabled\n const finalize = async (\n group: PathsObject,\n kind: OperationKind,\n slug?: string,\n plugin?: string,\n ): Promise<PathsObject> =>\n applySecurity({\n paths: await filterOperations({ paths: group, slug, kind, filters, plugin }),\n slug,\n kind,\n plugin,\n security: options.security,\n login,\n })\n const access = filters.includeAuth && filters.includeAdminAuth\n const collections = input.collections.filter((c) => shouldIncludeCollection(c, filters))\n const globals = input.globals.filter((g) => shouldIncludeGlobal(g, filters))\n\n const collectionTagInfos: CollectionTagInfo[] = []\n const globalTagInfos: GlobalTagInfo[] = []\n\n for (const block of config.blocks ?? []) {\n try {\n schemas[blockSchemaName(block.slug)] = buildBlockSchema(block, config)\n } catch (error) {\n logger.warn(`${PLUGIN_NAME}: skipped block \"${block.slug}\": ${(error as Error).message}`)\n }\n }\n\n for (const collection of collections) {\n try {\n const { read, create, update } = await buildEntitySchemas({ entity: collection, config, ctx })\n const base = schemaName(collection.slug)\n collectionTagInfos.push({\n base,\n hasAuth: filters.includeAuth && Boolean(collection.auth),\n hasVersions: Boolean(filters.includeVersions && collection.versions),\n description: resolveDescription(collection.admin?.description, ctx),\n })\n schemas[base] = read\n schemas[createSchemaName(base)] = create\n schemas[updateSchemaName(base)] = update\n schemas[listSchemaName(base)] = buildListEnvelopeSchema(base)\n if (collectionHasFilters(collection.fields)) {\n schemas[querySchemaName(base)] = buildQueryOperationsSchema({ base, fields: collection.fields, ctx })\n }\n registerParamSchemas(schemas, base, collection.fields, ctx)\n\n const security = await resolveEntitySecurity({\n entity: collection,\n operations: ['read', 'create', 'update', 'delete', 'validate', 'readVersions'],\n locale: ctx.locales[0],\n baseAccess: config.baseAccess,\n })\n const collectionPaths: PathsObject = { ...buildCollectionPaths({ collection, ctx, security, access }) }\n\n if (filters.includeAuth) {\n Object.assign(\n collectionPaths,\n buildAuthPaths({\n collection,\n ctx,\n includeAdmin: filters.includeAdminAuth,\n nestedTags: options.nestedTags,\n }),\n )\n }\n\n if (filters.includeVersions && collection.versions) {\n const { version, versionList } = versionComponentSchemas({ docBase: base, base, idType: ctx.defaultIDType })\n schemas[`${base}Version`] = version\n schemas[`${base}VersionList`] = versionList\n Object.assign(\n collectionPaths,\n buildVersionPaths({\n entity: collection,\n pathBase: `${ctx.apiRoute}/${collection.slug}`,\n ctx,\n schemaBase: base,\n nestedTags: options.nestedTags,\n security,\n }),\n )\n }\n\n applyHierarchy({ collection, base, paths: collectionPaths, schemas, ctx })\n Object.assign(paths, await finalize(collectionPaths, 'collection', collection.slug))\n } catch (error) {\n logger.warn(`${PLUGIN_NAME}: skipped collection \"${collection.slug}\": ${(error as Error).message}`)\n }\n }\n\n for (const global of globals) {\n try {\n const { read, update } = await buildEntitySchemas({ entity: global, config, ctx })\n const gbase = globalSchemaName(global.slug)\n globalTagInfos.push({\n base: gbase,\n hasVersions: Boolean(filters.includeVersions && global.versions),\n description: resolveDescription(global.admin?.description, ctx),\n })\n schemas[gbase] = read\n schemas[updateSchemaName(gbase)] = update\n registerParamSchemas(schemas, gbase, global.fields, ctx)\n\n const security = await resolveEntitySecurity({\n entity: global,\n operations: ['read', 'update', 'validate', 'readVersions'],\n locale: ctx.locales[0],\n baseAccess: config.baseAccess,\n })\n const globalPaths: PathsObject = { ...buildGlobalPaths({ global, ctx, security, access }) }\n\n if (filters.includeVersions && global.versions) {\n const { version, versionList } = versionComponentSchemas({\n docBase: gbase,\n base: gbase,\n idType: ctx.defaultIDType,\n })\n schemas[`${gbase}Version`] = version\n schemas[`${gbase}VersionList`] = versionList\n Object.assign(\n globalPaths,\n buildVersionPaths({\n entity: global,\n pathBase: `${ctx.apiRoute}/globals/${global.slug}`,\n ctx,\n schemaBase: gbase,\n nestedTags: options.nestedTags,\n security,\n global: true,\n }),\n )\n }\n\n Object.assign(paths, await finalize(globalPaths, 'global', global.slug))\n } catch (error) {\n logger.warn(`${PLUGIN_NAME}: skipped global \"${global.slug}\": ${(error as Error).message}`)\n }\n }\n\n if (filters.includeCustom) {\n try {\n Object.assign(\n paths,\n stripPluginKeys(await finalize(buildCustomEndpointPaths({ config, collections, globals, ctx }), 'custom')),\n )\n } catch (error) {\n logger.warn(`${PLUGIN_NAME}: failed to collect custom endpoints: ${(error as Error).message}`)\n }\n }\n\n const jobsConfig = config.jobs as Record<string, unknown> | undefined\n const hasJobs =\n filters.includeJobs &&\n Boolean((jobsConfig?.tasks as unknown[])?.length || (jobsConfig?.workflows as unknown[])?.length)\n\n if (filters.includeJobs) {\n try {\n const runAccess = (jobsConfig?.access as { run?: Access } | undefined)?.run\n const jobsSecurity = (await evaluateAccess(runAccess, { baseAccess: config.baseAccess }))\n ? undefined\n : securedRequirement()\n Object.assign(paths, await finalize(buildJobsPaths({ config, ctx, security: jobsSecurity }), 'jobs'))\n } catch (error) {\n logger.warn(`${PLUGIN_NAME}: failed to add jobs endpoint: ${(error as Error).message}`)\n }\n }\n\n const uploadSlugs = collections.filter((c) => c.upload).map((c) => c.slug)\n const reorderMounted = config.endpoints?.some(\n (e) => e.method === 'post' && e.path === '/reorder' && !getOpenapiMeta(e),\n )\n const orderableJoinTargets = new Set(\n input.collections.flatMap((c) =>\n Object.entries(c.joins ?? {})\n .filter(([, joins]) => joins.some((j) => j.field.orderable))\n .map(([slug]) => slug),\n ),\n )\n const reorderSlugs = reorderMounted\n ? collections.filter((c) => c.orderable || orderableJoinTargets.has(c.slug)).map((c) => c.slug)\n : []\n Object.assign(paths, await finalize(buildSystemPaths({ ctx, access, uploadSlugs, reorderSlugs }), 'system'))\n\n const pluginTags: string[] = []\n for (const plugin of OFFICIAL_PLUGINS) {\n try {\n const installed = config.plugins?.find((p) => p.slug === plugin.slug)\n if (!(plugin.installed?.(config) ?? installed)) continue\n const groups = plugin.build({ config, collections, ctx, options: installed?.options, schemas, t })\n for (const group of groups) {\n const finalized = await finalize(group.paths, 'plugin', group.slug, plugin.slug)\n if (Object.keys(finalized).length === 0) continue\n for (const [path, item] of Object.entries(finalized)) paths[path] = { ...paths[path], ...item }\n if (!pluginTags.includes(plugin.tag)) pluginTags.push(plugin.tag)\n }\n } catch (error) {\n logger.warn(`${PLUGIN_NAME}: skipped plugin \"${plugin.slug}\": ${(error as Error).message}`)\n }\n }\n\n const systemTags: SystemTag[] = []\n if (hasJobs) systemTags.push('Jobs')\n if (uploadSlugs.length > 0) systemTags.push('Uploads')\n if (reorderSlugs.length > 0) systemTags.push('Reorder')\n if (access) systemTags.push('Access')\n\n const supportedTimezones = config.admin?.timezones?.supportedTimezones\n if (Array.isArray(supportedTimezones) && supportedTimezones.length > 0) {\n schemas[schemaName('supportedTimezones')] = {\n type: 'string',\n description: t('schemaSupportedTimezones'),\n enum: supportedTimezones.map((tz) => (typeof tz === 'string' ? tz : tz.value)),\n }\n }\n\n schemas[ERROR_SCHEMA_NAME] = buildErrorResponseSchema()\n\n const cookiePrefix = config.cookiePrefix || 'payload'\n const securitySchemes: Record<string, SecuritySchemeObject> = {\n [SECURITY_SCHEME_NAME]: securityScheme({ cookiePrefix, t }),\n }\n if (login) {\n securitySchemes[INTERACTIVE_SCHEME_NAME] = interactiveSecurityScheme({\n tokenUrl: `${ctx.apiRoute}${options.interactiveAuth.path}`,\n t,\n })\n }\n\n const localeNote =\n ctx.locales.length > 0\n ? `\\n\\n## ${t('localizationHeading')}\\n\\n` +\n t('localizationNote', {\n locales: ctx.locales.map((l) => `\\`${l}\\``).join(', '),\n })\n : ''\n const docLanguagesNote =\n ctx.docLanguages.length > 1\n ? `\\n\\n${t('docLanguagesNote', { languages: ctx.docLanguages.map((l) => `\\`${l}\\``).join(', ') })}`\n : ''\n const info = {\n ...options.info,\n description:\n `${resolveDescription(options.info.description, ctx) ?? ''}${localeNote}${docLanguagesNote}`.trim() || undefined,\n }\n\n let doc = {\n openapi: '3.2.0',\n info,\n servers: input.servers,\n paths,\n components: {\n schemas,\n securitySchemes,\n },\n tags: buildTagHierarchy({\n collections: collectionTagInfos,\n globals: globalTagInfos,\n systemTags,\n pluginTags,\n t,\n nested: options.nestedTags,\n }),\n 'x-doc-languages': ctx.docLanguages,\n } as Document\n\n if (options.extensions.some((ext) => ext.transform)) doc = structuredClone(doc)\n\n for (const ext of options.extensions) {\n if (ext.paths) {\n const base: PathsObject = { ...doc.paths }\n for (const [path, item] of Object.entries(ext.paths)) {\n const current = base[path]\n if (!current || !item) continue\n base[path] = Object.fromEntries(\n Object.entries(current).filter(([key]) => !(OPERATION_METHODS.has(key) && key in item)),\n )\n }\n doc.paths = deepMerge(base, ext.paths) as PathsObject\n }\n if (ext.components) {\n doc.components = deepMerge(\n (doc.components ?? {}) as Record<string, unknown>,\n ext.components as Record<string, unknown>,\n ) as ComponentsObject\n }\n if (ext.tags) {\n const seen = new Set((doc.tags ?? []).map((tag: TagObject) => tag.name))\n doc.tags = [...(doc.tags ?? []), ...ext.tags.filter((tag: TagObject) => !seen.has(tag.name))]\n }\n if (ext.transform) {\n doc = await ext.transform({ ...ctx, doc, payload, options })\n if (typeof doc?.openapi !== 'string' || 'payload' in doc) {\n throw new Error(`[${PLUGIN_NAME}] extensions[].transform must return the document, it gets ({ doc, payload })`)\n }\n }\n }\n\n return doc\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"components.js","names":[],"sources":["../../../src/server/spec/components.ts"],"sourcesContent":["import type {\n OperationObject,\n ParameterObject,\n ReferenceObject,\n RequestBodyObject,\n ResponseObject,\n ResponsesObject,\n SchemaObject,\n SecurityRequirementObject,\n SecuritySchemeObject,\n} from '@scalar/openapi-types/3.2'\n\nimport type { Translate } from '@/shared/translations/types.js'\n\nimport { refTo } from './names.js'\n\ntype SchemaOrRef = SchemaObject | ReferenceObject\n\nexport const nullableType = (type: 'integer' | 'string' | 'number' | 'boolean'): SchemaObject => ({\n type: [type, 'null'],\n})\n\nexport const jsonResponse = (description: string, schema: SchemaOrRef): ResponseObject => ({\n description,\n content: { 'application/json': { schema } },\n})\n\nexport const jsonOk = (description: string, schema: SchemaOrRef): ResponsesObject => ({\n '200': jsonResponse(description, schema),\n})\n\nexport const jsonBody = (schema: SchemaOrRef): RequestBodyObject => ({\n required: true,\n content: { 'application/json': { schema } },\n})\n\nexport const messageResponse = (description: string): ResponsesObject =>\n jsonOk(description, { type: 'object', properties: { message: { type: 'string' } } })\n\nexport type ErrorCode = '400' | '401' | '403' | '404' | '500'\n\nconst allowed = (): SchemaObject => ({\n oneOf: [\n { type: 'boolean', enum: [true] },\n {\n type: 'object',\n properties: { permission: { type: 'boolean' }, where: { type: 'object', additionalProperties: true } },\n required: ['permission'],\n },\n ],\n})\n\nexport const docAccessOperation = ({\n tag,\n operationId,\n operations,\n bodyRef,\n errors,\n t,\n}: {\n tag: string\n operationId: string\n operations: string[]\n bodyRef: ReferenceObject\n errors: ErrorCode[]\n t: Translate\n}): OperationObject => ({\n tags: [tag],\n operationId,\n requestBody: { description: t('docAccessBody'), content: { 'application/json': { schema: bodyRef } } },\n responses: {\n ...jsonOk(t('docAccess'), {\n type: 'object',\n properties: {\n ...Object.fromEntries(operations.map((op) => [op, allowed()])),\n fields: {\n oneOf: [\n { type: 'boolean', enum: [true] },\n { type: 'object', additionalProperties: true },\n ],\n },\n },\n }),\n ...errorResponses(errors, t),\n },\n})\n\nexport const validateOperation = ({\n tag,\n operationId,\n parameters,\n requestBody,\n errors,\n security,\n t,\n}: {\n tag: string\n operationId: string\n parameters: ParameterObject[]\n requestBody: RequestBodyObject\n errors: ErrorCode[]\n security?: SecurityRequirementObject[]\n t: Translate\n}): OperationObject => ({\n tags: [tag],\n operationId,\n parameters,\n requestBody: {\n ...requestBody,\n description: [requestBody.description, t('validateBody')].filter(Boolean).join(' '),\n },\n responses: {\n ...jsonOk(t('validateResult'), {\n type: 'object',\n properties: {\n valid: { type: 'boolean' },\n errors: {\n type: 'array',\n items: {\n type: 'object',\n properties: {\n message: { type: 'string' },\n path: { type: 'string' },\n label: { oneOf: [{ type: 'string' }, { type: 'object', additionalProperties: { type: 'string' } }] },\n locale: { type: 'string' },\n tableName: { type: 'string' },\n },\n required: ['message', 'path'],\n },\n },\n },\n required: ['valid', 'errors'],\n }),\n ...errorResponses(errors, t),\n },\n security,\n})\n\nexport const ERROR_SCHEMA_NAME = 'ErrorResponse'\n\nconst ERROR_KEYS = {\n '400': 'error400',\n '401': 'error401',\n '403': 'error403',\n '404': 'error404',\n '500': 'error500',\n} as const\n\nexport const ERRORS = {\n list: ['400', '403', '500'] as ErrorCode[],\n read: ['403', '404', '500'] as ErrorCode[],\n create: ['400', '403', '500'] as ErrorCode[],\n update: ['400', '403', '404', '500'] as ErrorCode[],\n delete: ['403', '404', '500'] as ErrorCode[],\n globalRead: ['403', '500'] as ErrorCode[],\n globalUpdate: ['400', '403', '500'] as ErrorCode[],\n login: ['400', '401', '403', '500'] as ErrorCode[],\n session: ['401', '500'] as ErrorCode[],\n authAction: ['400', '403', '500'] as ErrorCode[],\n versionList: ['400', '403', '404', '500'] as ErrorCode[],\n versionRead: ['403', '404', '500'] as ErrorCode[],\n versionRestore: ['403', '404', '500'] as ErrorCode[],\n} as const\n\nexport const buildErrorResponseSchema = (): SchemaObject => ({\n type: 'object',\n properties: {\n errors: {\n type: 'array',\n items: {\n type: 'object',\n properties: {\n message: { type: 'string' },\n name: { type: 'string' },\n field: { type: 'string' },\n data: { type: 'object', additionalProperties: true },\n },\n required: ['message'],\n },\n },\n },\n required: ['errors'],\n})\n\nconst errorRef: ReferenceObject = { $ref: refTo(ERROR_SCHEMA_NAME) }\n\nconst errorContent = (description: string): ResponseObject => ({\n description,\n content: { 'application/json': { schema: errorRef } },\n})\n\nexport const errorResponses = (codes: ErrorCode[], t: Translate): ResponsesObject => {\n const responses: ResponsesObject = {}\n for (const code of codes) responses[code] = errorContent(t(ERROR_KEYS[code]))\n return responses\n}\n\nexport const buildListEnvelopeSchema = (base: string): SchemaObject => ({\n type: 'object',\n properties: {\n docs: { type: 'array', items: { $ref: refTo(base) } },\n totalDocs: { type: 'integer' },\n limit: { type: 'integer' },\n totalPages: { type: 'integer' },\n page: { type: 'integer' },\n pagingCounter: { type: 'integer' },\n hasPrevPage: { type: 'boolean' },\n hasNextPage: { type: 'boolean' },\n prevPage: nullableType('integer'),\n nextPage: nullableType('integer'),\n },\n required: ['docs', 'totalDocs', 'limit', 'totalPages', 'page'],\n})\n\nexport const uploadRequestBody = ({\n bodyRef,\n fileRequired,\n t,\n}: {\n bodyRef: ReferenceObject\n fileRequired: boolean\n t: Translate\n}): RequestBodyObject => {\n const fieldsSchemaName = bodyRef.$ref.split('/').pop() ?? 'the document'\n return {\n required: fileRequired,\n description: t('uploadBody'),\n content: {\n 'multipart/form-data': {\n schema: {\n type: 'object',\n properties: {\n file: {\n type: 'string',\n format: 'binary',\n description: t('uploadFile'),\n },\n _payload: {\n allOf: [bodyRef],\n description: t('uploadPayloadField', { schema: fieldsSchemaName }),\n },\n },\n ...(fileRequired ? { required: ['file'] } : {}),\n },\n encoding: {\n file: { contentType: 'application/octet-stream' },\n _payload: { contentType: 'application/json' },\n },\n },\n 'application/json': { schema: bodyRef },\n },\n }\n}\n\nexport const SECURITY_SCHEME_NAME = 'PayloadToken'\nexport const INTERACTIVE_SCHEME_NAME = 'PayloadLogin'\n\nexport const interactiveSecurityScheme = ({\n tokenUrl,\n t,\n}: {\n tokenUrl: string\n t: Translate\n}): SecuritySchemeObject => ({\n type: 'oauth2',\n description: t('securityInteractive'),\n flows: { password: { tokenUrl, scopes: {} } },\n})\n\nexport const securityScheme = ({ cookiePrefix, t }: { cookiePrefix: string; t: Translate }): SecuritySchemeObject => ({\n type: 'http',\n scheme: 'bearer',\n bearerFormat: 'JWT',\n description: t('securityBearer', { cookiePrefix }),\n})\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"components.js","names":[],"sources":["../../../src/server/spec/components.ts"],"sourcesContent":["import type {\n OperationObject,\n ParameterObject,\n ReferenceObject,\n RequestBodyObject,\n ResponseObject,\n ResponsesObject,\n SchemaObject,\n SecurityRequirementObject,\n SecuritySchemeObject,\n} from '@scalar/openapi-types/3.2'\n\nimport type { Translate } from '@/shared/translations/types.js'\n\nimport { refTo } from './names.js'\n\ntype SchemaOrRef = SchemaObject | ReferenceObject\n\nexport const nullableType = (type: 'integer' | 'string' | 'number' | 'boolean'): SchemaObject => ({\n type: [type, 'null'],\n})\n\nexport const jsonResponse = (description: string, schema: SchemaOrRef): ResponseObject => ({\n description,\n content: { 'application/json': { schema } },\n})\n\nexport const jsonOk = (description: string, schema: SchemaOrRef): ResponsesObject => ({\n '200': jsonResponse(description, schema),\n})\n\nexport const jsonBody = (schema: SchemaOrRef): RequestBodyObject => ({\n required: true,\n content: { 'application/json': { schema } },\n})\n\nexport const messageResponse = (description: string): ResponsesObject =>\n jsonOk(description, { type: 'object', properties: { message: { type: 'string' } } })\n\nexport type ErrorCode = '400' | '401' | '403' | '404' | '500'\n\nconst allowed = (): SchemaObject => ({\n oneOf: [\n { type: 'boolean', enum: [true] },\n {\n type: 'object',\n properties: { permission: { type: 'boolean' }, where: { type: 'object', additionalProperties: true } },\n required: ['permission'],\n },\n ],\n})\n\nexport const docAccessOperation = ({\n tag,\n operationId,\n operations,\n bodyRef,\n errors,\n t,\n}: {\n tag: string\n operationId: string\n operations: string[]\n bodyRef: ReferenceObject\n errors: ErrorCode[]\n t: Translate\n}): OperationObject => ({\n tags: [tag],\n operationId,\n requestBody: { description: t('docAccessBody'), content: { 'application/json': { schema: bodyRef } } },\n responses: {\n ...jsonOk(t('docAccess'), {\n type: 'object',\n properties: {\n ...Object.fromEntries(operations.map((op) => [op, allowed()])),\n fields: {\n oneOf: [\n { type: 'boolean', enum: [true] },\n { type: 'object', additionalProperties: true },\n ],\n },\n },\n }),\n ...errorResponses(errors, t),\n },\n})\n\nexport const validateOperation = ({\n tag,\n operationId,\n parameters,\n requestBody,\n errors,\n security,\n t,\n}: {\n tag: string\n operationId: string\n parameters: ParameterObject[]\n requestBody: RequestBodyObject\n errors: ErrorCode[]\n security?: SecurityRequirementObject[]\n t: Translate\n}): OperationObject => ({\n tags: [tag],\n operationId,\n parameters,\n requestBody: {\n ...requestBody,\n description: [requestBody.description, t('validateBody')].filter(Boolean).join(' '),\n },\n responses: {\n ...jsonOk(t('validateResult'), {\n type: 'object',\n properties: {\n valid: { type: 'boolean' },\n errors: {\n type: 'array',\n items: {\n type: 'object',\n properties: {\n message: { type: 'string' },\n path: { type: 'string' },\n label: { oneOf: [{ type: 'string' }, { type: 'object', additionalProperties: { type: 'string' } }] },\n locale: { type: 'string' },\n tableName: { type: 'string' },\n },\n required: ['message', 'path'],\n },\n },\n },\n required: ['valid', 'errors'],\n }),\n ...errorResponses(errors, t),\n },\n security,\n})\n\nexport const ERROR_SCHEMA_NAME = 'ErrorResponse'\n\nconst ERROR_KEYS = {\n '400': 'error400',\n '401': 'error401',\n '403': 'error403',\n '404': 'error404',\n '500': 'error500',\n} as const\n\nexport const ERRORS = {\n list: ['400', '403', '500'] as ErrorCode[],\n read: ['403', '404', '500'] as ErrorCode[],\n create: ['400', '403', '500'] as ErrorCode[],\n update: ['400', '403', '404', '500'] as ErrorCode[],\n delete: ['403', '404', '500'] as ErrorCode[],\n globalRead: ['403', '500'] as ErrorCode[],\n globalUpdate: ['400', '403', '500'] as ErrorCode[],\n login: ['400', '401', '403', '500'] as ErrorCode[],\n session: ['401', '500'] as ErrorCode[],\n authAction: ['400', '403', '500'] as ErrorCode[],\n versionList: ['400', '403', '404', '500'] as ErrorCode[],\n versionRead: ['403', '404', '500'] as ErrorCode[],\n versionRestore: ['403', '404', '500'] as ErrorCode[],\n} as const\n\nexport const buildErrorResponseSchema = (): SchemaObject => ({\n type: 'object',\n properties: {\n errors: {\n type: 'array',\n items: {\n type: 'object',\n properties: {\n message: { type: 'string' },\n name: { type: 'string' },\n field: { type: 'string' },\n data: { type: 'object', additionalProperties: true },\n },\n required: ['message'],\n },\n },\n },\n required: ['errors'],\n})\n\nconst errorRef: ReferenceObject = { $ref: refTo(ERROR_SCHEMA_NAME) }\n\nconst errorContent = (description: string): ResponseObject => ({\n description,\n content: { 'application/json': { schema: errorRef } },\n})\n\nexport const errorResponses = (codes: ErrorCode[], t: Translate): ResponsesObject => {\n const responses: ResponsesObject = {}\n for (const code of codes) responses[code] = errorContent(t(ERROR_KEYS[code]))\n return responses\n}\n\nexport const buildListEnvelopeSchema = (base: string): SchemaObject => ({\n type: 'object',\n properties: {\n docs: { type: 'array', items: { $ref: refTo(base) } },\n totalDocs: { type: 'integer' },\n limit: { type: 'integer' },\n totalPages: { type: 'integer' },\n page: { type: 'integer' },\n pagingCounter: { type: 'integer' },\n hasPrevPage: { type: 'boolean' },\n hasNextPage: { type: 'boolean' },\n prevPage: nullableType('integer'),\n nextPage: nullableType('integer'),\n },\n required: ['docs', 'totalDocs', 'limit', 'totalPages', 'page'],\n})\n\nexport const uploadRequestBody = ({\n bodyRef,\n fileRequired,\n t,\n}: {\n bodyRef: ReferenceObject\n fileRequired: boolean\n t: Translate\n}): RequestBodyObject => {\n const fieldsSchemaName = bodyRef.$ref.split('/').pop() ?? 'the document'\n return {\n required: fileRequired,\n description: t('uploadBody'),\n content: {\n 'multipart/form-data': {\n schema: {\n type: 'object',\n properties: {\n file: {\n type: 'string',\n format: 'binary',\n description: t('uploadFile'),\n },\n _payload: {\n allOf: [bodyRef],\n description: t('uploadPayloadField', { schema: fieldsSchemaName }),\n },\n },\n ...(fileRequired ? { required: ['file'] } : {}),\n },\n encoding: {\n file: { contentType: 'application/octet-stream' },\n _payload: { contentType: 'application/json' },\n },\n },\n 'application/json': { schema: bodyRef },\n },\n }\n}\n\nexport const SECURITY_SCHEME_NAME = 'PayloadToken'\nexport const INTERACTIVE_SCHEME_NAME = 'PayloadLogin'\n\nexport const interactiveSecurityScheme = ({ tokenUrl, t }: { tokenUrl: string; t: Translate }): SecuritySchemeObject =>\n ({\n type: 'oauth2',\n description: t('securityInteractive'),\n flows: { password: { tokenUrl, scopes: {} } },\n }) as SecuritySchemeObject\n\nexport const securityScheme = ({ cookiePrefix, t }: { cookiePrefix: string; t: Translate }): SecuritySchemeObject => ({\n type: 'http',\n scheme: 'bearer',\n bearerFormat: 'JWT',\n description: t('securityBearer', { cookiePrefix }),\n})\n"],"mappings":"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"}
|
|
@@ -1,16 +1,15 @@
|
|
|
1
|
-
import { BuildContext, Entity } from "../../shared/types/index.js";
|
|
1
|
+
import { BuildContext, Entity, Schema } from "../../shared/types/index.js";
|
|
2
2
|
import { Block, SanitizedConfig } from "payload";
|
|
3
|
-
import { SchemaObject } from "@scalar/openapi-types/3.2";
|
|
4
3
|
export declare const resolveLocalizedStrings: (value: unknown, ctx: BuildContext) => unknown;
|
|
5
4
|
export interface EntitySchemas {
|
|
6
|
-
read:
|
|
7
|
-
create:
|
|
8
|
-
update:
|
|
5
|
+
read: Schema;
|
|
6
|
+
create: Schema;
|
|
7
|
+
update: Schema;
|
|
9
8
|
}
|
|
10
9
|
export declare const buildEntitySchemas: ({ entity, config, ctx }: {
|
|
11
10
|
entity: Entity;
|
|
12
11
|
config: SanitizedConfig;
|
|
13
12
|
ctx: BuildContext;
|
|
14
13
|
}) => Promise<EntitySchemas>;
|
|
15
|
-
export declare const buildBlockSchema: (block: Block, config: SanitizedConfig) =>
|
|
14
|
+
export declare const buildBlockSchema: (block: Block, config: SanitizedConfig) => Schema;
|
|
16
15
|
//# sourceMappingURL=entitySchemas.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"entitySchemas.js","names":[],"sources":["../../../src/server/spec/entitySchemas.ts"],"sourcesContent":["import { getTranslation } from '@payloadcms/translations'\nimport type { SchemaObject } from '@scalar/openapi-types/3.2'\nimport { entityToJSONSchema } from 'payload'\nimport type { Block, Field, SanitizedConfig, SchemaVariant } from 'payload'\n\nimport { makeT } from '@/shared/translations/index.js'\nimport type { BuildContext, Entity, IDType } from '@/shared/types/index.js'\nimport { deepMerge, isPlainObject } from '@/shared/utils.js'\n\nimport { AUTH_FIELDS, flattenFields } from './fields.js'\nimport { rewriteRefs } from './names.js'\n\nconst READ_HIDDEN_FIELDS = new Set<string>([...AUTH_FIELDS, 'collection'])\n\nconst stripInterfaceName = <T>(entity: T): T => {\n const { interfaceName: _omit, ...rest } = entity as T & { interfaceName?: string }\n return rest as T\n}\n\nconst toComponentSchema = ({\n config,\n source,\n idType,\n defs = new Map(),\n variant,\n}: {\n config: SanitizedConfig\n source: Entity | Block\n idType: IDType\n defs?: Map<string, unknown>\n variant?: SchemaVariant\n}): SchemaObject => {\n const jsonSchema = entityToJSONSchema(\n config,\n source as never,\n defs as never,\n idType,\n new Set(),\n undefined,\n undefined,\n undefined,\n variant,\n )\n const blockSlugs = new Set((config.blocks ?? []).map((b) => b.slug))\n const schema = rewriteRefs(jsonSchema as never, blockSlugs, defs) as unknown as SchemaObject\n delete schema.title\n return schema\n}\n\nconst fieldOpenapi = (field: Field): Record<string, unknown> | undefined => {\n const meta = (field as { custom?: { openapi?: unknown } }).custom?.openapi\n return isPlainObject(meta) ? meta : undefined\n}\n\nconst isLocaleKeyed = (value: unknown, ctx: BuildContext): value is Record<string, string> => {\n if (!isPlainObject(value)) return false\n const keys = Object.keys(value)\n return (\n keys.length > 0 &&\n keys.every((k) => ctx.locales.includes(k)) &&\n Object.values(value).every((v) => typeof v === 'string')\n )\n}\n\nconst LOCALIZABLE_KEYS = new Set(['description', 'title', 'summary'])\n\nconst resolveTextValue = (value: unknown, ctx: BuildContext): string | undefined => {\n if (typeof value === 'function') {\n try {\n const result = (value as (args: { t: unknown; i18n: unknown }) => unknown)({ t: ctx.i18n.t, i18n: ctx.i18n })\n return typeof result === 'string' && result.length > 0 ? result : undefined\n } catch {\n return undefined\n }\n }\n if (isLocaleKeyed(value, ctx)) return getTranslation(value, ctx.i18n)\n return undefined\n}\n\nexport const resolveLocalizedStrings = (value: unknown, ctx: BuildContext): unknown => {\n if (!isPlainObject(value)) return value\n const out: Record<string, unknown> = {}\n for (const [k, v] of Object.entries(value)) {\n if (LOCALIZABLE_KEYS.has(k)) {\n const resolved = resolveTextValue(v, ctx)\n if (resolved !== undefined) out[k] = resolved\n else if (typeof v !== 'function') out[k] = resolveLocalizedStrings(v, ctx)\n continue\n }\n out[k] = resolveLocalizedStrings(v, ctx)\n }\n return out\n}\n\nconst applyFieldOverrides = ({\n schema,\n fields,\n ctx,\n}: {\n schema: SchemaObject\n fields: Field[]\n ctx: BuildContext\n}): void => {\n if (!schema.properties) return\n for (const field of flattenFields(fields)) {\n const name = 'name' in field ? field.name : undefined\n const override = fieldOpenapi(field)\n if (!name || !override) continue\n const current = schema.properties[name]\n if (isPlainObject(current)) {\n const resolved = resolveLocalizedStrings(override, ctx) as Record<string, unknown>\n schema.properties[name] = deepMerge(current, resolved) as SchemaObject\n }\n }\n}\n\nconst isRef = (value: unknown): value is { $ref: string } => isPlainObject(value) && typeof value.$ref === 'string'\n\nconst isBlockBranch = (branch: unknown): boolean => {\n if (isRef(branch)) return branch.$ref.startsWith('#/components/schemas/Block')\n return isPlainObject(branch) && isPlainObject(branch.properties) && 'blockType' in branch.properties\n}\n\nconst addBlockDiscriminators = (node: unknown): void => {\n if (Array.isArray(node)) {\n for (const item of node) addBlockDiscriminators(item)\n return\n }\n if (!isPlainObject(node)) return\n const oneOf = node.oneOf\n if (Array.isArray(oneOf) && oneOf.length > 0 && oneOf.every(isBlockBranch)) {\n ;(node as SchemaObject).discriminator = { propertyName: 'blockType' }\n }\n for (const value of Object.values(node)) addBlockDiscriminators(value)\n}\n\nconst fixPolymorphicJoinRequired = (node: unknown): void => {\n if (Array.isArray(node)) {\n for (const item of node) fixPolymorphicJoinRequired(item)\n return\n }\n if (!isPlainObject(node)) return\n const { properties, required } = node as SchemaObject\n if (isPlainObject(properties) && 'relationTo' in properties && Array.isArray(required)) {\n ;(node as SchemaObject).required = required.map((r: string) => (r === 'collectionSlug' ? 'relationTo' : r))\n }\n for (const value of Object.values(node)) fixPolymorphicJoinRequired(value)\n}\n\nconst markLocalizedFields = ({\n schema,\n fields,\n ctx,\n}: {\n schema: SchemaObject\n fields: Field[]\n ctx: BuildContext\n}): void => {\n if (!schema.properties || ctx.locales.length === 0) return\n for (const field of flattenFields(fields)) {\n if (!('localized' in field) || !field.localized) continue\n const name = 'name' in field ? field.name : undefined\n if (!name || !(name in schema.properties)) continue\n const single = schema.properties[name] as SchemaObject\n schema.properties[name] = {\n oneOf: [\n single,\n {\n type: 'object',\n description: makeT(ctx.i18n)('schemaPerLocale'),\n properties: Object.fromEntries(ctx.locales.map((l) => [l, single])),\n },\n ],\n }\n }\n}\n\nconst removeProps = (schema: SchemaObject, names: Iterable<string>): void => {\n if (!schema.properties) return\n for (const name of names) delete schema.properties[name]\n if (Array.isArray(schema.required)) {\n const drop = new Set(names)\n schema.required = schema.required.filter((r: string) => !drop.has(r))\n }\n}\n\nconst isConstantDeny = (access: unknown): boolean => {\n if (typeof access !== 'function' || access.length > 0) return false\n try {\n return access() === false\n } catch {\n return false\n }\n}\n\nconst deniedWrites = (fields: Field[], operation: 'create' | 'update'): string[] =>\n flattenFields(fields)\n .filter((field) => 'name' in field && 'access' in field && isConstantDeny(field.access?.[operation]))\n .map((field) => (field as { name: string }).name)\n\nconst cloneSchema = (schema: SchemaObject): SchemaObject => ({\n ...schema,\n properties: { ...schema.properties },\n required: Array.isArray(schema.required) ? [...schema.required] : schema.required,\n})\n\nexport interface EntitySchemas {\n read: SchemaObject\n create: SchemaObject\n update: SchemaObject\n}\n\nexport const buildEntitySchemas = async ({\n entity,\n config,\n ctx,\n}: {\n entity: Entity\n config: SanitizedConfig\n ctx: BuildContext\n}): Promise<EntitySchemas> => {\n const source = stripInterfaceName(entity)\n const defs = new Map<string, unknown>()\n const idType = ctx.defaultIDType\n const read = toComponentSchema({ config, source, idType, defs })\n const write = toComponentSchema({ config, source, idType, defs, variant: 'input' })\n const isAuth = 'auth' in entity && Boolean(entity.auth)\n\n for (const schema of [read, write]) {\n if (isAuth) removeProps(schema, READ_HIDDEN_FIELDS)\n applyFieldOverrides({ schema, fields: entity.fields, ctx })\n addBlockDiscriminators(schema)\n }\n fixPolymorphicJoinRequired(read)\n\n const create = cloneSchema(write)\n if (isAuth && create.properties) {\n create.properties.password = { type: 'string' }\n create.required = [...new Set([...(create.required ?? []), 'password'])]\n }\n\n removeProps(create, deniedWrites(entity.fields, 'create'))\n\n const update = cloneSchema(write)\n removeProps(update, deniedWrites(entity.fields, 'update'))\n delete update.required\n if (update.properties) delete update.properties.id\n if (isAuth && update.properties) {\n update.properties.password = { type: 'string' }\n }\n\n markLocalizedFields({ schema: read, fields: entity.fields, ctx })\n\n return { read, create, update }\n}\n\nexport const buildBlockSchema = (block: Block, config: SanitizedConfig): SchemaObject => {\n const schema = toComponentSchema({ config, source: stripInterfaceName(block), idType: 'text' })\n const blockType: SchemaObject = { type: 'string', enum: [block.slug] }\n const properties = { ...schema.properties, blockType }\n const required = [...new Set(['blockType', ...(schema.required ?? [])])]\n return { ...schema, type: 'object', properties, required }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"entitySchemas.js","names":[],"sources":["../../../src/server/spec/entitySchemas.ts"],"sourcesContent":["import { getTranslation } from '@payloadcms/translations'\nimport { entityToJSONSchema } from 'payload'\nimport type { Block, Field, SanitizedConfig, SchemaVariant } from 'payload'\n\nimport { makeT } from '@/shared/translations/index.js'\nimport type { BuildContext, Entity, IDType, Schema } from '@/shared/types/index.js'\nimport { deepMerge, isPlainObject } from '@/shared/utils.js'\n\nimport { AUTH_FIELDS, flattenFields } from './fields.js'\nimport { rewriteRefs } from './names.js'\n\nconst READ_HIDDEN_FIELDS = new Set<string>([...AUTH_FIELDS, 'collection'])\n\nconst stripInterfaceName = <T>(entity: T): T => {\n const { interfaceName: _omit, ...rest } = entity as T & { interfaceName?: string }\n return rest as T\n}\n\nconst toComponentSchema = ({\n config,\n source,\n idType,\n defs = new Map(),\n variant,\n}: {\n config: SanitizedConfig\n source: Entity | Block\n idType: IDType\n defs?: Map<string, unknown>\n variant?: SchemaVariant\n}): Schema => {\n const jsonSchema = entityToJSONSchema(\n config,\n source as never,\n defs as never,\n idType,\n new Set(),\n undefined,\n undefined,\n undefined,\n variant,\n )\n const blockSlugs = new Set((config.blocks ?? []).map((b) => b.slug))\n const schema = rewriteRefs(jsonSchema as never, blockSlugs, defs) as unknown as Schema\n delete schema.title\n return schema\n}\n\nconst fieldOpenapi = (field: Field): Record<string, unknown> | undefined => {\n const meta = (field as { custom?: { openapi?: unknown } }).custom?.openapi\n return isPlainObject(meta) ? meta : undefined\n}\n\nconst isLocaleKeyed = (value: unknown, ctx: BuildContext): value is Record<string, string> => {\n if (!isPlainObject(value)) return false\n const keys = Object.keys(value)\n return (\n keys.length > 0 &&\n keys.every((k) => ctx.locales.includes(k)) &&\n Object.values(value).every((v) => typeof v === 'string')\n )\n}\n\nconst LOCALIZABLE_KEYS = new Set(['description', 'title', 'summary'])\n\nconst resolveTextValue = (value: unknown, ctx: BuildContext): string | undefined => {\n if (typeof value === 'function') {\n try {\n const result = (value as (args: { t: unknown; i18n: unknown }) => unknown)({ t: ctx.i18n.t, i18n: ctx.i18n })\n return typeof result === 'string' && result.length > 0 ? result : undefined\n } catch {\n return undefined\n }\n }\n if (isLocaleKeyed(value, ctx)) return getTranslation(value, ctx.i18n)\n return undefined\n}\n\nexport const resolveLocalizedStrings = (value: unknown, ctx: BuildContext): unknown => {\n if (!isPlainObject(value)) return value\n const out: Record<string, unknown> = {}\n for (const [k, v] of Object.entries(value)) {\n if (LOCALIZABLE_KEYS.has(k)) {\n const resolved = resolveTextValue(v, ctx)\n if (resolved !== undefined) out[k] = resolved\n else if (typeof v !== 'function') out[k] = resolveLocalizedStrings(v, ctx)\n continue\n }\n out[k] = resolveLocalizedStrings(v, ctx)\n }\n return out\n}\n\nconst applyFieldOverrides = ({ schema, fields, ctx }: { schema: Schema; fields: Field[]; ctx: BuildContext }): void => {\n if (!schema.properties) return\n for (const field of flattenFields(fields)) {\n const name = 'name' in field ? field.name : undefined\n const override = fieldOpenapi(field)\n if (!name || !override) continue\n const current = schema.properties[name]\n if (isPlainObject(current)) {\n const resolved = resolveLocalizedStrings(override, ctx) as Record<string, unknown>\n schema.properties[name] = deepMerge(current, resolved) as Schema\n }\n }\n}\n\nconst isRef = (value: unknown): value is { $ref: string } => isPlainObject(value) && typeof value.$ref === 'string'\n\nconst isBlockBranch = (branch: unknown): boolean => {\n if (isRef(branch)) return branch.$ref.startsWith('#/components/schemas/Block')\n return isPlainObject(branch) && isPlainObject(branch.properties) && 'blockType' in branch.properties\n}\n\nconst addBlockDiscriminators = (node: unknown): void => {\n if (Array.isArray(node)) {\n for (const item of node) addBlockDiscriminators(item)\n return\n }\n if (!isPlainObject(node)) return\n const oneOf = node.oneOf\n if (Array.isArray(oneOf) && oneOf.length > 0 && oneOf.every(isBlockBranch)) {\n ;(node as Schema).discriminator = { propertyName: 'blockType' }\n }\n for (const value of Object.values(node)) addBlockDiscriminators(value)\n}\n\nconst fixPolymorphicJoinRequired = (node: unknown): void => {\n if (Array.isArray(node)) {\n for (const item of node) fixPolymorphicJoinRequired(item)\n return\n }\n if (!isPlainObject(node)) return\n const { properties, required } = node as Schema\n if (isPlainObject(properties) && 'relationTo' in properties && Array.isArray(required)) {\n ;(node as Schema).required = required.map((r: string) => (r === 'collectionSlug' ? 'relationTo' : r))\n }\n for (const value of Object.values(node)) fixPolymorphicJoinRequired(value)\n}\n\nconst markLocalizedFields = ({ schema, fields, ctx }: { schema: Schema; fields: Field[]; ctx: BuildContext }): void => {\n if (!schema.properties || ctx.locales.length === 0) return\n for (const field of flattenFields(fields)) {\n if (!('localized' in field) || !field.localized) continue\n const name = 'name' in field ? field.name : undefined\n if (!name || !(name in schema.properties)) continue\n const single = schema.properties[name] as Schema\n schema.properties[name] = {\n oneOf: [\n single,\n {\n type: 'object',\n description: makeT(ctx.i18n)('schemaPerLocale'),\n properties: Object.fromEntries(ctx.locales.map((l) => [l, single])),\n },\n ],\n }\n }\n}\n\nconst removeProps = (schema: Schema, names: Iterable<string>): void => {\n if (!schema.properties) return\n for (const name of names) delete schema.properties[name]\n if (Array.isArray(schema.required)) {\n const drop = new Set(names)\n schema.required = schema.required.filter((r: string) => !drop.has(r))\n }\n}\n\nconst isConstantDeny = (access: unknown): boolean => {\n if (typeof access !== 'function' || access.length > 0) return false\n try {\n return access() === false\n } catch {\n return false\n }\n}\n\nconst deniedWrites = (fields: Field[], operation: 'create' | 'update'): string[] =>\n flattenFields(fields)\n .filter((field) => 'name' in field && 'access' in field && isConstantDeny(field.access?.[operation]))\n .map((field) => (field as { name: string }).name)\n\nconst cloneSchema = (schema: Schema): Schema => ({\n ...schema,\n properties: { ...schema.properties },\n required: Array.isArray(schema.required) ? [...schema.required] : schema.required,\n})\n\nexport interface EntitySchemas {\n read: Schema\n create: Schema\n update: Schema\n}\n\nexport const buildEntitySchemas = async ({\n entity,\n config,\n ctx,\n}: {\n entity: Entity\n config: SanitizedConfig\n ctx: BuildContext\n}): Promise<EntitySchemas> => {\n const source = stripInterfaceName(entity)\n const defs = new Map<string, unknown>()\n const idType = ctx.defaultIDType\n const read = toComponentSchema({ config, source, idType, defs })\n const write = toComponentSchema({ config, source, idType, defs, variant: 'input' })\n const isAuth = 'auth' in entity && Boolean(entity.auth)\n\n for (const schema of [read, write]) {\n if (isAuth) removeProps(schema, READ_HIDDEN_FIELDS)\n applyFieldOverrides({ schema, fields: entity.fields, ctx })\n addBlockDiscriminators(schema)\n }\n fixPolymorphicJoinRequired(read)\n\n const create = cloneSchema(write)\n if (isAuth && create.properties) {\n create.properties.password = { type: 'string' }\n create.required = [...new Set([...(create.required ?? []), 'password'])]\n }\n\n removeProps(create, deniedWrites(entity.fields, 'create'))\n\n const update = cloneSchema(write)\n removeProps(update, deniedWrites(entity.fields, 'update'))\n delete update.required\n if (update.properties) delete update.properties.id\n if (isAuth && update.properties) {\n update.properties.password = { type: 'string' }\n }\n\n markLocalizedFields({ schema: read, fields: entity.fields, ctx })\n\n return { read, create, update }\n}\n\nexport const buildBlockSchema = (block: Block, config: SanitizedConfig): Schema => {\n const schema = toComponentSchema({ config, source: stripInterfaceName(block), idType: 'text' })\n const blockType: Schema = { type: 'string', enum: [block.slug] }\n const properties = { ...schema.properties, blockType }\n const required = [...new Set(['blockType', ...(schema.required ?? [])])]\n return { ...schema, type: 'object', properties, required }\n}\n"],"mappings":"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"}
|
|
@@ -3,10 +3,14 @@ import { SanitizedCollectionConfig, SanitizedGlobalConfig } from "payload";
|
|
|
3
3
|
import { PathsObject } from "@scalar/openapi-types/3.2";
|
|
4
4
|
export declare const shouldIncludeCollection: (collection: SanitizedCollectionConfig, filters: ResolvedFilters) => boolean;
|
|
5
5
|
export declare const shouldIncludeGlobal: (global: SanitizedGlobalConfig, filters: ResolvedFilters) => boolean;
|
|
6
|
-
export declare const
|
|
6
|
+
export declare const PLUGIN_KEY = "x-payload-plugin";
|
|
7
|
+
export declare const operationPlugin: (operation: unknown, plugin: string | undefined) => string | undefined;
|
|
8
|
+
export declare const stripPluginKeys: (paths: PathsObject) => PathsObject;
|
|
9
|
+
export declare const filterOperations: ({ paths, slug, kind, plugin, filters }: {
|
|
7
10
|
paths: PathsObject;
|
|
8
11
|
slug?: string;
|
|
9
12
|
kind: OperationKind;
|
|
13
|
+
plugin?: string;
|
|
10
14
|
filters: ResolvedFilters;
|
|
11
15
|
}) => Promise<PathsObject>;
|
|
12
16
|
//# sourceMappingURL=filters.d.ts.map
|
|
@@ -56,17 +56,26 @@ const ruleMatchesMethod = (rule, method) => {
|
|
|
56
56
|
if (rule.method === void 0) return true;
|
|
57
57
|
return Array.isArray(rule.method) ? rule.method.includes(method) : rule.method === method;
|
|
58
58
|
};
|
|
59
|
-
const
|
|
60
|
-
if (
|
|
61
|
-
if (
|
|
62
|
-
return
|
|
59
|
+
const matchesOptional = (matcher, value) => {
|
|
60
|
+
if (matcher === void 0) return true;
|
|
61
|
+
if (value === void 0) return false;
|
|
62
|
+
return matcher instanceof RegExp ? matcher.test(value) : matcher === value;
|
|
63
|
+
};
|
|
64
|
+
const ruleMatches = (rule, { method, slug, kind, path, plugin }) => ruleMatchesMethod(rule, method) && matchesOptional(rule.slug, slug) && matchesOptional(rule.plugin, plugin) && (rule.kind === void 0 || rule.kind === kind) && (rule.path === void 0 || rule.path.test(path));
|
|
65
|
+
const PLUGIN_KEY = "x-payload-plugin";
|
|
66
|
+
const operationPlugin = (operation, plugin) => {
|
|
67
|
+
const marked = operation?.[PLUGIN_KEY];
|
|
68
|
+
return plugin ?? (typeof marked === "string" ? marked : void 0);
|
|
69
|
+
};
|
|
70
|
+
const stripPluginKeys = (paths) => {
|
|
71
|
+
for (const item of Object.values(paths)) for (const method of HTTP_METHODS) delete item[method]?.[PLUGIN_KEY];
|
|
72
|
+
return paths;
|
|
63
73
|
};
|
|
64
|
-
const ruleMatches = (rule, { method, slug, kind, path }) => ruleMatchesMethod(rule, method) && ruleMatchesSlug(rule, slug) && (rule.kind === void 0 || rule.kind === kind) && (rule.path === void 0 || rule.path.test(path));
|
|
65
74
|
const isOperationExcluded = async (filters, op) => {
|
|
66
75
|
for (const rule of filters.excludeOperations) if (typeof rule === "function" ? await rule(op) === true : ruleMatches(rule, op)) return true;
|
|
67
76
|
return false;
|
|
68
77
|
};
|
|
69
|
-
const filterOperations = async ({ paths, slug, kind, filters }) => {
|
|
78
|
+
const filterOperations = async ({ paths, slug, kind, plugin, filters }) => {
|
|
70
79
|
if (filters.excludeOperations.length === 0) return paths;
|
|
71
80
|
for (const [path, item] of Object.entries(paths)) {
|
|
72
81
|
if (!item) continue;
|
|
@@ -74,18 +83,20 @@ const filterOperations = async ({ paths, slug, kind, filters }) => {
|
|
|
74
83
|
const ops = item;
|
|
75
84
|
for (const method of HTTP_METHODS) {
|
|
76
85
|
if (!ops[method]) continue;
|
|
77
|
-
|
|
86
|
+
const op = {
|
|
78
87
|
method,
|
|
79
88
|
path,
|
|
80
89
|
slug,
|
|
81
|
-
kind
|
|
82
|
-
|
|
90
|
+
kind,
|
|
91
|
+
plugin: operationPlugin(ops[method], plugin)
|
|
92
|
+
};
|
|
93
|
+
if (await isOperationExcluded(filters, op)) delete ops[method];
|
|
83
94
|
else remaining += 1;
|
|
84
95
|
}
|
|
85
96
|
if (remaining === 0) delete paths[path];
|
|
86
97
|
}
|
|
87
98
|
return paths;
|
|
88
99
|
};
|
|
89
|
-
export { filterOperations, shouldIncludeCollection, shouldIncludeGlobal };
|
|
100
|
+
export { PLUGIN_KEY, filterOperations, operationPlugin, shouldIncludeCollection, shouldIncludeGlobal, stripPluginKeys };
|
|
90
101
|
|
|
91
102
|
//# sourceMappingURL=filters.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"filters.js","names":[],"sources":["../../../src/server/spec/filters.ts"],"sourcesContent":["import type { PathsObject } from '@scalar/openapi-types/3.2'\nimport type { SanitizedCollectionConfig, SanitizedGlobalConfig } from 'payload'\n\nimport type {\n EntityKind,\n EntityMatcher,\n HttpMethod,\n OperationContext,\n OperationKind,\n OperationRule,\n ResolvedFilters,\n} from '@/shared/types/index.js'\n\nconst SYSTEM_COLLECTIONS = new Set([\n 'payload-jobs',\n 'payload-locked-documents',\n 'payload-preferences',\n 'payload-migrations',\n 'payload-kv',\n 'payload-llm-instructions',\n 'payload-query-presets',\n])\n\nconst SYSTEM_GLOBALS = new Set(['payload-jobs-stats'])\n\nconst HTTP_METHODS: HttpMethod[] = ['get', 'post', 'patch', 'put', 'delete']\n\ninterface EntityRef {\n slug: string\n kind: EntityKind\n}\n\nconst matchesEntity = (matcher: EntityMatcher, { slug, kind }: EntityRef): boolean => {\n if (typeof matcher === 'string') return matcher === slug\n if (matcher instanceof RegExp) return matcher.test(slug)\n return matcher.kind === kind && matcher.slug === slug\n}\n\nconst anyMatch = (matchers: EntityMatcher[], entity: EntityRef): boolean =>\n matchers.some((m) => matchesEntity(m, entity))\n\nconst isHiddenCollection = (collection: SanitizedCollectionConfig): boolean => {\n if ((collection as { hidden?: unknown }).hidden === true) return true\n const hidden = collection.admin?.hidden\n if (typeof hidden === 'function') {\n try {\n return hidden({ user: null } as never) === true\n } catch {\n return false\n }\n }\n return hidden === true\n}\n\nconst passesIncludeExclude = (entity: EntityRef, filters: ResolvedFilters): boolean => {\n if (filters.include.length > 0 && !anyMatch(filters.include, entity)) return false\n if (anyMatch(filters.exclude, entity)) return false\n return true\n}\n\nexport const shouldIncludeCollection = (collection: SanitizedCollectionConfig, filters: ResolvedFilters): boolean => {\n const { slug } = collection\n if (SYSTEM_COLLECTIONS.has(slug) && !filters.includeSystem) return false\n if (!filters.includeHidden && isHiddenCollection(collection)) return false\n return passesIncludeExclude({ slug, kind: 'collection' }, filters)\n}\n\nexport const shouldIncludeGlobal = (global: SanitizedGlobalConfig, filters: ResolvedFilters): boolean => {\n if (SYSTEM_GLOBALS.has(global.slug) && !filters.includeSystem) return false\n return passesIncludeExclude({ slug: global.slug, kind: 'global' }, filters)\n}\n\nconst ruleMatchesMethod = (rule: OperationRule, method: HttpMethod): boolean => {\n if (rule.method === undefined) return true\n return Array.isArray(rule.method) ? rule.method.includes(method) : rule.method === method\n}\n\nconst
|
|
1
|
+
{"version":3,"file":"filters.js","names":[],"sources":["../../../src/server/spec/filters.ts"],"sourcesContent":["import type { PathsObject } from '@scalar/openapi-types/3.2'\nimport type { SanitizedCollectionConfig, SanitizedGlobalConfig } from 'payload'\n\nimport type {\n EntityKind,\n EntityMatcher,\n HttpMethod,\n OperationContext,\n OperationKind,\n OperationRule,\n ResolvedFilters,\n} from '@/shared/types/index.js'\n\nconst SYSTEM_COLLECTIONS = new Set([\n 'payload-jobs',\n 'payload-locked-documents',\n 'payload-preferences',\n 'payload-migrations',\n 'payload-kv',\n 'payload-llm-instructions',\n 'payload-query-presets',\n])\n\nconst SYSTEM_GLOBALS = new Set(['payload-jobs-stats'])\n\nconst HTTP_METHODS: HttpMethod[] = ['get', 'post', 'patch', 'put', 'delete']\n\ninterface EntityRef {\n slug: string\n kind: EntityKind\n}\n\nconst matchesEntity = (matcher: EntityMatcher, { slug, kind }: EntityRef): boolean => {\n if (typeof matcher === 'string') return matcher === slug\n if (matcher instanceof RegExp) return matcher.test(slug)\n return matcher.kind === kind && matcher.slug === slug\n}\n\nconst anyMatch = (matchers: EntityMatcher[], entity: EntityRef): boolean =>\n matchers.some((m) => matchesEntity(m, entity))\n\nconst isHiddenCollection = (collection: SanitizedCollectionConfig): boolean => {\n if ((collection as { hidden?: unknown }).hidden === true) return true\n const hidden = collection.admin?.hidden\n if (typeof hidden === 'function') {\n try {\n return hidden({ user: null } as never) === true\n } catch {\n return false\n }\n }\n return hidden === true\n}\n\nconst passesIncludeExclude = (entity: EntityRef, filters: ResolvedFilters): boolean => {\n if (filters.include.length > 0 && !anyMatch(filters.include, entity)) return false\n if (anyMatch(filters.exclude, entity)) return false\n return true\n}\n\nexport const shouldIncludeCollection = (collection: SanitizedCollectionConfig, filters: ResolvedFilters): boolean => {\n const { slug } = collection\n if (SYSTEM_COLLECTIONS.has(slug) && !filters.includeSystem) return false\n if (!filters.includeHidden && isHiddenCollection(collection)) return false\n return passesIncludeExclude({ slug, kind: 'collection' }, filters)\n}\n\nexport const shouldIncludeGlobal = (global: SanitizedGlobalConfig, filters: ResolvedFilters): boolean => {\n if (SYSTEM_GLOBALS.has(global.slug) && !filters.includeSystem) return false\n return passesIncludeExclude({ slug: global.slug, kind: 'global' }, filters)\n}\n\nconst ruleMatchesMethod = (rule: OperationRule, method: HttpMethod): boolean => {\n if (rule.method === undefined) return true\n return Array.isArray(rule.method) ? rule.method.includes(method) : rule.method === method\n}\n\nconst matchesOptional = (matcher: string | RegExp | undefined, value: string | undefined): boolean => {\n if (matcher === undefined) return true\n if (value === undefined) return false\n return matcher instanceof RegExp ? matcher.test(value) : matcher === value\n}\n\nconst ruleMatches = (rule: OperationRule, { method, slug, kind, path, plugin }: OperationContext): boolean =>\n ruleMatchesMethod(rule, method) &&\n matchesOptional(rule.slug, slug) &&\n matchesOptional(rule.plugin, plugin) &&\n (rule.kind === undefined || rule.kind === kind) &&\n (rule.path === undefined || rule.path.test(path))\n\nexport const PLUGIN_KEY = 'x-payload-plugin'\n\nexport const operationPlugin = (operation: unknown, plugin: string | undefined): string | undefined => {\n const marked = (operation as Record<string, unknown> | undefined)?.[PLUGIN_KEY]\n return plugin ?? (typeof marked === 'string' ? marked : undefined)\n}\n\nexport const stripPluginKeys = (paths: PathsObject): PathsObject => {\n for (const item of Object.values(paths)) {\n for (const method of HTTP_METHODS)\n delete (item as Record<HttpMethod, Record<string, unknown> | undefined>)[method]?.[PLUGIN_KEY]\n }\n return paths\n}\n\nconst isOperationExcluded = async (filters: ResolvedFilters, op: OperationContext): Promise<boolean> => {\n for (const rule of filters.excludeOperations) {\n if (typeof rule === 'function' ? (await rule(op)) === true : ruleMatches(rule, op)) return true\n }\n return false\n}\n\nexport const filterOperations = async ({\n paths,\n slug,\n kind,\n plugin,\n filters,\n}: {\n paths: PathsObject\n slug?: string\n kind: OperationKind\n plugin?: string\n filters: ResolvedFilters\n}): Promise<PathsObject> => {\n if (filters.excludeOperations.length === 0) return paths\n\n for (const [path, item] of Object.entries(paths)) {\n if (!item) continue\n let remaining = 0\n const ops = item as Record<HttpMethod, unknown>\n for (const method of HTTP_METHODS) {\n if (!ops[method]) continue\n const op = { method, path, slug, kind, plugin: operationPlugin(ops[method], plugin) }\n if (await isOperationExcluded(filters, op)) {\n delete ops[method]\n } else {\n remaining += 1\n }\n }\n if (remaining === 0) delete paths[path]\n }\n return paths\n}\n"],"mappings":"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"}
|
|
@@ -8,10 +8,15 @@ const applyHierarchy = ({ collection, base, paths, schemas, ctx }) => {
|
|
|
8
8
|
[hierarchy.slugPathFieldName]: t("schemaHierarchySlugPath"),
|
|
9
9
|
[hierarchy.titlePathFieldName]: t("schemaHierarchyTitlePath")
|
|
10
10
|
};
|
|
11
|
-
const
|
|
11
|
+
const propertiesOf = (name) => {
|
|
12
|
+
const schema = schemas[name];
|
|
13
|
+
return typeof schema === "object" ? schema.properties : void 0;
|
|
14
|
+
};
|
|
15
|
+
const read = propertiesOf(base);
|
|
12
16
|
for (const [name, description] of Object.entries(pathFields)) {
|
|
13
|
-
|
|
14
|
-
|
|
17
|
+
const current = read?.[name];
|
|
18
|
+
if (read && typeof current === "object") read[name] = {
|
|
19
|
+
...current,
|
|
15
20
|
readOnly: true,
|
|
16
21
|
description
|
|
17
22
|
};
|
|
@@ -19,7 +24,7 @@ const applyHierarchy = ({ collection, base, paths, schemas, ctx }) => {
|
|
|
19
24
|
createSchemaName(base),
|
|
20
25
|
updateSchemaName(base),
|
|
21
26
|
querySchemaName(base)
|
|
22
|
-
]) delete
|
|
27
|
+
]) delete propertiesOf(other)?.[name];
|
|
23
28
|
}
|
|
24
29
|
const param = {
|
|
25
30
|
name: "computeHierarchyPaths",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hierarchy.js","names":[],"sources":["../../../src/server/spec/hierarchy.ts"],"sourcesContent":["import type { OperationObject, ParameterObject, PathsObject, SchemaObject } from '@scalar/openapi-types/3.2'\nimport type { SanitizedCollectionConfig } from 'payload'\n\nimport { createSchemaName, querySchemaName, updateSchemaName } from '@/server/spec/names.js'\nimport { makeT } from '@/shared/translations/index.js'\nimport type { BuildContext } from '@/shared/types/index.js'\n\nexport const applyHierarchy = ({\n collection,\n base,\n paths,\n schemas,\n ctx,\n}: {\n collection: SanitizedCollectionConfig\n base: string\n paths: PathsObject\n schemas: Record<string, SchemaObject>\n ctx: BuildContext\n}): void => {\n const { hierarchy } = collection\n if (!hierarchy) return\n const t = makeT(ctx.i18n)\n const pathFields = {\n [hierarchy.slugPathFieldName]: t('schemaHierarchySlugPath'),\n [hierarchy.titlePathFieldName]: t('schemaHierarchyTitlePath'),\n }\n\n const
|
|
1
|
+
{"version":3,"file":"hierarchy.js","names":[],"sources":["../../../src/server/spec/hierarchy.ts"],"sourcesContent":["import type { OperationObject, ParameterObject, PathsObject, SchemaObject } from '@scalar/openapi-types/3.2'\nimport type { SanitizedCollectionConfig } from 'payload'\n\nimport { createSchemaName, querySchemaName, updateSchemaName } from '@/server/spec/names.js'\nimport { makeT } from '@/shared/translations/index.js'\nimport type { BuildContext } from '@/shared/types/index.js'\n\nexport const applyHierarchy = ({\n collection,\n base,\n paths,\n schemas,\n ctx,\n}: {\n collection: SanitizedCollectionConfig\n base: string\n paths: PathsObject\n schemas: Record<string, SchemaObject>\n ctx: BuildContext\n}): void => {\n const { hierarchy } = collection\n if (!hierarchy) return\n const t = makeT(ctx.i18n)\n const pathFields = {\n [hierarchy.slugPathFieldName]: t('schemaHierarchySlugPath'),\n [hierarchy.titlePathFieldName]: t('schemaHierarchyTitlePath'),\n }\n\n const propertiesOf = (name: string) => {\n const schema = schemas[name]\n return typeof schema === 'object' ? schema.properties : undefined\n }\n const read = propertiesOf(base)\n for (const [name, description] of Object.entries(pathFields)) {\n const current = read?.[name]\n if (read && typeof current === 'object') read[name] = { ...current, readOnly: true, description }\n for (const other of [createSchemaName(base), updateSchemaName(base), querySchemaName(base)]) {\n delete propertiesOf(other)?.[name]\n }\n }\n\n const param: ParameterObject = {\n name: 'computeHierarchyPaths',\n in: 'query',\n description: t('paramComputeHierarchyPaths', {\n slugPath: hierarchy.slugPathFieldName,\n titlePath: hierarchy.titlePathFieldName,\n }),\n schema: { type: 'boolean' },\n }\n const route = `${ctx.apiRoute}/${collection.slug}`\n for (const op of [paths[route]?.get, paths[`${route}/{id}`]?.get] as (OperationObject | undefined)[]) {\n op?.parameters?.push(param)\n }\n}\n"],"mappings":";;AAOA,MAAa,kBAAkB,EAC7B,YACA,MACA,OACA,SACA,UAOU;CACV,MAAM,EAAE,cAAc;CACtB,IAAI,CAAC,WAAW;CAChB,MAAM,IAAI,MAAM,IAAI,IAAI;CACxB,MAAM,aAAa;GAChB,UAAU,oBAAoB,EAAE,yBAAyB;GACzD,UAAU,qBAAqB,EAAE,0BAA0B;CAC9D;CAEA,MAAM,gBAAgB,SAAiB;EACrC,MAAM,SAAS,QAAQ;EACvB,OAAO,OAAO,WAAW,WAAW,OAAO,aAAa,KAAA;CAC1D;CACA,MAAM,OAAO,aAAa,IAAI;CAC9B,KAAK,MAAM,CAAC,MAAM,gBAAgB,OAAO,QAAQ,UAAU,GAAG;EAC5D,MAAM,UAAU,OAAO;EACvB,IAAI,QAAQ,OAAO,YAAY,UAAU,KAAK,QAAQ;GAAE,GAAG;GAAS,UAAU;GAAM;EAAY;EAChG,KAAK,MAAM,SAAS;GAAC,iBAAiB,IAAI;GAAG,iBAAiB,IAAI;GAAG,gBAAgB,IAAI;EAAC,GACxF,OAAO,aAAa,KAAK,CAAC,GAAG;CAEjC;CAEA,MAAM,QAAyB;EAC7B,MAAM;EACN,IAAI;EACJ,aAAa,EAAE,8BAA8B;GAC3C,UAAU,UAAU;GACpB,WAAW,UAAU;EACvB,CAAC;EACD,QAAQ,EAAE,MAAM,UAAU;CAC5B;CACA,MAAM,QAAQ,GAAG,IAAI,SAAS,GAAG,WAAW;CAC5C,KAAK,MAAM,MAAM,CAAC,MAAM,MAAM,EAAE,KAAK,MAAM,GAAG,MAAM,OAAO,EAAE,GAAG,GAC9D,IAAI,YAAY,KAAK,KAAK;AAE9B"}
|