payload-intl 1.1.3 → 1.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (220) hide show
  1. package/README.md +62 -221
  2. package/dist/_common/index.d.ts +17 -0
  3. package/dist/_common/index.d.ts.map +1 -0
  4. package/dist/_common/index.js +27 -0
  5. package/dist/_common/index.js.map +1 -0
  6. package/dist/_common/procedure.d.ts +34 -0
  7. package/dist/_common/procedure.d.ts.map +1 -0
  8. package/dist/_common/procedure.js +110 -0
  9. package/dist/_common/procedure.js.map +1 -0
  10. package/dist/_common/urls.d.ts +11 -0
  11. package/dist/_common/urls.d.ts.map +1 -0
  12. package/dist/_common/urls.js +23 -0
  13. package/dist/_common/urls.js.map +1 -0
  14. package/dist/_common/utils.d.ts +5 -0
  15. package/dist/_common/utils.d.ts.map +1 -0
  16. package/dist/_common/utils.js +42 -0
  17. package/dist/_common/utils.js.map +1 -0
  18. package/dist/components/MessageController.d.ts +4 -6
  19. package/dist/components/MessageController.d.ts.map +1 -1
  20. package/dist/components/MessageController.js +25 -41
  21. package/dist/components/MessageController.js.map +1 -1
  22. package/dist/components/MessageFormContext.d.ts +29 -0
  23. package/dist/components/MessageFormContext.d.ts.map +1 -0
  24. package/dist/components/MessageFormContext.js +30 -0
  25. package/dist/components/MessageFormContext.js.map +1 -0
  26. package/dist/components/MessagesForm.d.ts +2 -2
  27. package/dist/components/MessagesForm.d.ts.map +1 -1
  28. package/dist/components/MessagesForm.js +126 -112
  29. package/dist/components/MessagesForm.js.map +1 -1
  30. package/dist/components/MessagesForm.module.css +41 -0
  31. package/dist/{exports/link.d.ts → components/MessagesLink.d.ts} +3 -3
  32. package/dist/components/MessagesLink.d.ts.map +1 -0
  33. package/dist/components/MessagesLink.js +19 -0
  34. package/dist/components/MessagesLink.js.map +1 -0
  35. package/dist/components/MessagesView.d.ts +10 -0
  36. package/dist/components/MessagesView.d.ts.map +1 -0
  37. package/dist/components/MessagesView.js +48 -0
  38. package/dist/components/MessagesView.js.map +1 -0
  39. package/dist/components/actions/JsonImport.d.ts.map +1 -1
  40. package/dist/components/actions/JsonImport.js +68 -61
  41. package/dist/components/actions/JsonImport.js.map +1 -1
  42. package/dist/components/actions/JsonImport.module.css +14 -0
  43. package/dist/components/actions/Move.d.ts.map +1 -1
  44. package/dist/components/actions/Move.js +41 -0
  45. package/dist/components/actions/Move.js.map +1 -0
  46. package/dist/components/actions/Move.module.css +8 -0
  47. package/dist/components/inputs/InputWrapper.d.ts +1 -1
  48. package/dist/components/inputs/InputWrapper.d.ts.map +1 -1
  49. package/dist/components/inputs/InputWrapper.js +31 -29
  50. package/dist/components/inputs/InputWrapper.js.map +1 -1
  51. package/dist/components/inputs/InputWrapper.module.css +34 -0
  52. package/dist/components/inputs/LexicalInput.d.ts +3 -3
  53. package/dist/components/inputs/LexicalInput.js +86 -0
  54. package/dist/components/inputs/LexicalInput.js.map +1 -0
  55. package/dist/components/inputs/MessageInput.d.ts +2 -2
  56. package/dist/components/inputs/MessageInput.d.ts.map +1 -1
  57. package/dist/components/inputs/MessageInput.js +110 -37
  58. package/dist/components/inputs/MessageInput.js.map +1 -1
  59. package/dist/components/inputs/MessageInput.module.css +25 -0
  60. package/dist/components/inputs/variables/VariableChip.d.ts +8 -2
  61. package/dist/components/inputs/variables/VariableChip.d.ts.map +1 -1
  62. package/dist/components/inputs/variables/VariableChip.js +86 -64
  63. package/dist/components/inputs/variables/VariableChip.js.map +1 -1
  64. package/dist/components/inputs/variables/VariableChip.module.css +66 -0
  65. package/dist/components/inputs/variables/VariableIcon.d.ts +1 -1
  66. package/dist/components/inputs/variables/VariableIcon.d.ts.map +1 -1
  67. package/dist/components/inputs/variables/VariableIcon.js +35 -0
  68. package/dist/components/inputs/variables/VariableIcon.js.map +1 -0
  69. package/dist/components/inputs/variables/VariableNode.d.ts +11 -0
  70. package/dist/components/inputs/variables/VariableNode.d.ts.map +1 -0
  71. package/dist/components/inputs/variables/VariableNode.js +36 -0
  72. package/dist/components/inputs/variables/VariableNode.js.map +1 -0
  73. package/dist/components/inputs/variables/VariableSuggestion.module.css +34 -0
  74. package/dist/components/inputs/variables/editors/DateVariableEditor.d.ts +2 -2
  75. package/dist/components/inputs/variables/editors/DateVariableEditor.d.ts.map +1 -1
  76. package/dist/components/inputs/variables/editors/DateVariableEditor.js +15 -0
  77. package/dist/components/inputs/variables/editors/DateVariableEditor.js.map +1 -0
  78. package/dist/components/inputs/variables/editors/PluralVariableEditor.d.ts +1 -1
  79. package/dist/components/inputs/variables/editors/PluralVariableEditor.d.ts.map +1 -1
  80. package/dist/components/inputs/variables/editors/PluralVariableEditor.js +191 -172
  81. package/dist/components/inputs/variables/editors/PluralVariableEditor.js.map +1 -1
  82. package/dist/components/inputs/variables/editors/PluralVariableEditor.module.css +149 -0
  83. package/dist/components/inputs/variables/editors/SelectVariableEditor.d.ts +1 -1
  84. package/dist/components/inputs/variables/editors/SelectVariableEditor.d.ts.map +1 -1
  85. package/dist/components/inputs/variables/editors/SelectVariableEditor.js +58 -52
  86. package/dist/components/inputs/variables/editors/SelectVariableEditor.js.map +1 -1
  87. package/dist/components/inputs/variables/editors/SelectVariableEditor.module.css +25 -0
  88. package/dist/components/inputs/variables/editors/TagVariableEditor.d.ts +1 -1
  89. package/dist/components/inputs/variables/editors/TagVariableEditor.d.ts.map +1 -1
  90. package/dist/components/inputs/variables/editors/TagVariableEditor.js +36 -32
  91. package/dist/components/inputs/variables/editors/TagVariableEditor.js.map +1 -1
  92. package/dist/components/inputs/variables/editors/TagVariableEditor.module.css +7 -0
  93. package/dist/components/inputs/variables/editors/TimeVariableEditor.d.ts +2 -2
  94. package/dist/components/inputs/variables/editors/TimeVariableEditor.d.ts.map +1 -1
  95. package/dist/components/inputs/variables/editors/TimeVariableEditor.js +15 -0
  96. package/dist/components/inputs/variables/editors/TimeVariableEditor.js.map +1 -0
  97. package/dist/components/inputs/variables/pickers/NumericVariablePicker.d.ts +1 -1
  98. package/dist/components/inputs/variables/pickers/NumericVariablePicker.d.ts.map +1 -1
  99. package/dist/components/inputs/variables/pickers/NumericVariablePicker.js +48 -51
  100. package/dist/components/inputs/variables/pickers/NumericVariablePicker.js.map +1 -1
  101. package/dist/components/inputs/variables/pickers/NumericVariablePicker.module.css +27 -0
  102. package/dist/components/inputs/variables/pickers/TemporalElementEditor.d.ts +1 -1
  103. package/dist/components/inputs/variables/pickers/TemporalElementEditor.d.ts.map +1 -1
  104. package/dist/components/inputs/variables/pickers/TemporalElementEditor.js +58 -0
  105. package/dist/components/inputs/variables/pickers/TemporalElementEditor.js.map +1 -0
  106. package/dist/components/inputs/variables/pickers/TemporalElementEditor.module.css +27 -0
  107. package/dist/components/layout/MessageField.d.ts +3 -3
  108. package/dist/components/layout/MessageField.d.ts.map +1 -1
  109. package/dist/components/layout/MessageField.js +56 -56
  110. package/dist/components/layout/MessageField.js.map +1 -1
  111. package/dist/components/layout/MessageField.module.css +19 -0
  112. package/dist/components/layout/MessagesTabs.d.ts +1 -1
  113. package/dist/components/layout/MessagesTabs.d.ts.map +1 -1
  114. package/dist/components/layout/MessagesTabs.js +40 -43
  115. package/dist/components/layout/MessagesTabs.js.map +1 -1
  116. package/dist/components/layout/MessagesTabs.module.css +21 -0
  117. package/dist/components/layout/MessagesTree.d.ts +3 -2
  118. package/dist/components/layout/MessagesTree.d.ts.map +1 -1
  119. package/dist/components/layout/MessagesTree.js +65 -67
  120. package/dist/components/layout/MessagesTree.js.map +1 -1
  121. package/dist/components/layout/MessagesTree.module.css +36 -0
  122. package/dist/const.d.ts +8 -9
  123. package/dist/const.d.ts.map +1 -1
  124. package/dist/const.js +16 -13
  125. package/dist/const.js.map +1 -1
  126. package/dist/endpoints/get-messages.d.ts +1 -1
  127. package/dist/endpoints/get-messages.d.ts.map +1 -1
  128. package/dist/endpoints/get-messages.js +7 -13
  129. package/dist/endpoints/get-messages.js.map +1 -1
  130. package/dist/endpoints/set-messages.d.ts +1 -1
  131. package/dist/endpoints/set-messages.d.ts.map +1 -1
  132. package/dist/endpoints/set-messages.js +61 -45
  133. package/dist/endpoints/set-messages.js.map +1 -1
  134. package/dist/entities.d.ts +5 -0
  135. package/dist/entities.d.ts.map +1 -0
  136. package/dist/entities.js +38 -0
  137. package/dist/entities.js.map +1 -0
  138. package/dist/exports/client.d.ts +2 -0
  139. package/dist/exports/client.d.ts.map +1 -0
  140. package/dist/exports/client.js +3 -0
  141. package/dist/exports/client.js.map +1 -0
  142. package/dist/exports/rsc.d.ts +2 -2
  143. package/dist/exports/rsc.d.ts.map +1 -1
  144. package/dist/exports/rsc.js +4 -7
  145. package/dist/exports/rsc.js.map +1 -1
  146. package/dist/hooks.d.ts +4 -0
  147. package/dist/hooks.d.ts.map +1 -0
  148. package/dist/hooks.js +24 -0
  149. package/dist/hooks.js.map +1 -0
  150. package/dist/index.d.ts +20 -3
  151. package/dist/index.d.ts.map +1 -1
  152. package/dist/index.js +64 -80
  153. package/dist/index.js.map +1 -1
  154. package/dist/payload-types.d.ts +258 -0
  155. package/dist/payload-types.d.ts.map +1 -0
  156. package/dist/payload-types.js +15 -0
  157. package/dist/payload-types.js.map +1 -0
  158. package/dist/requests/fetchMessageFromAPI.js +14 -12
  159. package/dist/requests/fetchMessageFromAPI.js.map +1 -1
  160. package/dist/requests/fetchMessageFromPayload.d.ts +1 -1
  161. package/dist/requests/fetchMessageFromPayload.js +28 -29
  162. package/dist/requests/fetchMessageFromPayload.js.map +1 -1
  163. package/dist/requests/fetchMessages.d.ts +3 -3
  164. package/dist/requests/fetchMessages.js +10 -12
  165. package/dist/requests/fetchMessages.js.map +1 -1
  166. package/dist/types.d.ts +7 -23
  167. package/dist/types.d.ts.map +1 -1
  168. package/dist/types.js +3 -0
  169. package/dist/types.js.map +1 -0
  170. package/dist/utils/config.d.ts +3 -3
  171. package/dist/utils/config.d.ts.map +1 -1
  172. package/dist/utils/config.js +27 -17
  173. package/dist/utils/config.js.map +1 -1
  174. package/dist/utils/error-handling.js +14 -12
  175. package/dist/utils/error-handling.js.map +1 -1
  176. package/dist/utils/format.d.ts +1 -1
  177. package/dist/utils/format.js +21 -13
  178. package/dist/utils/format.js.map +1 -1
  179. package/dist/utils/guards.d.ts +2 -1
  180. package/dist/utils/guards.js +8 -18
  181. package/dist/utils/guards.js.map +1 -1
  182. package/dist/utils/icu-tranform.d.ts +13 -18
  183. package/dist/utils/icu-tranform.d.ts.map +1 -1
  184. package/dist/utils/icu-tranform.js +140 -108
  185. package/dist/utils/icu-tranform.js.map +1 -1
  186. package/dist/utils/sanitize.d.ts +1 -1
  187. package/dist/utils/sanitize.js +36 -17
  188. package/dist/utils/sanitize.js.map +1 -1
  189. package/dist/utils/schema.d.ts +1 -3
  190. package/dist/utils/schema.d.ts.map +1 -1
  191. package/dist/utils/schema.js +42 -34
  192. package/dist/utils/schema.js.map +1 -1
  193. package/dist/utils/validate.d.ts +2 -2
  194. package/dist/utils/validate.js +52 -53
  195. package/dist/utils/validate.js.map +1 -1
  196. package/package.json +35 -52
  197. package/dist/components/inputs/variables/VariableSuggestion.d.ts +0 -9
  198. package/dist/components/inputs/variables/VariableSuggestion.d.ts.map +0 -1
  199. package/dist/components/inputs/variables/VariableSuggestion.js +0 -41
  200. package/dist/components/inputs/variables/VariableSuggestion.js.map +0 -1
  201. package/dist/components/inputs/variables/extension.d.ts +0 -4
  202. package/dist/components/inputs/variables/extension.d.ts.map +0 -1
  203. package/dist/components/inputs/variables/extension.js +0 -93
  204. package/dist/components/inputs/variables/extension.js.map +0 -1
  205. package/dist/context/messages-form.d.ts +0 -29
  206. package/dist/context/messages-form.d.ts.map +0 -1
  207. package/dist/context/messages-form.js +0 -26
  208. package/dist/context/messages-form.js.map +0 -1
  209. package/dist/exports/link.d.ts.map +0 -1
  210. package/dist/exports/link.js +0 -21
  211. package/dist/exports/link.js.map +0 -1
  212. package/dist/exports/view.d.ts +0 -10
  213. package/dist/exports/view.d.ts.map +0 -1
  214. package/dist/exports/view.js +0 -53
  215. package/dist/exports/view.js.map +0 -1
  216. package/dist/styles.css +0 -1
  217. package/dist/utils/cn.d.ts +0 -3
  218. package/dist/utils/cn.d.ts.map +0 -1
  219. package/dist/utils/cn.js +0 -9
  220. package/dist/utils/cn.js.map +0 -1
package/README.md CHANGED
@@ -1,267 +1,108 @@
1
1
  # payload-intl
2
2
 
3
- **payload-intl** moves translations out of your codebase.
3
+ Schema-driven internationalization for Payload CMS using ICU MessageFormat.
4
4
 
5
- 1. Define message keys (and their arguments) in TypeScript.
6
- 2. Translate them in Payload’s admin panel — no code required.
5
+ [![npm version](https://img.shields.io/npm/v/payload-intl)](https://www.npmjs.com/package/payload-intl)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
7
7
 
8
- **Features**
9
-
10
- - Define message schema in code; edit translations in a rich admin UI
11
- - Compatible with [next-intl](https://next-intl.dev/) and any ICU consumer
12
- - Automatic runtime validation of message arguments
13
- - Autocomplete to quickly insert and configure valid ICU arguments
14
- - Add optional descriptions to each message so editors understand the context
15
- - Visual tree & tabbed editor for quick navigation
16
- <!-- - Support for Rich Text messages -->
17
- <!-- - Import/export JSON, copy messages between paths -->
18
-
19
- ## Getting Started
20
-
21
- ```bash
22
- # pnpm
23
- pnpm add payload-intl
24
- # yarn
25
- yarn add payload-intl
26
- # npm
27
- npm install payload-intl
28
- ```
29
-
30
- ### 1) Define messages
31
-
32
- Organize messages in a hierarchical structure using ICU MessageFormat:
33
-
34
- ```typescript
35
- // messages.ts
36
- export default {
37
- UserProfile: {
38
- title: "Hello {firstName}",
39
- description:
40
- "Welcome back, {firstName}! You have {count, plural, =0 {no messages} one {# message} other {# messages}}.",
41
- status:
42
- "Your account is {status, select, active {active} inactive {inactive} pending {pending} other {unknown}}.",
43
- },
44
- Navigation: {
45
- home: "Home",
46
- about: "About",
47
- },
48
- } as const;
49
- ```
50
-
51
- You can also use JSON files, but additional steps are required for type-safe arguments with next-intl. See the [next-intl documentation](https://next-intl.dev/docs/workflows/typescript#messages-arguments) for details.
52
-
53
- ### 2) Configure Payload
54
-
55
- Add the plugin to your `payload.config.ts`:
56
-
57
- ```typescript
58
- import { buildConfig } from "payload";
59
- import { intlPlugin } from "payload-intl";
8
+ ## Overview
60
9
 
61
- import messages from "./messages";
10
+ Define your message keys as a typed schema using ICU MessageFormat syntax, then manage translations across locales in a rich admin UI with validation, autocompletion, and support for plurals, selects, dates, and tags. Messages can be fetched server-side or client-side.
62
11
 
63
- export default buildConfig({
64
- // the plugin reads locales from this config
65
- localization: {
66
- locales: ["en", "de", "fr"],
67
- defaultLocale: "en",
68
- },
69
- plugins: [
70
- // add the plugin
71
- intlPlugin({
72
- schema: messages,
73
- }),
74
- // add the "messages" collection to your storage adapter
75
- ],
76
- });
77
- ```
78
-
79
- ### 3) Fetch messages in your app
80
-
81
- **Node.js:**
82
-
83
- ```typescript
84
- import config from "@payload-config";
85
- import { getPayload } from "payload";
86
- import { fetchMessages } from "payload-intl/requests";
12
+ **Features**
87
13
 
88
- const payload = await getPayload({ config });
89
- const messages = await fetchMessages(payload, "en");
90
- ```
14
+ - **ICU MessageFormat** variables, plurals, selects, number/date/time formatting, and XML-like tags.
15
+ - **Schema-driven** define message keys and templates in a typed schema with automatic validation.
16
+ - **Rich editor UI** — message editor with variable chips, autocompletion, and inline ICU element editors.
17
+ - **JSON import** — bulk-import translations from JSON files directly in the admin UI.
18
+ - **Flexible fetching** — works with a Payload instance (server-side) or a config object (client-side API fetch).
91
19
 
92
- **Edge runtime:**
20
+ ## Installation
93
21
 
94
- ```typescript
95
- const response = await fetch(`${process.env.PAYLOAD_API_URL}/intl-plugin/en`);
96
- const messages = await response.json();
22
+ ```sh
23
+ pnpm add payload-intl
97
24
  ```
98
25
 
99
- ## Plugin Options
100
-
101
- The `intlPlugin` accepts the following configuration:
26
+ ## Usage
102
27
 
103
- | Option | Default | Description |
104
- | ----------------------- | ------------------------ | ------------------------------------------------------- |
105
- | `schema` | **Required** | Your messages schema definition |
106
- | `collectionSlug` | `"messages"` | Custom collection slug |
107
- | `editorAccess` | Authenticated users only | Access control for editing messages |
108
- | `hooks` | - | Collection hooks with and additional `afterUpdate` hook |
109
- | `tabs` | - | Enable tabbed interface |
110
-
111
- <!-- ## Storage Adapter Requirements
112
-
113
- The plugin creates a "messages" upload collection that stores translations as JSON files.
114
-
115
- You must ensure that the storage provider returns the direct URL to the uploaded files and read access is guaranteed. -->
116
-
117
- <!-- ## Message Schema Definition
118
-
119
- ### Message Descriptions
120
-
121
- Add context for editors using the syntax `"[Description] message"`:
122
-
123
- ```typescript
124
- export default {
125
- UserProfile: {
126
- title: "[Greeting shown at the top of user profile page] Hello {firstName}",
127
- description:
128
- "[Subtitle with user's name and message count] Welcome back, {firstName}! You have {count} new messages.",
129
- },
130
- } as const;
131
- ``` -->
132
-
133
- <!-- ### Rich Text Messages
134
-
135
- Use `"$RICH$"` as the message value to enable rich text editing. Note that rich text messages do not support ICU arguments.
136
-
137
- ```typescript
138
- export default {
139
- Content: {
140
- welcome: "$RICH$", // Rich text editor will be used
141
- terms: "$RICH$", // Rich text editor will be used
142
- },
143
- } as const;
144
- ``` -->
145
-
146
- ## Example Usage
147
-
148
- Here's a complete example showing how to integrate payload-intl using next-intl and S3 storage:
149
-
150
- ```typescript
28
+ ```ts
151
29
  // payload.config.ts
152
- import { s3Storage } from "@payloadcms/storage-s3";
153
- import { revalidateTag } from "next/cache";
154
- import { buildConfig } from "payload";
155
- import { intlPlugin } from "payload-intl";
156
-
157
- import { messages } from "./i18n/messages";
30
+ import { buildConfig } from 'payload';
31
+ import { intlPlugin } from 'payload-intl';
158
32
 
159
33
  export default buildConfig({
160
34
  localization: {
161
- locales: ["en", "de", "fr"],
162
- defaultLocale: "en",
35
+ locales: ['en', 'de', 'fr'],
36
+ defaultLocale: 'en',
163
37
  },
38
+ // ...
164
39
  plugins: [
165
40
  intlPlugin({
166
- schema: messages,
167
- hooks: {
168
- afterUpdate: () => revalidateTag("messages"), // or anything else you want
169
- },
170
- }),
171
- s3Storage({
172
- collections: {
173
- messages: {
174
- prefix: "messages", // or anything else you want
41
+ schema: {
42
+ common: {
43
+ greeting: '[Main greeting] Hello {name}!',
44
+ items: '{count, plural, one {# item} other {# items}}',
45
+ },
46
+ auth: {
47
+ login: 'Sign in',
48
+ logout: 'Sign out',
175
49
  },
176
50
  },
51
+ tabs: true,
177
52
  }),
178
53
  ],
179
54
  });
180
55
  ```
181
56
 
182
- ```typescript
183
- // i18n/messages.ts
184
- export const messages = {
185
- UserProfile: {
186
- title: "Hello {firstName}",
187
- description:
188
- "Welcome back, {firstName}! You have {count, plural, =0 {no messages} one {# message} other {# messages}}.",
189
- },
190
- // ...
191
- } as const;
192
- ```
57
+ Fetch messages in your application:
58
+
59
+ ```ts
60
+ import { fetchMessages } from 'payload-intl';
193
61
 
194
- ```typescript
195
- // i18n/global.ts
196
- import type messages from "./messages";
62
+ // Server-side — pass the Payload instance directly
63
+ const messages = await fetchMessages(payload, 'en');
197
64
 
198
- declare module "next-intl" {
199
- interface AppConfig {
200
- Messages: typeof messages;
201
- // ...
202
- }
203
- }
65
+ // Client-side — pass a config object to fetch from the REST API
66
+ const messages = await fetchMessages({ serverUrl: 'http://localhost:3000' }, 'en');
204
67
  ```
205
68
 
206
- ```typescript
207
- // i18n/request.ts
208
- import { getRequestConfig } from "next-intl/server";
69
+ ### Options
209
70
 
210
- import { fetchCachedMessages } from "./server/messages";
71
+ | Option | Type | Default | Description |
72
+ | --- | --- | --- | --- |
73
+ | `schema` | `MessagesSchema` | — | Required. Nested object defining message keys and ICU templates. Leaf values are ICU MessageFormat strings, optionally prefixed with a `[description]`. |
74
+ | `collectionSlug` | `CollectionSlug` | `'messages'` | Slug of the collection used to store translation files. |
75
+ | `editorAccess` | `(req: PayloadRequest) => boolean \| Promise<boolean>` | `(req) => req.user !== null` | Access control function that determines who can edit messages. |
76
+ | `hooks` | `MessagesHooks` | `{}` | Collection hooks. Extends Payload's collection hooks with an additional `afterUpdate` callback fired when translations are saved. |
77
+ | `tabs` | `boolean` | `false` | When enabled, top-level keys in the schema are rendered as tabs in the admin UI. |
211
78
 
212
- export default getRequestConfig(async ({ locale }) => {
213
- const messages = await fetchCachedMessages(locale);
79
+ ## Contributing
214
80
 
215
- return {
216
- locale,
217
- messages,
218
- };
219
- });
220
- ```
81
+ This plugin lives in the [payload-plugins](https://github.com/davincicoding-org/payload-plugins) monorepo.
221
82
 
222
- ```typescript
223
- // server.ts
224
- "use server";
83
+ ### Development
225
84
 
226
- import config from "@payload-config";
227
- import { unstable_cache } from "next/cache";
228
- import { getPayload } from "payload";
229
- import { fetchMessages } from "payload-intl";
85
+ ```sh
86
+ pnpm install
230
87
 
231
- export const fetchCachedMessages = unstable_cache(
232
- async (locale: string) => {
233
- // Node.js
234
- const payload = await getPayload({ config });
235
- return await fetchMessages(payload, locale);
88
+ # watch this plugin for changes
89
+ pnpm --filter payload-intl dev
236
90
 
237
- // Edge runtime
238
- const response = await fetch(
239
- `${process.env.PAYLOAD_API_URL}/intl-plugin/en`,
240
- );
241
- return await response.json();
242
- },
243
- ["messages"],
244
- {
245
- revalidate: false,
246
- },
247
- );
91
+ # run the Payload dev app (in a second terminal)
92
+ pnpm --filter sandbox dev
248
93
  ```
249
94
 
250
- ## Development
95
+ The `sandbox/` directory is a Next.js + Payload app that imports plugins via `workspace:*` — use it to test changes locally.
251
96
 
252
- ```bash
253
- # Install dependencies
254
- pnpm install
97
+ ### Code quality
255
98
 
256
- # Start development server
257
- pnpm dev
99
+ - **Formatting & linting** — handled by [Biome](https://biomejs.dev/), enforced on commit via husky + lint-staged.
100
+ - **Commits** — must follow [Conventional Commits](https://www.conventionalcommits.org/) with a valid scope (e.g. `fix(payload-intl): ...`).
101
+ - **Changesets** — please include a [changeset](https://github.com/changesets/changesets) in your PR by running `pnpm release`.
258
102
 
259
- # Build the plugin
260
- pnpm build
103
+ ### Issues & PRs
261
104
 
262
- # Run tests
263
- pnpm test
264
- ```
105
+ Bug reports and feature requests are welcome — [open an issue](https://github.com/davincicoding-org/payload-plugins/issues).
265
106
 
266
107
  ## License
267
108
 
@@ -0,0 +1,17 @@
1
+ import type { CollectionConfig, CollectionSlug, TypeWithID } from 'payload';
2
+ import z from 'zod';
3
+ export declare const entityIdSchema: z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>;
4
+ export type EntityID = TypeWithID['id'] & z.infer<typeof entityIdSchema>;
5
+ export declare const isPopulated: <T extends TypeWithID>(relationship: T | EntityID) => relationship is T;
6
+ export declare function assertPopulated<T extends TypeWithID | null>(docsOrIds: (T | EntityID)[], errorMessage?: (id: EntityID) => string): T[];
7
+ export declare function assertPopulated<T extends TypeWithID | null>(docOrId: T | EntityID, errorMessage?: (id: EntityID) => string): T;
8
+ export declare const createCollectionConfigFactory: <T extends Record<string, unknown>>(factory: Omit<CollectionConfig, "slug"> | ((options: T & {
9
+ slug: CollectionSlug;
10
+ }) => Omit<CollectionConfig, "slug">)) => (options: T & {
11
+ slug: CollectionSlug;
12
+ }) => CollectionConfig;
13
+ export declare const resolveForeignKey: (entity: TypeWithID["id"] | TypeWithID) => string | number;
14
+ export { defineProcedure, type Procedure, type ProcedureBuilder, } from './procedure';
15
+ export { getAdminURL, getApiURL, getServerURL } from './urls';
16
+ export { findFields, uncaughtSwitchCase } from './utils';
17
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/_common/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAC5E,OAAO,CAAC,MAAM,KAAK,CAAC;AAIpB,eAAO,MAAM,cAAc,iDAAoC,CAAC;AAChE,MAAM,MAAM,QAAQ,GAAG,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC;AAIzE,eAAO,MAAM,WAAW,GAAI,CAAC,SAAS,UAAU,gBAChC,CAAC,GAAG,QAAQ,KACzB,YAAY,IAAI,CAAqC,CAAC;AAEzD,wBAAgB,eAAe,CAAC,CAAC,SAAS,UAAU,GAAG,IAAI,EACzD,SAAS,EAAE,CAAC,CAAC,GAAG,QAAQ,CAAC,EAAE,EAC3B,YAAY,CAAC,EAAE,CAAC,EAAE,EAAE,QAAQ,KAAK,MAAM,GACtC,CAAC,EAAE,CAAC;AACP,wBAAgB,eAAe,CAAC,CAAC,SAAS,UAAU,GAAG,IAAI,EACzD,OAAO,EAAE,CAAC,GAAG,QAAQ,EACrB,YAAY,CAAC,EAAE,CAAC,EAAE,EAAE,QAAQ,KAAK,MAAM,GACtC,CAAC,CAAC;AAeL,eAAO,MAAM,6BAA6B,GACvC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,WAE5B,IAAI,CAAC,gBAAgB,EAAE,MAAM,CAAC,GAC9B,CAAC,CACC,OAAO,EAAE,CAAC,GAAG;IAAE,IAAI,EAAE,cAAc,CAAA;CAAE,KAClC,IAAI,CAAC,gBAAgB,EAAE,MAAM,CAAC,CAAC,eAEhC,CAAC,GAAG;IAAE,IAAI,EAAE,cAAc,CAAA;CAAE,KAAG,gBAGvC,CAAC;AAEL,eAAO,MAAM,iBAAiB,WAAY,UAAU,CAAC,IAAI,CAAC,GAAG,UAAU,oBACtB,CAAC;AAElD,OAAO,EACL,eAAe,EACf,KAAK,SAAS,EACd,KAAK,gBAAgB,GACtB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAC9D,OAAO,EAAE,UAAU,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC"}
@@ -0,0 +1,27 @@
1
+ import z from 'zod';
2
+ // MARK: Types
3
+ export const entityIdSchema = z.union([
4
+ z.number(),
5
+ z.string()
6
+ ]);
7
+ // MARK: Type Guards
8
+ export const isPopulated = (relationship)=>typeof relationship === 'object';
9
+ export function assertPopulated(value, errorMessage = (id)=>`Doc is not populated: [${id}]`) {
10
+ if (value === null) return value;
11
+ if (Array.isArray(value)) {
12
+ return value.map((item)=>assertPopulated(item, errorMessage));
13
+ }
14
+ if (isPopulated(value)) return value;
15
+ throw new Error(errorMessage(value));
16
+ }
17
+ // MARK: Utilities
18
+ export const createCollectionConfigFactory = (factory)=>(options)=>({
19
+ slug: options.slug,
20
+ ...typeof factory === 'function' ? factory(options) : factory
21
+ });
22
+ export const resolveForeignKey = (entity)=>typeof entity === 'object' ? entity.id : entity;
23
+ export { defineProcedure } from './procedure';
24
+ export { getAdminURL, getApiURL, getServerURL } from './urls';
25
+ export { findFields, uncaughtSwitchCase } from './utils';
26
+
27
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/_common/index.ts"],"sourcesContent":["import type { CollectionConfig, CollectionSlug, TypeWithID } from 'payload';\nimport z from 'zod';\n\n// MARK: Types\n\nexport const entityIdSchema = z.union([z.number(), z.string()]);\nexport type EntityID = TypeWithID['id'] & z.infer<typeof entityIdSchema>;\n\n// MARK: Type Guards\n\nexport const isPopulated = <T extends TypeWithID>(\n relationship: T | EntityID,\n): relationship is T => typeof relationship === 'object';\n\nexport function assertPopulated<T extends TypeWithID | null>(\n docsOrIds: (T | EntityID)[],\n errorMessage?: (id: EntityID) => string,\n): T[];\nexport function assertPopulated<T extends TypeWithID | null>(\n docOrId: T | EntityID,\n errorMessage?: (id: EntityID) => string,\n): T;\nexport function assertPopulated<T extends TypeWithID | null>(\n value: T | EntityID | (T | EntityID)[],\n errorMessage = (id: EntityID) => `Doc is not populated: [${id}]`,\n): T | T[] {\n if (value === null) return value;\n if (Array.isArray(value)) {\n return value.map((item) => assertPopulated(item, errorMessage));\n }\n if (isPopulated(value)) return value;\n throw new Error(errorMessage(value as EntityID));\n}\n\n// MARK: Utilities\n\nexport const createCollectionConfigFactory =\n <T extends Record<string, unknown>>(\n factory:\n | Omit<CollectionConfig, 'slug'>\n | ((\n options: T & { slug: CollectionSlug },\n ) => Omit<CollectionConfig, 'slug'>),\n ) =>\n (options: T & { slug: CollectionSlug }): CollectionConfig => ({\n slug: options.slug,\n ...(typeof factory === 'function' ? factory(options) : factory),\n });\n\nexport const resolveForeignKey = (entity: TypeWithID['id'] | TypeWithID) =>\n typeof entity === 'object' ? entity.id : entity;\n\nexport {\n defineProcedure,\n type Procedure,\n type ProcedureBuilder,\n} from './procedure';\nexport { getAdminURL, getApiURL, getServerURL } from './urls';\nexport { findFields, uncaughtSwitchCase } from './utils';\n"],"names":["z","entityIdSchema","union","number","string","isPopulated","relationship","assertPopulated","value","errorMessage","id","Array","isArray","map","item","Error","createCollectionConfigFactory","factory","options","slug","resolveForeignKey","entity","defineProcedure","getAdminURL","getApiURL","getServerURL","findFields","uncaughtSwitchCase"],"mappings":"AACA,OAAOA,OAAO,MAAM;AAEpB,cAAc;AAEd,OAAO,MAAMC,iBAAiBD,EAAEE,KAAK,CAAC;IAACF,EAAEG,MAAM;IAAIH,EAAEI,MAAM;CAAG,EAAE;AAGhE,oBAAoB;AAEpB,OAAO,MAAMC,cAAc,CACzBC,eACsB,OAAOA,iBAAiB,SAAS;AAUzD,OAAO,SAASC,gBACdC,KAAsC,EACtCC,eAAe,CAACC,KAAiB,CAAC,uBAAuB,EAAEA,GAAG,CAAC,CAAC;IAEhE,IAAIF,UAAU,MAAM,OAAOA;IAC3B,IAAIG,MAAMC,OAAO,CAACJ,QAAQ;QACxB,OAAOA,MAAMK,GAAG,CAAC,CAACC,OAASP,gBAAgBO,MAAML;IACnD;IACA,IAAIJ,YAAYG,QAAQ,OAAOA;IAC/B,MAAM,IAAIO,MAAMN,aAAaD;AAC/B;AAEA,kBAAkB;AAElB,OAAO,MAAMQ,gCACX,CACEC,UAMF,CAACC,UAA6D,CAAA;YAC5DC,MAAMD,QAAQC,IAAI;YAClB,GAAI,OAAOF,YAAY,aAAaA,QAAQC,WAAWD,OAAO;QAChE,CAAA,EAAG;AAEL,OAAO,MAAMG,oBAAoB,CAACC,SAChC,OAAOA,WAAW,WAAWA,OAAOX,EAAE,GAAGW,OAAO;AAElD,SACEC,eAAe,QAGV,cAAc;AACrB,SAASC,WAAW,EAAEC,SAAS,EAAEC,YAAY,QAAQ,SAAS;AAC9D,SAASC,UAAU,EAAEC,kBAAkB,QAAQ,UAAU"}
@@ -0,0 +1,34 @@
1
+ import type { Endpoint, PayloadRequest } from 'payload';
2
+ type Method = 'get' | 'post' | 'put' | 'patch' | 'delete';
3
+ /** Any Zod-like schema with safeParse and inferred output */
4
+ interface ZodLike<TOutput = unknown> {
5
+ safeParse(data: unknown): {
6
+ success: true;
7
+ data: TOutput;
8
+ } | {
9
+ success: false;
10
+ error: unknown;
11
+ };
12
+ }
13
+ type InferOutput<T> = T extends ZodLike<infer O> ? O : never;
14
+ interface ProcedureConfig<TSchema extends ZodLike | undefined = undefined> {
15
+ path: `/${string}`;
16
+ method: Method;
17
+ input?: TSchema;
18
+ }
19
+ export interface Procedure<TInput, TOutput> {
20
+ path: `/${string}`;
21
+ method: Method;
22
+ endpoint(handler: (req: PayloadRequest, ...args: TInput extends void ? [] : [input: TInput]) => Promise<unknown | Response>): Endpoint;
23
+ call(apiUrl: string, ...args: TInput extends void ? [] : [input: TInput]): Promise<TOutput>;
24
+ }
25
+ export interface ProcedureBuilder<TInput> {
26
+ path: string;
27
+ method: Method;
28
+ returns<TOutput>(): Procedure<TInput, TOutput>;
29
+ endpoint(handler: (req: PayloadRequest, ...args: TInput extends void ? [] : [input: TInput]) => Promise<unknown | Response>): Endpoint;
30
+ call(apiUrl: string, ...args: TInput extends void ? [] : [input: TInput]): Promise<unknown>;
31
+ }
32
+ export declare function defineProcedure<TSchema extends ZodLike | undefined = undefined>(config: ProcedureConfig<TSchema>): ProcedureBuilder<TSchema extends ZodLike ? InferOutput<TSchema> : void>;
33
+ export {};
34
+ //# sourceMappingURL=procedure.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"procedure.d.ts","sourceRoot":"","sources":["../../src/_common/procedure.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAExD,KAAK,MAAM,GAAG,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,OAAO,GAAG,QAAQ,CAAC;AAE1D,6DAA6D;AAC7D,UAAU,OAAO,CAAC,OAAO,GAAG,OAAO;IACjC,SAAS,CACP,IAAI,EAAE,OAAO,GACZ;QAAE,OAAO,EAAE,IAAI,CAAC;QAAC,IAAI,EAAE,OAAO,CAAA;KAAE,GAAG;QAAE,OAAO,EAAE,KAAK,CAAC;QAAC,KAAK,EAAE,OAAO,CAAA;KAAE,CAAC;CAC1E;AAED,KAAK,WAAW,CAAC,CAAC,IAAI,CAAC,SAAS,OAAO,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;AAE7D,UAAU,eAAe,CAAC,OAAO,SAAS,OAAO,GAAG,SAAS,GAAG,SAAS;IACvE,IAAI,EAAE,IAAI,MAAM,EAAE,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,SAAS,CAAC,MAAM,EAAE,OAAO;IACxC,IAAI,EAAE,IAAI,MAAM,EAAE,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CACN,OAAO,EAAE,CACP,GAAG,EAAE,cAAc,EACnB,GAAG,IAAI,EAAE,MAAM,SAAS,IAAI,GAAG,EAAE,GAAG,CAAC,KAAK,EAAE,MAAM,CAAC,KAChD,OAAO,CAAC,OAAO,GAAG,QAAQ,CAAC,GAC/B,QAAQ,CAAC;IACZ,IAAI,CACF,MAAM,EAAE,MAAM,EACd,GAAG,IAAI,EAAE,MAAM,SAAS,IAAI,GAAG,EAAE,GAAG,CAAC,KAAK,EAAE,MAAM,CAAC,GAClD,OAAO,CAAC,OAAO,CAAC,CAAC;CACrB;AAED,MAAM,WAAW,gBAAgB,CAAC,MAAM;IACtC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,OAAO,KAAK,SAAS,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/C,QAAQ,CACN,OAAO,EAAE,CACP,GAAG,EAAE,cAAc,EACnB,GAAG,IAAI,EAAE,MAAM,SAAS,IAAI,GAAG,EAAE,GAAG,CAAC,KAAK,EAAE,MAAM,CAAC,KAChD,OAAO,CAAC,OAAO,GAAG,QAAQ,CAAC,GAC/B,QAAQ,CAAC;IACZ,IAAI,CACF,MAAM,EAAE,MAAM,EACd,GAAG,IAAI,EAAE,MAAM,SAAS,IAAI,GAAG,EAAE,GAAG,CAAC,KAAK,EAAE,MAAM,CAAC,GAClD,OAAO,CAAC,OAAO,CAAC,CAAC;CACrB;AA2GD,wBAAgB,eAAe,CAC7B,OAAO,SAAS,OAAO,GAAG,SAAS,GAAG,SAAS,EAE/C,MAAM,EAAE,eAAe,CAAC,OAAO,CAAC,GAC/B,gBAAgB,CAAC,OAAO,SAAS,OAAO,GAAG,WAAW,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC,CAazE"}
@@ -0,0 +1,110 @@
1
+ function wrapOutput(output) {
2
+ if (output instanceof Response) return output;
3
+ return Response.json(output);
4
+ }
5
+ function createProcedure(config, inputSchema) {
6
+ return {
7
+ path: config.path,
8
+ method: config.method,
9
+ endpoint (handler) {
10
+ return {
11
+ path: config.path,
12
+ method: config.method,
13
+ handler: async (req)=>{
14
+ if (inputSchema) {
15
+ if (config.method === 'get') {
16
+ const routeParams = req.routeParams ?? {};
17
+ const searchParams = req.searchParams ? Object.fromEntries(req.searchParams.entries()) : {};
18
+ const merged = {
19
+ ...searchParams,
20
+ ...routeParams
21
+ };
22
+ const result = inputSchema.safeParse(merged);
23
+ if (!result.success) {
24
+ return Response.json({
25
+ error: result.error
26
+ }, {
27
+ status: 400
28
+ });
29
+ }
30
+ // biome-ignore lint/complexity/noBannedTypes: ugly type cast
31
+ const output = await handler(req, result.data);
32
+ return wrapOutput(output);
33
+ }
34
+ const { addDataAndFileToRequest } = await import(/* webpackIgnore: true */ 'payload');
35
+ await addDataAndFileToRequest(req);
36
+ const result = inputSchema.safeParse(req.data);
37
+ if (!result.success) {
38
+ return Response.json({
39
+ error: result.error
40
+ }, {
41
+ status: 400
42
+ });
43
+ }
44
+ // biome-ignore lint/complexity/noBannedTypes: ugly type cast
45
+ const output = await handler(req, result.data);
46
+ return wrapOutput(output);
47
+ }
48
+ // biome-ignore lint/complexity/noBannedTypes: ugly type cast
49
+ const output = await handler(req);
50
+ return wrapOutput(output);
51
+ }
52
+ };
53
+ },
54
+ call (apiUrl, ...args) {
55
+ const input = args[0];
56
+ if (config.method === 'get') {
57
+ let resolvedPath = config.path;
58
+ const queryParams = {};
59
+ if (input) {
60
+ for (const [key, value] of Object.entries(input)){
61
+ if (resolvedPath.includes(`:${key}`)) {
62
+ resolvedPath = resolvedPath.replace(`:${key}`, encodeURIComponent(String(value)));
63
+ } else {
64
+ queryParams[key] = String(value);
65
+ }
66
+ }
67
+ }
68
+ const queryString = new URLSearchParams(queryParams).toString();
69
+ const url = `${apiUrl}${resolvedPath}${queryString ? `?${queryString}` : ''}`;
70
+ return fetch(url, {
71
+ method: 'GET',
72
+ credentials: 'include'
73
+ }).then(async (response)=>{
74
+ if (!response.ok) {
75
+ throw new Error(`Request failed: ${response.status} ${response.statusText}`);
76
+ }
77
+ return response.json();
78
+ });
79
+ }
80
+ const url = `${apiUrl}${config.path}`;
81
+ return fetch(url, {
82
+ method: config.method.toUpperCase(),
83
+ credentials: 'include',
84
+ headers: {
85
+ 'Content-Type': 'application/json'
86
+ },
87
+ body: input ? JSON.stringify(input) : undefined
88
+ }).then(async (response)=>{
89
+ if (!response.ok) {
90
+ throw new Error(`Request failed: ${response.status} ${response.statusText}`);
91
+ }
92
+ return response.json();
93
+ });
94
+ }
95
+ };
96
+ }
97
+ export function defineProcedure(config) {
98
+ const proc = createProcedure(config, config.input);
99
+ return {
100
+ path: config.path,
101
+ method: config.method,
102
+ returns () {
103
+ return createProcedure(config, config.input);
104
+ },
105
+ endpoint: proc.endpoint,
106
+ call: proc.call
107
+ };
108
+ }
109
+
110
+ //# sourceMappingURL=procedure.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/_common/procedure.ts"],"sourcesContent":["import type { Endpoint, PayloadRequest } from 'payload';\n\ntype Method = 'get' | 'post' | 'put' | 'patch' | 'delete';\n\n/** Any Zod-like schema with safeParse and inferred output */\ninterface ZodLike<TOutput = unknown> {\n safeParse(\n data: unknown,\n ): { success: true; data: TOutput } | { success: false; error: unknown };\n}\n\ntype InferOutput<T> = T extends ZodLike<infer O> ? O : never;\n\ninterface ProcedureConfig<TSchema extends ZodLike | undefined = undefined> {\n path: `/${string}`;\n method: Method;\n input?: TSchema;\n}\n\nexport interface Procedure<TInput, TOutput> {\n path: `/${string}`;\n method: Method;\n endpoint(\n handler: (\n req: PayloadRequest,\n ...args: TInput extends void ? [] : [input: TInput]\n ) => Promise<unknown | Response>,\n ): Endpoint;\n call(\n apiUrl: string,\n ...args: TInput extends void ? [] : [input: TInput]\n ): Promise<TOutput>;\n}\n\nexport interface ProcedureBuilder<TInput> {\n path: string;\n method: Method;\n returns<TOutput>(): Procedure<TInput, TOutput>;\n endpoint(\n handler: (\n req: PayloadRequest,\n ...args: TInput extends void ? [] : [input: TInput]\n ) => Promise<unknown | Response>,\n ): Endpoint;\n call(\n apiUrl: string,\n ...args: TInput extends void ? [] : [input: TInput]\n ): Promise<unknown>;\n}\n\nfunction wrapOutput(output: unknown): Response {\n if (output instanceof Response) return output;\n return Response.json(output);\n}\n\nfunction createProcedure<TInput, TOutput>(\n config: ProcedureConfig<ZodLike | undefined>,\n inputSchema: ZodLike | undefined,\n): Procedure<TInput, TOutput> {\n return {\n path: config.path,\n method: config.method,\n endpoint(handler) {\n return {\n path: config.path,\n method: config.method,\n handler: async (req) => {\n if (inputSchema) {\n if (config.method === 'get') {\n const routeParams = req.routeParams ?? {};\n const searchParams = req.searchParams\n ? Object.fromEntries(req.searchParams.entries())\n : {};\n const merged = { ...searchParams, ...routeParams };\n const result = inputSchema.safeParse(merged);\n if (!result.success) {\n return Response.json({ error: result.error }, { status: 400 });\n }\n // biome-ignore lint/complexity/noBannedTypes: ugly type cast\n const output = await (handler as Function)(req, result.data);\n return wrapOutput(output);\n }\n\n const { addDataAndFileToRequest } = await import(\n /* webpackIgnore: true */ 'payload'\n );\n await addDataAndFileToRequest(req);\n const result = inputSchema.safeParse(req.data);\n if (!result.success) {\n return Response.json({ error: result.error }, { status: 400 });\n }\n // biome-ignore lint/complexity/noBannedTypes: ugly type cast\n const output = await (handler as Function)(req, result.data);\n return wrapOutput(output);\n }\n // biome-ignore lint/complexity/noBannedTypes: ugly type cast\n const output = await (handler as Function)(req);\n return wrapOutput(output);\n },\n };\n },\n call(apiUrl, ...args) {\n const input = args[0] as Record<string, unknown> | undefined;\n\n if (config.method === 'get') {\n let resolvedPath = config.path;\n const queryParams: Record<string, string> = {};\n\n if (input) {\n for (const [key, value] of Object.entries(input)) {\n if (resolvedPath.includes(`:${key}`)) {\n resolvedPath = resolvedPath.replace(\n `:${key}`,\n encodeURIComponent(String(value)),\n ) as `/${string}`;\n } else {\n queryParams[key] = String(value);\n }\n }\n }\n\n const queryString = new URLSearchParams(queryParams).toString();\n const url = `${apiUrl}${resolvedPath}${queryString ? `?${queryString}` : ''}`;\n\n return fetch(url, {\n method: 'GET',\n credentials: 'include',\n }).then(async (response) => {\n if (!response.ok) {\n throw new Error(\n `Request failed: ${response.status} ${response.statusText}`,\n );\n }\n return response.json();\n }) as Promise<TOutput>;\n }\n\n const url = `${apiUrl}${config.path}`;\n return fetch(url, {\n method: config.method.toUpperCase(),\n credentials: 'include',\n headers: { 'Content-Type': 'application/json' },\n body: input ? JSON.stringify(input) : undefined,\n }).then(async (response) => {\n if (!response.ok) {\n throw new Error(\n `Request failed: ${response.status} ${response.statusText}`,\n );\n }\n return response.json();\n }) as Promise<TOutput>;\n },\n };\n}\n\nexport function defineProcedure<\n TSchema extends ZodLike | undefined = undefined,\n>(\n config: ProcedureConfig<TSchema>,\n): ProcedureBuilder<TSchema extends ZodLike ? InferOutput<TSchema> : void> {\n type TInput = TSchema extends ZodLike ? InferOutput<TSchema> : undefined;\n const proc = createProcedure<TInput, unknown>(config, config.input);\n\n return {\n path: config.path,\n method: config.method,\n returns<TOutput>(): Procedure<TInput, TOutput> {\n return createProcedure<TInput, TOutput>(config, config.input);\n },\n endpoint: proc.endpoint as unknown as ProcedureBuilder<TInput>['endpoint'],\n call: proc.call as ProcedureBuilder<TInput>['call'],\n };\n}\n"],"names":["wrapOutput","output","Response","json","createProcedure","config","inputSchema","path","method","endpoint","handler","req","routeParams","searchParams","Object","fromEntries","entries","merged","result","safeParse","success","error","status","data","addDataAndFileToRequest","call","apiUrl","args","input","resolvedPath","queryParams","key","value","includes","replace","encodeURIComponent","String","queryString","URLSearchParams","toString","url","fetch","credentials","then","response","ok","Error","statusText","toUpperCase","headers","body","JSON","stringify","undefined","defineProcedure","proc","returns"],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ import type { PayloadRequest } from 'payload';
2
+ export declare function getServerURL(req: PayloadRequest): string;
3
+ export declare function getAdminURL({ req, path, }: {
4
+ req: PayloadRequest;
5
+ path?: '' | `/${string}` | null;
6
+ }): string;
7
+ export declare function getApiURL({ req, path, }: {
8
+ req: PayloadRequest;
9
+ path?: '' | `/${string}` | null;
10
+ }): string;
11
+ //# sourceMappingURL=urls.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"urls.d.ts","sourceRoot":"","sources":["../../src/_common/urls.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAG9C,wBAAgB,YAAY,CAAC,GAAG,EAAE,cAAc,GAAG,MAAM,CAWxD;AAED,wBAAgB,WAAW,CAAC,EAC1B,GAAG,EACH,IAAI,GACL,EAAE;IACD,GAAG,EAAE,cAAc,CAAC;IACpB,IAAI,CAAC,EAAE,EAAE,GAAG,IAAI,MAAM,EAAE,GAAG,IAAI,CAAC;CACjC,GAAG,MAAM,CAMT;AAED,wBAAgB,SAAS,CAAC,EACxB,GAAG,EACH,IAAI,GACL,EAAE;IACD,GAAG,EAAE,cAAc,CAAC;IACpB,IAAI,CAAC,EAAE,EAAE,GAAG,IAAI,MAAM,EAAE,GAAG,IAAI,CAAC;CACjC,GAAG,MAAM,CAMT"}
@@ -0,0 +1,23 @@
1
+ import { formatAdminURL } from 'payload/shared';
2
+ export function getServerURL(req) {
3
+ if (!req.url) throw new Error('Could not get serverURL, since request URL is not available');
4
+ const { config } = req.payload;
5
+ if (config.serverURL) return config.serverURL;
6
+ return `${new URL(req.url).protocol}//${req.headers.get('host')}`;
7
+ }
8
+ export function getAdminURL({ req, path }) {
9
+ return formatAdminURL({
10
+ adminRoute: req.payload.config.routes.admin,
11
+ serverURL: getServerURL(req),
12
+ path
13
+ });
14
+ }
15
+ export function getApiURL({ req, path }) {
16
+ return formatAdminURL({
17
+ apiRoute: req.payload.config.routes.api,
18
+ serverURL: getServerURL(req),
19
+ path
20
+ });
21
+ }
22
+
23
+ //# sourceMappingURL=urls.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/_common/urls.ts"],"sourcesContent":["import type { PayloadRequest } from 'payload';\nimport { formatAdminURL } from 'payload/shared';\n\nexport function getServerURL(req: PayloadRequest): string {\n if (!req.url)\n throw new Error(\n 'Could not get serverURL, since request URL is not available',\n );\n\n const { config } = req.payload;\n\n if (config.serverURL) return config.serverURL;\n\n return `${new URL(req.url).protocol}//${req.headers.get('host')}`;\n}\n\nexport function getAdminURL({\n req,\n path,\n}: {\n req: PayloadRequest;\n path?: '' | `/${string}` | null;\n}): string {\n return formatAdminURL({\n adminRoute: req.payload.config.routes.admin,\n serverURL: getServerURL(req),\n path,\n });\n}\n\nexport function getApiURL({\n req,\n path,\n}: {\n req: PayloadRequest;\n path?: '' | `/${string}` | null;\n}): string {\n return formatAdminURL({\n apiRoute: req.payload.config.routes.api,\n serverURL: getServerURL(req),\n path,\n });\n}\n"],"names":["formatAdminURL","getServerURL","req","url","Error","config","payload","serverURL","URL","protocol","headers","get","getAdminURL","path","adminRoute","routes","admin","getApiURL","apiRoute","api"],"mappings":"AACA,SAASA,cAAc,QAAQ,iBAAiB;AAEhD,OAAO,SAASC,aAAaC,GAAmB;IAC9C,IAAI,CAACA,IAAIC,GAAG,EACV,MAAM,IAAIC,MACR;IAGJ,MAAM,EAAEC,MAAM,EAAE,GAAGH,IAAII,OAAO;IAE9B,IAAID,OAAOE,SAAS,EAAE,OAAOF,OAAOE,SAAS;IAE7C,OAAO,GAAG,IAAIC,IAAIN,IAAIC,GAAG,EAAEM,QAAQ,CAAC,EAAE,EAAEP,IAAIQ,OAAO,CAACC,GAAG,CAAC,SAAS;AACnE;AAEA,OAAO,SAASC,YAAY,EAC1BV,GAAG,EACHW,IAAI,EAIL;IACC,OAAOb,eAAe;QACpBc,YAAYZ,IAAII,OAAO,CAACD,MAAM,CAACU,MAAM,CAACC,KAAK;QAC3CT,WAAWN,aAAaC;QACxBW;IACF;AACF;AAEA,OAAO,SAASI,UAAU,EACxBf,GAAG,EACHW,IAAI,EAIL;IACC,OAAOb,eAAe;QACpBkB,UAAUhB,IAAII,OAAO,CAACD,MAAM,CAACU,MAAM,CAACI,GAAG;QACvCZ,WAAWN,aAAaC;QACxBW;IACF;AACF"}
@@ -0,0 +1,5 @@
1
+ import type { Field } from 'payload';
2
+ export declare const uncaughtSwitchCase: (value: never) => never;
3
+ export declare function findFields<T extends Field>(fields: Field[], condition: (field: Field) => field is T): T[];
4
+ export declare function findFields(fields: Field[], condition: (field: Field) => boolean): Field[];
5
+ //# sourceMappingURL=utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/_common/utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAErC,eAAO,MAAM,kBAAkB,UAAW,KAAK,UAE9C,CAAC;AAEF,wBAAgB,UAAU,CAAC,CAAC,SAAS,KAAK,EACxC,MAAM,EAAE,KAAK,EAAE,EACf,SAAS,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,KAAK,IAAI,CAAC,GACtC,CAAC,EAAE,CAAC;AACP,wBAAgB,UAAU,CACxB,MAAM,EAAE,KAAK,EAAE,EACf,SAAS,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,OAAO,GACnC,KAAK,EAAE,CAAC"}