@seshuk/payload-plugin-openapi 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +595 -0
- package/dist/bin/generateSpec.d.ts +25 -0
- package/dist/bin/generateSpec.js +61 -0
- package/dist/constants.d.ts +1 -0
- package/dist/constants.js +2 -0
- package/dist/endpoints/interactiveAuth.d.ts +4 -0
- package/dist/endpoints/interactiveAuth.js +27 -0
- package/dist/endpoints/spec.d.ts +5 -0
- package/dist/endpoints/spec.js +39 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +67 -0
- package/dist/options.d.ts +2 -0
- package/dist/options.js +44 -0
- package/dist/spec/build.d.ts +14 -0
- package/dist/spec/build.js +36 -0
- package/dist/spec/buildDocument.d.ts +15 -0
- package/dist/spec/buildDocument.js +234 -0
- package/dist/spec/components.d.ts +46 -0
- package/dist/spec/components.js +280 -0
- package/dist/spec/downconvert.d.ts +3 -0
- package/dist/spec/downconvert.js +55 -0
- package/dist/spec/entitySchemas.d.ts +15 -0
- package/dist/spec/entitySchemas.js +246 -0
- package/dist/spec/fields.d.ts +5 -0
- package/dist/spec/fields.js +44 -0
- package/dist/spec/filters.d.ts +11 -0
- package/dist/spec/filters.js +74 -0
- package/dist/spec/names.d.ts +14 -0
- package/dist/spec/names.js +37 -0
- package/dist/spec/params.d.ts +47 -0
- package/dist/spec/params.js +344 -0
- package/dist/spec/paths/auth.d.ts +9 -0
- package/dist/spec/paths/auth.js +285 -0
- package/dist/spec/paths/collections.d.ts +7 -0
- package/dist/spec/paths/collections.js +261 -0
- package/dist/spec/paths/custom.d.ts +9 -0
- package/dist/spec/paths/custom.js +41 -0
- package/dist/spec/paths/globals.d.ts +7 -0
- package/dist/spec/paths/globals.js +68 -0
- package/dist/spec/paths/jobs.d.ts +7 -0
- package/dist/spec/paths/jobs.js +130 -0
- package/dist/spec/paths/versions.d.ts +18 -0
- package/dist/spec/paths/versions.js +243 -0
- package/dist/spec/tags.d.ts +27 -0
- package/dist/spec/tags.js +79 -0
- package/dist/translations/index.d.ts +6 -0
- package/dist/translations/index.js +96 -0
- package/dist/translations/locales/ar.d.ts +2 -0
- package/dist/translations/locales/ar.js +76 -0
- package/dist/translations/locales/az.d.ts +2 -0
- package/dist/translations/locales/az.js +76 -0
- package/dist/translations/locales/bg.d.ts +2 -0
- package/dist/translations/locales/bg.js +76 -0
- package/dist/translations/locales/bnBd.d.ts +2 -0
- package/dist/translations/locales/bnBd.js +76 -0
- package/dist/translations/locales/bnIn.d.ts +2 -0
- package/dist/translations/locales/bnIn.js +76 -0
- package/dist/translations/locales/ca.d.ts +2 -0
- package/dist/translations/locales/ca.js +76 -0
- package/dist/translations/locales/cs.d.ts +2 -0
- package/dist/translations/locales/cs.js +76 -0
- package/dist/translations/locales/da.d.ts +2 -0
- package/dist/translations/locales/da.js +76 -0
- package/dist/translations/locales/de.d.ts +2 -0
- package/dist/translations/locales/de.js +76 -0
- package/dist/translations/locales/en.d.ts +2 -0
- package/dist/translations/locales/en.js +76 -0
- package/dist/translations/locales/es.d.ts +2 -0
- package/dist/translations/locales/es.js +76 -0
- package/dist/translations/locales/et.d.ts +2 -0
- package/dist/translations/locales/et.js +76 -0
- package/dist/translations/locales/fa.d.ts +2 -0
- package/dist/translations/locales/fa.js +76 -0
- package/dist/translations/locales/fr.d.ts +2 -0
- package/dist/translations/locales/fr.js +76 -0
- package/dist/translations/locales/he.d.ts +2 -0
- package/dist/translations/locales/he.js +76 -0
- package/dist/translations/locales/hr.d.ts +2 -0
- package/dist/translations/locales/hr.js +76 -0
- package/dist/translations/locales/hu.d.ts +2 -0
- package/dist/translations/locales/hu.js +76 -0
- package/dist/translations/locales/hy.d.ts +2 -0
- package/dist/translations/locales/hy.js +76 -0
- package/dist/translations/locales/id.d.ts +2 -0
- package/dist/translations/locales/id.js +76 -0
- package/dist/translations/locales/is.d.ts +2 -0
- package/dist/translations/locales/is.js +76 -0
- package/dist/translations/locales/it.d.ts +2 -0
- package/dist/translations/locales/it.js +76 -0
- package/dist/translations/locales/ja.d.ts +2 -0
- package/dist/translations/locales/ja.js +76 -0
- package/dist/translations/locales/ko.d.ts +2 -0
- package/dist/translations/locales/ko.js +76 -0
- package/dist/translations/locales/lt.d.ts +2 -0
- package/dist/translations/locales/lt.js +76 -0
- package/dist/translations/locales/lv.d.ts +2 -0
- package/dist/translations/locales/lv.js +76 -0
- package/dist/translations/locales/my.d.ts +2 -0
- package/dist/translations/locales/my.js +76 -0
- package/dist/translations/locales/nb.d.ts +2 -0
- package/dist/translations/locales/nb.js +76 -0
- package/dist/translations/locales/nl.d.ts +2 -0
- package/dist/translations/locales/nl.js +76 -0
- package/dist/translations/locales/pl.d.ts +2 -0
- package/dist/translations/locales/pl.js +76 -0
- package/dist/translations/locales/pt.d.ts +2 -0
- package/dist/translations/locales/pt.js +76 -0
- package/dist/translations/locales/ro.d.ts +2 -0
- package/dist/translations/locales/ro.js +76 -0
- package/dist/translations/locales/rs.d.ts +2 -0
- package/dist/translations/locales/rs.js +76 -0
- package/dist/translations/locales/rsLatin.d.ts +2 -0
- package/dist/translations/locales/rsLatin.js +76 -0
- package/dist/translations/locales/ru.d.ts +2 -0
- package/dist/translations/locales/ru.js +76 -0
- package/dist/translations/locales/sk.d.ts +2 -0
- package/dist/translations/locales/sk.js +76 -0
- package/dist/translations/locales/sl.d.ts +2 -0
- package/dist/translations/locales/sl.js +76 -0
- package/dist/translations/locales/sv.d.ts +2 -0
- package/dist/translations/locales/sv.js +76 -0
- package/dist/translations/locales/ta.d.ts +2 -0
- package/dist/translations/locales/ta.js +76 -0
- package/dist/translations/locales/th.d.ts +2 -0
- package/dist/translations/locales/th.js +76 -0
- package/dist/translations/locales/tr.d.ts +2 -0
- package/dist/translations/locales/tr.js +76 -0
- package/dist/translations/locales/uk.d.ts +2 -0
- package/dist/translations/locales/uk.js +76 -0
- package/dist/translations/locales/vi.d.ts +2 -0
- package/dist/translations/locales/vi.js +76 -0
- package/dist/translations/locales/zh.d.ts +2 -0
- package/dist/translations/locales/zh.js +76 -0
- package/dist/translations/locales/zhTw.d.ts +2 -0
- package/dist/translations/locales/zhTw.js +76 -0
- package/dist/translations/types.d.ts +82 -0
- package/dist/translations/types.js +1 -0
- package/dist/types.d.ts +183 -0
- package/dist/types.js +1 -0
- package/dist/ui/createUiPlugin.d.ts +14 -0
- package/dist/ui/createUiPlugin.js +28 -0
- package/dist/ui/html.d.ts +8 -0
- package/dist/ui/html.js +8 -0
- package/dist/ui/scalar.d.ts +1 -0
- package/dist/ui/scalar.js +12 -0
- package/dist/ui/swagger.d.ts +1 -0
- package/dist/ui/swagger.js +12 -0
- package/dist/utils.d.ts +3 -0
- package/dist/utils.js +13 -0
- package/package.json +84 -0
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
const zhTw = {
|
|
2
|
+
'@seshuk/payload-plugin-openapi': {
|
|
3
|
+
paramDepth: '要填充的關聯文件層級數。',
|
|
4
|
+
paramSort: '用於排序的欄位;加上 `-` 前綴表示降序,例如 `-createdAt`。',
|
|
5
|
+
paramSortShort: '用於排序的欄位;加上 `-` 前綴表示降序。',
|
|
6
|
+
paramDraft: '回傳草稿版本。',
|
|
7
|
+
paramTrash: '包含已刪除(垃圾桶)的文件。',
|
|
8
|
+
paramFlattenLocales: '搭配 `locale=all` 使用時,設為 false 可將本地化欄位保留為各語系物件。預設為 true。',
|
|
9
|
+
paramLocale: '要回傳的語系,或使用 `all` 取得所有語系。請參閱 API 說明中的「本地化」一節。',
|
|
10
|
+
paramFallbackLocale: '當本地化值缺失時用以回退的語系,或使用 `none` 停用。',
|
|
11
|
+
schemaSelect: '選擇要回傳的欄位,例如 `select[title]=true`。省略則回傳全部。',
|
|
12
|
+
schemaPopulate: '依集合填充關聯文件,例如 `populate[posts][title]=true`。',
|
|
13
|
+
schemaJoins: '各 join 的控制項(limit/page/sort/where/count),例如 `joins[posts][limit]=10`。',
|
|
14
|
+
schemaWhere: 'Payload 的 `where` 篩選器。先巢狀欄位再接運算子:`where[field][equals]=value`。使用 `and` / `or` 陣列組合子句,例如 `where[or][0][field][equals]=value`。每個欄位僅列出其型別適用的運算子。',
|
|
15
|
+
schemaSupportedTimezones: '採用 IANA 格式的支援時區。',
|
|
16
|
+
schemaPerLocale: '各語系的值,於 `locale=all` 時回傳。',
|
|
17
|
+
collectionList: '分頁的文件清單',
|
|
18
|
+
collectionDoc: '單一文件',
|
|
19
|
+
collectionCreated: '已建立的文件',
|
|
20
|
+
collectionUpdated: '已更新的文件',
|
|
21
|
+
collectionDeleted: '已刪除的文件',
|
|
22
|
+
collectionBulkUpdate: '批次更新結果',
|
|
23
|
+
collectionBulkDelete: '批次刪除結果',
|
|
24
|
+
collectionCount: '文件數量',
|
|
25
|
+
collectionDuplicated: '已複製的文件',
|
|
26
|
+
globalDoc: '全域文件',
|
|
27
|
+
authLogin: '登入結果',
|
|
28
|
+
authLogout: '登出結果',
|
|
29
|
+
authMe: '目前已驗證的使用者',
|
|
30
|
+
authRefreshToken: '已重新整理的權杖',
|
|
31
|
+
authForgotPassword: '已寄出密碼重設郵件',
|
|
32
|
+
authResetPassword: '密碼重設結果',
|
|
33
|
+
authFirstRegister: '第一位使用者,建立時附帶驗證權杖',
|
|
34
|
+
authInit: '此驗證集合是否已有任何使用者',
|
|
35
|
+
authAccess: '目前使用者對此集合的存取(權限)',
|
|
36
|
+
authUnlock: '解鎖結果',
|
|
37
|
+
authVerify: '驗證結果',
|
|
38
|
+
versionList: '分頁的版本清單',
|
|
39
|
+
versionSingle: '單一版本',
|
|
40
|
+
versionRestored: '已還原的文件',
|
|
41
|
+
versionWhere: '針對版本欄位進行篩選,例如 `where[parent][equals]=<docId>`。',
|
|
42
|
+
jobsRunSummary: '執行排入佇列的工作(並依預設處理排程)',
|
|
43
|
+
jobsSchedulesSummary: '將依排程到期的工作排入佇列',
|
|
44
|
+
jobsRunResult: '執行結果',
|
|
45
|
+
jobsSchedulesResult: '排程結果',
|
|
46
|
+
jobsRunAllQueues: '執行所有佇列的工作。',
|
|
47
|
+
jobsLimit: '可執行的工作數上限。',
|
|
48
|
+
jobsDisableScheduling: '略過 `run` 預設執行的排程處理。',
|
|
49
|
+
jobsSilent: '抑制執行記錄。',
|
|
50
|
+
jobsSchedulesAllQueues: '處理所有佇列的排程。',
|
|
51
|
+
jobsQueue: '將操作限制於單一佇列。已知佇列:{{queues}}。',
|
|
52
|
+
uploadFile: '要上傳的二進位檔案。',
|
|
53
|
+
uploadBody: '傳送 `multipart/form-data` 以上傳檔案(一個二進位的 `file` 部分,加上一個含 JSON 字串化欄位的 `_payload` 部分),或在沒有檔案時僅以 `application/json` 傳送欄位。',
|
|
54
|
+
uploadPayloadField: 'JSON 字串化的 {{schema}} 欄位。範例:`{"alt":"A caption"}`。',
|
|
55
|
+
error400: '驗證或查詢錯誤 (ValidationError, QueryError)',
|
|
56
|
+
error401: '未驗證 (AuthenticationError)',
|
|
57
|
+
error403: '遭存取控制拒絕 (Forbidden, UnverifiedEmail)',
|
|
58
|
+
error404: '找不到文件 (NotFound)',
|
|
59
|
+
error500: '伺服器內部錯誤 (APIError)',
|
|
60
|
+
securityBearer: '貼上登入端點回傳的 `token`。以 `Authorization: Bearer <token>` 傳送。Payload 也接受 `JWT <token>` 方案及 `{{cookiePrefix}}-token` cookie。',
|
|
61
|
+
securityInteractive: '互動式登入:輸入使用者名稱(或電子郵件)與密碼;Client ID/Secret 留空。',
|
|
62
|
+
tagCollections: '集合',
|
|
63
|
+
tagCollectionsDesc: '文件集合端點(CRUD、count、duplicate)。',
|
|
64
|
+
tagGlobals: '全域',
|
|
65
|
+
tagGlobalsDesc: '全域文件端點。',
|
|
66
|
+
tagSystem: '系統',
|
|
67
|
+
tagSystemDesc: 'Payload 系統端點。',
|
|
68
|
+
tagAuth: '驗證',
|
|
69
|
+
tagVersions: '版本',
|
|
70
|
+
tagJobs: '工作',
|
|
71
|
+
localizationHeading: '本地化',
|
|
72
|
+
localizationNote: '可用的語系:{{locales}}。對讀取端點傳入 `?locale=<code>` 以選取單一語系。傳入 `?locale=all` 可一次接收所有語系——此時每個本地化欄位會以語系代碼作為鍵的物件回傳(例如 `{ "en": "Hello", "de": "Hallo" }`),而非單一值。搭配 `locale=all` 設定 `?flattenLocales=false` 可保留該各語系物件形式。欄位結構描述顯示的是單一語系的形態。',
|
|
73
|
+
docLanguagesNote: '本文件提供以下語言版本:{{languages}}。在此規格 URL 後附加 `?lang=<code>` 即可切換語言。'
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
export { zhTw };
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import type { DefaultTranslationKeys, NestedKeysStripped, TFunction } from '@payloadcms/translations';
|
|
2
|
+
import type { en } from './locales/en.js';
|
|
3
|
+
export type PluginDefaultTranslationsObject = {
|
|
4
|
+
'@seshuk/payload-plugin-openapi': {
|
|
5
|
+
paramDepth: string;
|
|
6
|
+
paramSort: string;
|
|
7
|
+
paramSortShort: string;
|
|
8
|
+
paramDraft: string;
|
|
9
|
+
paramTrash: string;
|
|
10
|
+
paramFlattenLocales: string;
|
|
11
|
+
paramLocale: string;
|
|
12
|
+
paramFallbackLocale: string;
|
|
13
|
+
schemaSelect: string;
|
|
14
|
+
schemaPopulate: string;
|
|
15
|
+
schemaJoins: string;
|
|
16
|
+
schemaWhere: string;
|
|
17
|
+
schemaSupportedTimezones: string;
|
|
18
|
+
schemaPerLocale: string;
|
|
19
|
+
collectionList: string;
|
|
20
|
+
collectionDoc: string;
|
|
21
|
+
collectionCreated: string;
|
|
22
|
+
collectionUpdated: string;
|
|
23
|
+
collectionDeleted: string;
|
|
24
|
+
collectionBulkUpdate: string;
|
|
25
|
+
collectionBulkDelete: string;
|
|
26
|
+
collectionCount: string;
|
|
27
|
+
collectionDuplicated: string;
|
|
28
|
+
globalDoc: string;
|
|
29
|
+
authLogin: string;
|
|
30
|
+
authLogout: string;
|
|
31
|
+
authMe: string;
|
|
32
|
+
authRefreshToken: string;
|
|
33
|
+
authForgotPassword: string;
|
|
34
|
+
authResetPassword: string;
|
|
35
|
+
authFirstRegister: string;
|
|
36
|
+
authInit: string;
|
|
37
|
+
authAccess: string;
|
|
38
|
+
authUnlock: string;
|
|
39
|
+
authVerify: string;
|
|
40
|
+
versionList: string;
|
|
41
|
+
versionSingle: string;
|
|
42
|
+
versionRestored: string;
|
|
43
|
+
versionWhere: string;
|
|
44
|
+
jobsRunSummary: string;
|
|
45
|
+
jobsSchedulesSummary: string;
|
|
46
|
+
jobsRunResult: string;
|
|
47
|
+
jobsSchedulesResult: string;
|
|
48
|
+
jobsRunAllQueues: string;
|
|
49
|
+
jobsLimit: string;
|
|
50
|
+
jobsDisableScheduling: string;
|
|
51
|
+
jobsSilent: string;
|
|
52
|
+
jobsSchedulesAllQueues: string;
|
|
53
|
+
jobsQueue: string;
|
|
54
|
+
uploadFile: string;
|
|
55
|
+
uploadBody: string;
|
|
56
|
+
uploadPayloadField: string;
|
|
57
|
+
error400: string;
|
|
58
|
+
error401: string;
|
|
59
|
+
error403: string;
|
|
60
|
+
error404: string;
|
|
61
|
+
error500: string;
|
|
62
|
+
securityBearer: string;
|
|
63
|
+
securityInteractive: string;
|
|
64
|
+
tagCollections: string;
|
|
65
|
+
tagCollectionsDesc: string;
|
|
66
|
+
tagGlobals: string;
|
|
67
|
+
tagGlobalsDesc: string;
|
|
68
|
+
tagSystem: string;
|
|
69
|
+
tagSystemDesc: string;
|
|
70
|
+
tagAuth: string;
|
|
71
|
+
tagVersions: string;
|
|
72
|
+
tagJobs: string;
|
|
73
|
+
localizationHeading: string;
|
|
74
|
+
localizationNote: string;
|
|
75
|
+
docLanguagesNote: string;
|
|
76
|
+
};
|
|
77
|
+
};
|
|
78
|
+
export type PluginOpenApiTranslations = typeof en;
|
|
79
|
+
export type PluginOpenApiTranslationKeys = NestedKeysStripped<PluginOpenApiTranslations>;
|
|
80
|
+
export type PluginOpenApiTFunction = TFunction<DefaultTranslationKeys | PluginOpenApiTranslationKeys>;
|
|
81
|
+
export type MessageKey = keyof PluginDefaultTranslationsObject['@seshuk/payload-plugin-openapi'];
|
|
82
|
+
export type Translate = (key: MessageKey, vars?: Record<string, unknown>) => string;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { };
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import type { SanitizedCollectionConfig, SanitizedGlobalConfig } from 'payload';
|
|
2
|
+
import type { ComponentsObject, Document, PathsObject, TagObject } from '@scalar/openapi-types/3.2';
|
|
3
|
+
import type { I18n } from '@payloadcms/translations';
|
|
4
|
+
export type OpenApiVersion = '3.0' | '3.1' | '3.2';
|
|
5
|
+
export type EntityKind = 'collection' | 'global';
|
|
6
|
+
export type HttpMethod = 'get' | 'post' | 'patch' | 'put' | 'delete';
|
|
7
|
+
export type IDType = 'text' | 'number';
|
|
8
|
+
export type Entity = SanitizedCollectionConfig | SanitizedGlobalConfig;
|
|
9
|
+
export interface OpenApiMetadata {
|
|
10
|
+
title: string;
|
|
11
|
+
version: string;
|
|
12
|
+
description?: string;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Interactive "Authorize" login for the docs UI. When on, the plugin adds a
|
|
16
|
+
* token endpoint and a security scheme so Scalar/Swagger can log in with a
|
|
17
|
+
* username and password instead of pasting a token. Off by default. The
|
|
18
|
+
* endpoint path is relative to the API route (`/openapi-auth` → `/api/openapi-auth`).
|
|
19
|
+
* - `true` — on, at the default endpoint (`/openapi-auth`).
|
|
20
|
+
* - `{ endpoint }` — on, at a custom token endpoint path.
|
|
21
|
+
*/
|
|
22
|
+
export type InteractiveAuthOption = boolean | {
|
|
23
|
+
endpoint?: string;
|
|
24
|
+
};
|
|
25
|
+
/** Match an entity by slug (any kind), a {kind,slug} pair, or a slug pattern. */
|
|
26
|
+
export type EntityMatcher = string | RegExp | {
|
|
27
|
+
kind: EntityKind;
|
|
28
|
+
slug: string;
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* Drops generated operations. Within one rule, every set field must match (AND);
|
|
32
|
+
* an operation is removed if any rule matches it. Fields:
|
|
33
|
+
* - `method` — HTTP method(s); omit to match any.
|
|
34
|
+
* - `slug` — entity slug, exact string or RegExp; omit to match any.
|
|
35
|
+
* - `kind` — restrict to collections or globals; omit to match both.
|
|
36
|
+
* - `path` — RegExp tested against the final route path (e.g. `/api/posts/{id}`).
|
|
37
|
+
*/
|
|
38
|
+
export interface OperationRule {
|
|
39
|
+
method?: HttpMethod | HttpMethod[];
|
|
40
|
+
slug?: string | RegExp;
|
|
41
|
+
kind?: EntityKind;
|
|
42
|
+
path?: RegExp;
|
|
43
|
+
}
|
|
44
|
+
/** Context passed to {@link FilterOptions.excludeWhen} for each operation. */
|
|
45
|
+
export interface OperationContext {
|
|
46
|
+
method: HttpMethod;
|
|
47
|
+
path: string;
|
|
48
|
+
slug: string;
|
|
49
|
+
kind: EntityKind;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Controls what appears in the spec, in one flat object: which entities are
|
|
53
|
+
* documented, which Payload-internal collections to show, which extra endpoint
|
|
54
|
+
* groups to generate, and which operations to drop.
|
|
55
|
+
*/
|
|
56
|
+
export interface FilterOptions {
|
|
57
|
+
/** Allowlist of entities; when non-empty, only matching entities are documented. */
|
|
58
|
+
include?: EntityMatcher[];
|
|
59
|
+
/** Entities to omit entirely (schemas + paths). */
|
|
60
|
+
exclude?: EntityMatcher[];
|
|
61
|
+
/** Include collections flagged `hidden`/`admin.hidden`. Default false. */
|
|
62
|
+
includeHidden?: boolean;
|
|
63
|
+
/** Include Payload-internal collections (payload-jobs, payload-preferences, ...). Default false. */
|
|
64
|
+
includeSystem?: boolean;
|
|
65
|
+
/** Document endpoints carrying `custom.openapi` metadata. Default true. */
|
|
66
|
+
includeCustom?: boolean;
|
|
67
|
+
/** Document auth operation endpoints (login/logout/me/...). Default true. */
|
|
68
|
+
includeAuth?: boolean;
|
|
69
|
+
/** Document admin/bootstrap auth endpoints (/init, /access, first-register). Default false. */
|
|
70
|
+
includeAdminAuth?: boolean;
|
|
71
|
+
/** Document version operation endpoints (/versions, /versions/{id}). Default true. */
|
|
72
|
+
includeVersions?: boolean;
|
|
73
|
+
/** Document jobs endpoints (/payload-jobs/run, /handle-schedules). Default true. */
|
|
74
|
+
includeJobs?: boolean;
|
|
75
|
+
/** Per-operation removal rules. */
|
|
76
|
+
excludeOperations?: OperationRule[];
|
|
77
|
+
/** Escape hatch: return true to drop an operation. Runs after excludeOperations. */
|
|
78
|
+
excludeWhen?: (ctx: OperationContext) => boolean;
|
|
79
|
+
}
|
|
80
|
+
export interface OpenApiExtension {
|
|
81
|
+
paths?: PathsObject;
|
|
82
|
+
components?: ComponentsObject;
|
|
83
|
+
tags?: TagObject[];
|
|
84
|
+
transform?: (doc: Document, ctx: BuildContext) => Document;
|
|
85
|
+
}
|
|
86
|
+
export type OpenApiPluginOptions = {
|
|
87
|
+
metadata: OpenApiMetadata;
|
|
88
|
+
/**
|
|
89
|
+
* OpenAPI version to serve. The document is always built as 3.2; `3.1` and
|
|
90
|
+
* `3.0` each run a downconversion pass on the result. Default `3.2`.
|
|
91
|
+
*/
|
|
92
|
+
openapiVersion?: OpenApiVersion;
|
|
93
|
+
/**
|
|
94
|
+
* Where the spec is served, relative to the Payload API route (`routes.api`,
|
|
95
|
+
* `/api` by default). So the `/openapi.json` default is served at
|
|
96
|
+
* `/api/openapi.json`. Default `/openapi.json`.
|
|
97
|
+
*/
|
|
98
|
+
specEndpoint?: string;
|
|
99
|
+
enabled?: boolean;
|
|
100
|
+
/**
|
|
101
|
+
* Mount the runtime spec (and interactive-auth) endpoints. Default true. Set
|
|
102
|
+
* false to register only the `openapi:generate` CLI bin and skip serving over
|
|
103
|
+
* HTTP — generate the spec to a file and host it however you like (static
|
|
104
|
+
* asset, CDN, committed to the repo). The docs UI plugins (`scalar`/`swaggerUi`)
|
|
105
|
+
* are unaffected; point their `specEndpoint` at wherever the file ends up.
|
|
106
|
+
*/
|
|
107
|
+
serve?: boolean;
|
|
108
|
+
filters?: FilterOptions;
|
|
109
|
+
/** Interactive docs-UI login. See {@link InteractiveAuthOption}. Default false. */
|
|
110
|
+
interactiveAuth?: InteractiveAuthOption;
|
|
111
|
+
/**
|
|
112
|
+
* Emit an OpenAPI 3.2 nested tag hierarchy (nav groups via `kind`/`parent`,
|
|
113
|
+
* plus per-entity auth/version sub-tags). Off by default: Scalar and Swagger UI
|
|
114
|
+
* don't render 3.2 nested tags yet and would show a confusing flat list of group
|
|
115
|
+
* names. When off, `doc.tags` is a flat list of per-entity tags with their
|
|
116
|
+
* descriptions.
|
|
117
|
+
*/
|
|
118
|
+
nestedTags?: boolean;
|
|
119
|
+
/**
|
|
120
|
+
* Cache the built document for the life of the process. The Payload config is
|
|
121
|
+
* static after boot, so the spec is the same every time apart from the server
|
|
122
|
+
* URL, which is filled in fresh per response. Default true; set false in dev to
|
|
123
|
+
* rebuild on every request, e.g. `cache: process.env.NODE_ENV === 'production'`.
|
|
124
|
+
*/
|
|
125
|
+
cache?: boolean;
|
|
126
|
+
extensions?: OpenApiExtension[];
|
|
127
|
+
};
|
|
128
|
+
export interface UiPluginOptions {
|
|
129
|
+
/**
|
|
130
|
+
* Where the docs UI is served, relative to the Payload API route
|
|
131
|
+
* (`routes.api`, `/api` by default). So the `/docs` default is served at
|
|
132
|
+
* `/api/docs`. Default `/docs`.
|
|
133
|
+
*/
|
|
134
|
+
path?: string;
|
|
135
|
+
/** Absolute URL the UI fetches the OpenAPI document from. Default `<apiRoute>/openapi.json`. */
|
|
136
|
+
specEndpoint?: string;
|
|
137
|
+
/** CDN base for the UI's assets. Default is the official jsDelivr package. */
|
|
138
|
+
cdnBase?: string;
|
|
139
|
+
/**
|
|
140
|
+
* Extra UI config, merged into the underlying library's init options (Scalar's
|
|
141
|
+
* `createApiReference` config or Swagger UI's `SwaggerUIBundle`). Must be
|
|
142
|
+
* JSON-serializable, functions aren't supported here.
|
|
143
|
+
*/
|
|
144
|
+
configuration?: Record<string, unknown>;
|
|
145
|
+
/** Set false to skip mounting the docs UI. */
|
|
146
|
+
enabled?: boolean;
|
|
147
|
+
}
|
|
148
|
+
export interface ResolvedFilters {
|
|
149
|
+
include: EntityMatcher[];
|
|
150
|
+
exclude: EntityMatcher[];
|
|
151
|
+
includeHidden: boolean;
|
|
152
|
+
includeSystem: boolean;
|
|
153
|
+
includeCustom: boolean;
|
|
154
|
+
includeAuth: boolean;
|
|
155
|
+
includeAdminAuth: boolean;
|
|
156
|
+
includeVersions: boolean;
|
|
157
|
+
includeJobs: boolean;
|
|
158
|
+
excludeOperations: OperationRule[];
|
|
159
|
+
excludeWhen?: (ctx: OperationContext) => boolean;
|
|
160
|
+
}
|
|
161
|
+
export interface ResolvedOptions {
|
|
162
|
+
metadata: OpenApiMetadata;
|
|
163
|
+
openapiVersion: OpenApiVersion;
|
|
164
|
+
specEndpoint: string;
|
|
165
|
+
serve: boolean;
|
|
166
|
+
filters: ResolvedFilters;
|
|
167
|
+
interactiveAuth: {
|
|
168
|
+
enabled: boolean;
|
|
169
|
+
endpoint: string;
|
|
170
|
+
};
|
|
171
|
+
nestedTags: boolean;
|
|
172
|
+
cache: boolean;
|
|
173
|
+
extensions: OpenApiExtension[];
|
|
174
|
+
}
|
|
175
|
+
export interface BuildContext {
|
|
176
|
+
defaultIDType: IDType;
|
|
177
|
+
locales: string[];
|
|
178
|
+
apiRoute: string;
|
|
179
|
+
/** Languages the docs are available in (project `i18n.supportedLanguages`). */
|
|
180
|
+
docLanguages: string[];
|
|
181
|
+
/** Request i18n: resolves Payload entity descriptions and, via {@link makeT}, plugin strings. */
|
|
182
|
+
i18n: I18n;
|
|
183
|
+
}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Plugin } from 'payload';
|
|
2
|
+
import type { UiPluginOptions } from '../types.js';
|
|
3
|
+
export interface UiRenderArgs {
|
|
4
|
+
specUrl: string;
|
|
5
|
+
cdnBase: string;
|
|
6
|
+
title: string;
|
|
7
|
+
configuration: Record<string, unknown>;
|
|
8
|
+
}
|
|
9
|
+
export interface UiPluginSpec {
|
|
10
|
+
defaultDocsUrl: string;
|
|
11
|
+
defaultCdnBase: string;
|
|
12
|
+
render: (args: UiRenderArgs) => string;
|
|
13
|
+
}
|
|
14
|
+
export declare const createUiPlugin: (spec: UiPluginSpec) => (options?: UiPluginOptions) => Plugin;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
const createUiPlugin = (spec)=>(options = {})=>(config)=>{
|
|
2
|
+
if (false === options.enabled) return config;
|
|
3
|
+
const docsPath = options.path ?? spec.defaultDocsUrl;
|
|
4
|
+
const specUrl = options.specEndpoint ?? `${config.routes?.api ?? '/api'}/openapi.json`;
|
|
5
|
+
const cdnBase = options.cdnBase ?? spec.defaultCdnBase;
|
|
6
|
+
const html = spec.render({
|
|
7
|
+
specUrl,
|
|
8
|
+
cdnBase,
|
|
9
|
+
title: 'API Reference',
|
|
10
|
+
configuration: options.configuration ?? {}
|
|
11
|
+
});
|
|
12
|
+
return {
|
|
13
|
+
...config,
|
|
14
|
+
endpoints: [
|
|
15
|
+
...config.endpoints ?? [],
|
|
16
|
+
{
|
|
17
|
+
path: docsPath,
|
|
18
|
+
method: 'get',
|
|
19
|
+
handler: ()=>new Response(html, {
|
|
20
|
+
headers: {
|
|
21
|
+
'content-type': 'text/html; charset=utf-8'
|
|
22
|
+
}
|
|
23
|
+
})
|
|
24
|
+
}
|
|
25
|
+
]
|
|
26
|
+
};
|
|
27
|
+
};
|
|
28
|
+
export { createUiPlugin };
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export declare const escapeHtml: (value: string) => string;
|
|
2
|
+
export declare const jsonForScript: (value: unknown) => string;
|
|
3
|
+
export declare const htmlPage: ({ title, head, body }: {
|
|
4
|
+
title: string;
|
|
5
|
+
head: string;
|
|
6
|
+
body: string;
|
|
7
|
+
}) => string;
|
|
8
|
+
export declare const specUrlExpr: (specUrl: string) => string;
|
package/dist/ui/html.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
const escapeHtml = (value)=>value.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');
|
|
2
|
+
const jsonForScript = (value)=>JSON.stringify(value ?? {}).replace(/</g, '\\u003c');
|
|
3
|
+
const htmlPage = ({ title, head, body })=>`<!doctype html><html><head><meta charset="utf-8"/><title>${escapeHtml(title)}</title><meta name="viewport" content="width=device-width, initial-scale=1"/>${head}</head><body>${body}</body></html>`;
|
|
4
|
+
const specUrlExpr = (specUrl)=>{
|
|
5
|
+
const u = JSON.stringify(specUrl);
|
|
6
|
+
return `(function(){var l=new URLSearchParams(location.search).get('lang');return ${u}+(l?((${u}.indexOf('?')>-1?'&':'?')+'lang='+encodeURIComponent(l)):'')})()`;
|
|
7
|
+
};
|
|
8
|
+
export { escapeHtml, htmlPage, jsonForScript, specUrlExpr };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const scalar: (options?: import("../types.js").UiPluginOptions) => import("payload").Plugin;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { createUiPlugin } from "./createUiPlugin.js";
|
|
2
|
+
import { escapeHtml, htmlPage, jsonForScript, specUrlExpr } from "./html.js";
|
|
3
|
+
const scalar = createUiPlugin({
|
|
4
|
+
defaultDocsUrl: '/docs',
|
|
5
|
+
defaultCdnBase: 'https://cdn.jsdelivr.net/npm/@scalar/api-reference',
|
|
6
|
+
render: ({ specUrl, cdnBase, title, configuration })=>htmlPage({
|
|
7
|
+
title,
|
|
8
|
+
head: '',
|
|
9
|
+
body: `<div id="app"></div><script src="${escapeHtml(cdnBase)}"></script><script>Scalar.createApiReference('#app',{url:${specUrlExpr(specUrl)},...${jsonForScript(configuration)}})</script>`
|
|
10
|
+
})
|
|
11
|
+
});
|
|
12
|
+
export { scalar };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const swaggerUi: (options?: import("../types.js").UiPluginOptions) => import("payload").Plugin;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { createUiPlugin } from "./createUiPlugin.js";
|
|
2
|
+
import { escapeHtml, htmlPage, jsonForScript, specUrlExpr } from "./html.js";
|
|
3
|
+
const swaggerUi = createUiPlugin({
|
|
4
|
+
defaultDocsUrl: '/docs',
|
|
5
|
+
defaultCdnBase: 'https://cdn.jsdelivr.net/npm/swagger-ui-dist',
|
|
6
|
+
render: ({ specUrl, cdnBase, title, configuration })=>htmlPage({
|
|
7
|
+
title,
|
|
8
|
+
head: `<link rel="stylesheet" href="${escapeHtml(cdnBase)}/swagger-ui.css"/>`,
|
|
9
|
+
body: `<div id="swagger-ui"></div><script src="${escapeHtml(cdnBase)}/swagger-ui-bundle.js"></script><script>window.onload=()=>SwaggerUIBundle({url:${specUrlExpr(specUrl)},dom_id:'#swagger-ui',...${jsonForScript(configuration)}})</script>`
|
|
10
|
+
})
|
|
11
|
+
});
|
|
12
|
+
export { swaggerUi };
|
package/dist/utils.d.ts
ADDED
package/dist/utils.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
const upperFirst = (value)=>0 === value.length ? value : value[0].toUpperCase() + value.slice(1);
|
|
2
|
+
const isPlainObject = (value)=>Boolean(value) && 'object' == typeof value && !Array.isArray(value);
|
|
3
|
+
const deepMerge = (a, b)=>{
|
|
4
|
+
const out = {
|
|
5
|
+
...a
|
|
6
|
+
};
|
|
7
|
+
for (const [key, value] of Object.entries(b)){
|
|
8
|
+
const prev = out[key];
|
|
9
|
+
out[key] = isPlainObject(prev) && isPlainObject(value) ? deepMerge(prev, value) : value;
|
|
10
|
+
}
|
|
11
|
+
return out;
|
|
12
|
+
};
|
|
13
|
+
export { deepMerge, isPlainObject, upperFirst };
|
package/package.json
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@seshuk/payload-plugin-openapi",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "OpenAPI 3.0/3.1/3.2 spec generator for Payload CMS, with Scalar/Swagger UI.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"api-docs",
|
|
7
|
+
"cms",
|
|
8
|
+
"openapi",
|
|
9
|
+
"openapi-spec",
|
|
10
|
+
"payload",
|
|
11
|
+
"payload-plugin",
|
|
12
|
+
"payloadcms",
|
|
13
|
+
"plugin",
|
|
14
|
+
"scalar",
|
|
15
|
+
"swagger",
|
|
16
|
+
"typescript"
|
|
17
|
+
],
|
|
18
|
+
"homepage": "https://github.com/maximseshuk/payload-plugin-openapi#readme",
|
|
19
|
+
"bugs": "https://github.com/maximseshuk/payload-plugin-openapi/issues",
|
|
20
|
+
"license": "MIT",
|
|
21
|
+
"author": "Maxim Seshuk",
|
|
22
|
+
"repository": {
|
|
23
|
+
"type": "git",
|
|
24
|
+
"url": "git+https://github.com/maximseshuk/payload-plugin-openapi.git"
|
|
25
|
+
},
|
|
26
|
+
"funding": "https://ko-fi.com/V7V61UCT39",
|
|
27
|
+
"files": [
|
|
28
|
+
"dist"
|
|
29
|
+
],
|
|
30
|
+
"type": "module",
|
|
31
|
+
"main": "./dist/index.js",
|
|
32
|
+
"types": "./dist/index.d.ts",
|
|
33
|
+
"exports": {
|
|
34
|
+
".": {
|
|
35
|
+
"import": "./dist/index.js",
|
|
36
|
+
"types": "./dist/index.d.ts",
|
|
37
|
+
"default": "./dist/index.js"
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
"dependencies": {
|
|
41
|
+
"@scalar/openapi-types": "^0.9.1"
|
|
42
|
+
},
|
|
43
|
+
"devDependencies": {
|
|
44
|
+
"@payloadcms/db-sqlite": "^3.85.1",
|
|
45
|
+
"@payloadcms/next": "^3.85.1",
|
|
46
|
+
"@payloadcms/translations": "^3.85.1",
|
|
47
|
+
"@rslib/core": "^0.22.1",
|
|
48
|
+
"@seriousme/openapi-schema-validator": "^2.9.0",
|
|
49
|
+
"cross-env": "^10.1.0",
|
|
50
|
+
"next": "^16.2.9",
|
|
51
|
+
"oxfmt": "^0.54.0",
|
|
52
|
+
"oxlint": "^1.69.0",
|
|
53
|
+
"payload": "^3.85.1",
|
|
54
|
+
"react": "^19.2.7",
|
|
55
|
+
"react-dom": "^19.2.7",
|
|
56
|
+
"typescript": "^6.0.3",
|
|
57
|
+
"vite-tsconfig-paths": "7.0.0-alpha.1",
|
|
58
|
+
"vitest": "^4.1.8"
|
|
59
|
+
},
|
|
60
|
+
"peerDependencies": {
|
|
61
|
+
"@payloadcms/translations": "^3.53.0",
|
|
62
|
+
"payload": "^3.53.0"
|
|
63
|
+
},
|
|
64
|
+
"engines": {
|
|
65
|
+
"node": ">=20.9.0"
|
|
66
|
+
},
|
|
67
|
+
"scripts": {
|
|
68
|
+
"build": "rslib build",
|
|
69
|
+
"build:watch": "rslib build --watch",
|
|
70
|
+
"dev": "cross-env PAYLOAD_CONFIG_PATH=./tests/payload.config.ts next dev tests --turbo",
|
|
71
|
+
"dev:safe": "rm -rf .next && cross-env PAYLOAD_CONFIG_PATH=./tests/payload.config.ts next dev tests --turbo --port 3001",
|
|
72
|
+
"dev:generate-importmap": "cd tests && cross-env PAYLOAD_CONFIG_PATH=./payload.config.ts payload generate:importmap",
|
|
73
|
+
"lint": "oxlint src tests",
|
|
74
|
+
"lint:fix": "oxlint src tests --fix",
|
|
75
|
+
"format": "oxfmt .",
|
|
76
|
+
"format:check": "oxfmt --check .",
|
|
77
|
+
"test": "vitest run",
|
|
78
|
+
"test:watch": "vitest",
|
|
79
|
+
"typecheck": "tsc --noEmit",
|
|
80
|
+
"release:patch": "pnpm version patch -m \"chore(release): v%s\"",
|
|
81
|
+
"release:minor": "pnpm version minor -m \"chore(release): v%s\"",
|
|
82
|
+
"release:major": "pnpm version major -m \"chore(release): v%s\""
|
|
83
|
+
}
|
|
84
|
+
}
|