Compare commits
49
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a695ec7319 | ||
|
|
c6730335ef | ||
|
|
247b849759 | ||
|
|
0dee178ded | ||
|
|
9359f26788 | ||
|
|
5630765215 | ||
|
|
ed4a78b109 | ||
|
|
9acb22e2f9 | ||
|
|
6a8361d710 | ||
|
|
502ffee31f | ||
|
|
5a322230ae | ||
|
|
f919c288b2 | ||
|
|
21b948a4f8 | ||
|
|
d04271f942 | ||
|
|
4f08e9ea4d | ||
|
|
67347f1e34 | ||
|
|
75f2b6400d | ||
|
|
26aec1c5af | ||
|
|
0fe7ca0575 | ||
|
|
d745a764fe | ||
|
|
6c273118a2 | ||
|
|
a010a7368a | ||
|
|
08bd00f01f | ||
|
|
cef7f46718 | ||
|
|
ac48f1e9d1 | ||
|
|
3d8bc9019f | ||
|
|
cd65c2799f | ||
|
|
282be290cd | ||
|
|
080f41c7d8 | ||
|
|
37e417e057 | ||
|
|
05b3dc8cb1 | ||
|
|
81fa409513 | ||
|
|
90068c7952 | ||
|
|
2215766940 | ||
|
|
5aa66ff37a | ||
|
|
eb043bba4a | ||
|
|
68560534ac | ||
|
|
657f50a135 | ||
|
|
720bf6914b | ||
|
|
5f76050fa8 | ||
|
|
4bf50514bf | ||
|
|
195d4169f5 | ||
|
|
4eedc1f642 | ||
|
|
f7d5e79d27 | ||
|
|
e76692c9ba | ||
|
|
90cbc4370f | ||
|
|
9a45b3a50f | ||
|
|
05150e7ece | ||
|
|
dd97bb67fe |
@@ -18,7 +18,6 @@
|
|||||||
|
|
||||||
# production
|
# production
|
||||||
/build
|
/build
|
||||||
/dist
|
|
||||||
|
|
||||||
# misc
|
# misc
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
|||||||
@@ -11,60 +11,23 @@ blog/archive system (collections under an archive page, listing, pagination).
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Before you start
|
|
||||||
|
|
||||||
The `git.intecion.net` server is internal — you'll only see the code and the
|
|
||||||
package once you're signed in to your Intecion account. Every installation method
|
|
||||||
needs your personal access token. Without it you'll get a 404 or an
|
|
||||||
"Unauthorized" error.
|
|
||||||
|
|
||||||
You generate the token once. It's tied to your account — don't ask anyone for
|
|
||||||
theirs, and don't put it in any file that ends up in a repository.
|
|
||||||
|
|
||||||
### Generate a token
|
|
||||||
|
|
||||||
1. Sign in to Gitea → click your avatar → **Settings**.
|
|
||||||
2. **Applications → Generate New Token**.
|
|
||||||
3. Name it (e.g. `ipal-kit-install`) and select the scope:
|
|
||||||
- `read:package` — if you only install the plugin in projects,
|
|
||||||
- `write:package` — additionally, if you'll publish new versions.
|
|
||||||
4. Click **Generate**. Copy the token now — Gitea shows it only once.
|
|
||||||
|
|
||||||
### Store the token in your environment
|
|
||||||
|
|
||||||
Don't paste the token straight into project files. Keep it in an environment
|
|
||||||
variable, so your install config carries no secret.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# add to ~/.zshrc (or ~/.bashrc)
|
|
||||||
export GITEA_TOKEN=paste_your_token_here
|
|
||||||
```
|
|
||||||
|
|
||||||
Reload your shell (`source ~/.zshrc`) or open a new terminal window.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
There are two routes. Pick **Route A** if you just want to use the plugin in a
|
The plugin is published to a **public package registry** on the company Gitea.
|
||||||
project — it's faster and versioned. Choose **Route B** only when you need a
|
Installing it needs no token — just point the `@intecion` scope at the registry.
|
||||||
specific, unreleased commit straight from the repository.
|
|
||||||
|
|
||||||
### Route A — from the registry (recommended)
|
**1. Point the scope at the registry.** Add this line to an `.npmrc` file in your
|
||||||
|
project (or to `~/.npmrc` to apply it everywhere):
|
||||||
The plugin is published to the package registry in Gitea. You pull a ready-built
|
|
||||||
package and build nothing locally.
|
|
||||||
|
|
||||||
**1. Configure the registry.** Create an `.npmrc` file in your project directory
|
|
||||||
(or add these two lines to `~/.npmrc` to make it work globally):
|
|
||||||
|
|
||||||
```
|
```
|
||||||
@intecion:registry=https://git.intecion.net/api/packages/IntecionSoftware/npm/
|
@intecion:registry=https://git.intecion.net/api/packages/IntecionSoftware/npm/
|
||||||
//git.intecion.net/api/packages/IntecionSoftware/npm/:_authToken=${GITEA_TOKEN}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Because the token lives in the `${GITEA_TOKEN}` variable, this file holds no
|
Only packages starting with `@intecion/` go to Gitea. Everything else
|
||||||
secret — you can safely commit it.
|
(`react`, `next`, `@payloadcms/*`, …) still comes from the public npm registry —
|
||||||
|
this line is a scoped override, not a change of your default registry. The file
|
||||||
|
holds no secret, so you can commit it (and you should — your deployment needs it
|
||||||
|
too).
|
||||||
|
|
||||||
**2. Install:**
|
**2. Install:**
|
||||||
|
|
||||||
@@ -72,28 +35,25 @@ secret — you can safely commit it.
|
|||||||
pnpm add @intecion/ipal-kit
|
pnpm add @intecion/ipal-kit
|
||||||
```
|
```
|
||||||
|
|
||||||
### Route B — straight from the repository
|
That's it. No token, no login — the registry is public for reads.
|
||||||
|
|
||||||
pnpm clones the repository and uses the pre-built output. There's no `gitea:`
|
### Why the registry, not a git install
|
||||||
shorthand — give the full address.
|
|
||||||
|
|
||||||
Over HTTPS with your token:
|
You *can* install straight from the repo
|
||||||
|
(`pnpm add git+https://git.intecion.net/IntecionSoftware/ipal-kit.git`), and for a
|
||||||
|
quick throwaway test it works. But prefer the registry for real projects:
|
||||||
|
|
||||||
```bash
|
- **Client components resolve correctly.** A git install unpacks into a
|
||||||
pnpm add git+https://$GITEA_TOKEN@git.intecion.net/IntecionSoftware/ipal-kit.git
|
commit-hashed path (`.pnpm/@intecion+ipal-kit@git+https…#hash/…`) that breaks
|
||||||
```
|
Next.js's React Client Manifest — the consent banner, analytics, and Turnstile
|
||||||
|
components fail at runtime with "Could not find the module … in the React
|
||||||
|
Client Manifest". The registry unpacks to a clean `node_modules/@intecion/…`
|
||||||
|
path, so this doesn't happen.
|
||||||
|
- **Versioning.** `pnpm` sees a real version number, so a version bump cleanly
|
||||||
|
replaces the old one — no cache-clearing dance.
|
||||||
|
|
||||||
Or over SSH, if you have a key added in Gitea:
|
Keep the git install only for pulling a specific unreleased commit during
|
||||||
|
plugin development.
|
||||||
```bash
|
|
||||||
pnpm add git+ssh://[email protected]:IntecionSoftware/ipal-kit.git
|
|
||||||
```
|
|
||||||
|
|
||||||
Append a specific version after `#` to pin to a release:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pnpm add git+https://$GITEA_TOKEN@git.intecion.net/IntecionSoftware/ipal-kit.git#v1.0.0
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -107,6 +67,10 @@ pnpm add @payloadcms/[email protected] @payloadcms/[email protected] \
|
|||||||
nodemailer lucide-react slugify server-only
|
nodemailer lucide-react slugify server-only
|
||||||
```
|
```
|
||||||
|
|
||||||
|
> **Match `lucide-react` to the plugin.** The plugin uses `lucide-react@^0.400.0`.
|
||||||
|
> If your project pulls a different major (e.g. `1.x`), you end up with two copies
|
||||||
|
> and confusing type errors. Pin your project to the same range.
|
||||||
|
|
||||||
From here, the guide takes over: **[docs/getting-started.md](./docs/getting-started.md)** —
|
From here, the guide takes over: **[docs/getting-started.md](./docs/getting-started.md)** —
|
||||||
from an empty project to a working site.
|
from an empty project to a working site.
|
||||||
|
|
||||||
@@ -114,16 +78,25 @@ from an empty project to a working site.
|
|||||||
|
|
||||||
## Publishing a new version
|
## Publishing a new version
|
||||||
|
|
||||||
This section only applies if you develop the plugin itself and publish new
|
This section applies only if you develop the plugin itself. Publishing (unlike
|
||||||
versions. You'll need a token with the `write:package` scope.
|
installing) **does** need a token, with the `write:package` scope.
|
||||||
|
|
||||||
In `~/.npmrc`:
|
In `~/.npmrc` (path differs per OS — see the table below):
|
||||||
|
|
||||||
```
|
```
|
||||||
//git.intecion.net/api/packages/IntecionSoftware/npm/:_authToken=${GITEA_TOKEN}
|
//git.intecion.net/api/packages/IntecionSoftware/npm/:_authToken=${GITEA_TOKEN}
|
||||||
```
|
```
|
||||||
|
|
||||||
Build, bump the version number, publish:
|
Generate the token in Gitea → **Settings → Applications → Generate New Token**,
|
||||||
|
scope `write:package`. Put it in the `GITEA_TOKEN` env var.
|
||||||
|
|
||||||
|
| System | Global `.npmrc` path |
|
||||||
|
|---|---|
|
||||||
|
| macOS | `~/.npmrc` |
|
||||||
|
| Linux / Ubuntu | `~/.npmrc` |
|
||||||
|
| Windows | `%USERPROFILE%\.npmrc` |
|
||||||
|
|
||||||
|
Then build and publish:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
pnpm clean && pnpm build
|
pnpm clean && pnpm build
|
||||||
@@ -131,8 +104,7 @@ npm version patch # 1.0.0 → 1.0.1
|
|||||||
npm publish
|
npm publish
|
||||||
```
|
```
|
||||||
|
|
||||||
Bump the version on every release — that way pnpm always sees the new package and
|
Full checklist and troubleshooting: **[docs/publishing.md](./docs/publishing.md)**.
|
||||||
nobody gets stuck on a stale one from the cache.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -141,7 +113,7 @@ nobody gets stuck on a stale one from the cache.
|
|||||||
Everything lives in the **[docs/](./docs)** directory. To get started:
|
Everything lives in the **[docs/](./docs)** directory. To get started:
|
||||||
|
|
||||||
- **[getting-started.md](./docs/getting-started.md)** — project setup, step by step
|
- **[getting-started.md](./docs/getting-started.md)** — project setup, step by step
|
||||||
- **[install.md](./docs/install.md)** — more on installation and troubleshooting
|
- **[publishing.md](./docs/publishing.md)** — releasing new plugin versions
|
||||||
- **[README.md](./docs/README.md)** — index of the plugin's modules
|
- **[README.md](./docs/README.md)** — index of the plugin's modules
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -150,10 +122,10 @@ Everything lives in the **[docs/](./docs)** directory. To get started:
|
|||||||
|
|
||||||
| What you see | What's wrong |
|
| What you see | What's wrong |
|
||||||
|---|---|
|
|---|---|
|
||||||
| `404` or `Unauthorized` on `pnpm add @intecion/...` | no token in `.npmrc`, a wrong token, or you don't have access to the organization in Gitea |
|
| `404` on `pnpm add @intecion/...` | the `@intecion:registry` line is missing from `.npmrc` — the install went to public npm instead of Gitea |
|
||||||
| `404` when installing from the `.git` address | you're not signed in, the token is missing from the address, or you lack access to the repository |
|
| `Could not find the module … in the React Client Manifest` | installed from git, not the registry — reinstall from the registry (clean path) |
|
||||||
| `ERR_PNPM_GIT_DEP_PREPARE_NOT_ALLOWED` | an attempt to build on install from git — report it to whoever publishes the plugin |
|
| two versions of `lucide-react` | your project pins a different major than the plugin's `^0.400.0` — align them |
|
||||||
| old code after reinstalling | cache: run `pnpm store prune`, then remove `.next` and `node_modules/@intecion` |
|
| `missing secret key … to secure Payload` | `PAYLOAD_SECRET` isn't set in the runtime environment (not a plugin issue) |
|
||||||
|
|
||||||
If you get stuck, check **[docs/install.md](./docs/install.md)** or message the
|
If you get stuck, see **[docs/publishing.md](./docs/publishing.md)** or message the
|
||||||
team that maintains the plugin.
|
team that maintains the plugin.
|
||||||
Vendored
+16
@@ -0,0 +1,16 @@
|
|||||||
|
export { MaskedField } from '../globals/SiteIntegrations/components/MaskedField.js';
|
||||||
|
export { TestEmailButton } from '../globals/SiteIntegrations/components/TestEmailButton.js';
|
||||||
|
export { Analytics } from '../modules/analytics/client.js';
|
||||||
|
/**
|
||||||
|
* Entry point: ipal-kit/client
|
||||||
|
*
|
||||||
|
* Client-side ('use client') exports — React hooks, providers, and UI
|
||||||
|
* components. Kept separate from the main entry so server bundles don't pull in
|
||||||
|
* client-only code.
|
||||||
|
*/
|
||||||
|
export { ConsentProvider, CookieBanner, CookieButton, useConsent, useConsentContext, } from '../modules/consent/client.js';
|
||||||
|
export type { CookieBannerClassNames } from '../modules/consent/client.js';
|
||||||
|
export { Turnstile } from '../modules/turnstile/client.js';
|
||||||
|
export type { TurnstileProps } from '../modules/turnstile/client.js';
|
||||||
|
export { resolveFormMessage } from '../modules/notifications/resolveFormMessage.js';
|
||||||
|
export type { FormNotificationTexts } from '../modules/notifications/types.js';
|
||||||
Vendored
+15
@@ -0,0 +1,15 @@
|
|||||||
|
'use client';
|
||||||
|
export { MaskedField } from '../globals/SiteIntegrations/components/MaskedField.js';
|
||||||
|
export { TestEmailButton } from '../globals/SiteIntegrations/components/TestEmailButton.js';
|
||||||
|
export { Analytics } from '../modules/analytics/client.js';
|
||||||
|
/**
|
||||||
|
* Entry point: ipal-kit/client
|
||||||
|
*
|
||||||
|
* Client-side ('use client') exports — React hooks, providers, and UI
|
||||||
|
* components. Kept separate from the main entry so server bundles don't pull in
|
||||||
|
* client-only code.
|
||||||
|
*/ export { ConsentProvider, CookieBanner, CookieButton, useConsent, useConsentContext } from '../modules/consent/client.js';
|
||||||
|
export { Turnstile } from '../modules/turnstile/client.js';
|
||||||
|
export { resolveFormMessage } from '../modules/notifications/resolveFormMessage.js';
|
||||||
|
|
||||||
|
//# sourceMappingURL=client.js.map
|
||||||
Vendored
+1
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"sources":["../../src/exports/client.ts"],"sourcesContent":["'use client'\nexport { MaskedField } from '../globals/SiteIntegrations/components/MaskedField.js'\nexport { TestEmailButton } from '../globals/SiteIntegrations/components/TestEmailButton.js'\nexport { Analytics } from '../modules/analytics/client.js'\n/**\n * Entry point: ipal-kit/client\n *\n * Client-side ('use client') exports — React hooks, providers, and UI\n * components. Kept separate from the main entry so server bundles don't pull in\n * client-only code.\n */\nexport {\n ConsentProvider,\n CookieBanner,\n CookieButton,\n useConsent,\n useConsentContext,\n} from '../modules/consent/client.js'\nexport type { CookieBannerClassNames } from '../modules/consent/client.js'\nexport { Turnstile } from '../modules/turnstile/client.js'\nexport type { TurnstileProps } from '../modules/turnstile/client.js'\n\nexport { resolveFormMessage } from '../modules/notifications/resolveFormMessage.js'\nexport type { FormNotificationTexts } from '../modules/notifications/types.js'\n"],"names":["MaskedField","TestEmailButton","Analytics","ConsentProvider","CookieBanner","CookieButton","useConsent","useConsentContext","Turnstile","resolveFormMessage"],"mappings":"AAAA;AACA,SAASA,WAAW,QAAQ,wDAAuD;AACnF,SAASC,eAAe,QAAQ,4DAA2D;AAC3F,SAASC,SAAS,QAAQ,iCAAgC;AAC1D;;;;;;CAMC,GACD,SACEC,eAAe,EACfC,YAAY,EACZC,YAAY,EACZC,UAAU,EACVC,iBAAiB,QACZ,+BAA8B;AAErC,SAASC,SAAS,QAAQ,iCAAgC;AAG1D,SAASC,kBAAkB,QAAQ,iDAAgD"}
|
||||||
Vendored
+9
@@ -0,0 +1,9 @@
|
|||||||
|
/**
|
||||||
|
* Entry point: ipal-kit/next/middleware
|
||||||
|
*
|
||||||
|
* Re-exports the locale middleware factory and default matcher for use in a
|
||||||
|
* client project's middleware.ts. Kept as a thin re-export so the Next-facing
|
||||||
|
* surface is separate from the main package export.
|
||||||
|
*/
|
||||||
|
export { createLocaleMiddleware, DEFAULT_MIDDLEWARE_MATCHER } from '../modules/i18n/index.js';
|
||||||
|
export type { LocaleMiddlewareResult } from '../modules/i18n/index.js';
|
||||||
Vendored
+9
@@ -0,0 +1,9 @@
|
|||||||
|
/**
|
||||||
|
* Entry point: ipal-kit/next/middleware
|
||||||
|
*
|
||||||
|
* Re-exports the locale middleware factory and default matcher for use in a
|
||||||
|
* client project's middleware.ts. Kept as a thin re-export so the Next-facing
|
||||||
|
* surface is separate from the main package export.
|
||||||
|
*/ export { createLocaleMiddleware, DEFAULT_MIDDLEWARE_MATCHER } from '../modules/i18n/index.js';
|
||||||
|
|
||||||
|
//# sourceMappingURL=next-middleware.js.map
|
||||||
Vendored
+1
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"sources":["../../src/exports/next-middleware.ts"],"sourcesContent":["/**\n * Entry point: ipal-kit/next/middleware\n *\n * Re-exports the locale middleware factory and default matcher for use in a\n * client project's middleware.ts. Kept as a thin re-export so the Next-facing\n * surface is separate from the main package export.\n */\nexport { createLocaleMiddleware, DEFAULT_MIDDLEWARE_MATCHER } from '../modules/i18n/index.js'\nexport type { LocaleMiddlewareResult } from '../modules/i18n/index.js'\n"],"names":["createLocaleMiddleware","DEFAULT_MIDDLEWARE_MATCHER"],"mappings":"AAAA;;;;;;CAMC,GACD,SAASA,sBAAsB,EAAEC,0BAA0B,QAAQ,2BAA0B"}
|
||||||
Vendored
+9
@@ -0,0 +1,9 @@
|
|||||||
|
/**
|
||||||
|
* Entry point: ipal-kit/rsc
|
||||||
|
*
|
||||||
|
* Server-component exports. RenderBlocks is a React Server Component, so it
|
||||||
|
* lives here rather than in the main package entry to keep React out of the
|
||||||
|
* server-config bundle.
|
||||||
|
*/
|
||||||
|
export { RenderBlocks } from '../modules/blocks/index.js';
|
||||||
|
export type { BlockComponentMap, BlockData, EnhanceProps, RenderBlocksProps, } from '../modules/blocks/index.js';
|
||||||
Vendored
+9
@@ -0,0 +1,9 @@
|
|||||||
|
/**
|
||||||
|
* Entry point: ipal-kit/rsc
|
||||||
|
*
|
||||||
|
* Server-component exports. RenderBlocks is a React Server Component, so it
|
||||||
|
* lives here rather than in the main package entry to keep React out of the
|
||||||
|
* server-config bundle.
|
||||||
|
*/ export { RenderBlocks } from '../modules/blocks/index.js';
|
||||||
|
|
||||||
|
//# sourceMappingURL=rsc.js.map
|
||||||
Vendored
+1
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"sources":["../../src/exports/rsc.ts"],"sourcesContent":["/**\n * Entry point: ipal-kit/rsc\n *\n * Server-component exports. RenderBlocks is a React Server Component, so it\n * lives here rather than in the main package entry to keep React out of the\n * server-config bundle.\n */\nexport { RenderBlocks } from '../modules/blocks/index.js'\nexport type {\n BlockComponentMap,\n BlockData,\n EnhanceProps,\n RenderBlocksProps,\n} from '../modules/blocks/index.js'\n"],"names":["RenderBlocks"],"mappings":"AAAA;;;;;;CAMC,GACD,SAASA,YAAY,QAAQ,6BAA4B"}
|
||||||
Vendored
+17
@@ -0,0 +1,17 @@
|
|||||||
|
/**
|
||||||
|
* Entry point: ipal-kit/server
|
||||||
|
*
|
||||||
|
* Server-only runtime functions — these import 'server-only' (Turnstile secret,
|
||||||
|
* SMTP password, nodemailer). Kept OUT of the main entry so that loading the
|
||||||
|
* Payload config or running `payload generate:importmap` (both Node scripts,
|
||||||
|
* no bundler) never pulls in 'server-only', which throws outside a bundled
|
||||||
|
* server context.
|
||||||
|
*
|
||||||
|
* Import these only from Server Actions, route handlers, or server components —
|
||||||
|
* never from the Payload config or client code.
|
||||||
|
*/
|
||||||
|
export { verifyTurnstile } from '../modules/turnstile/index.js';
|
||||||
|
export { sendEmail } from '../modules/email/index.js';
|
||||||
|
export type { SendEmailArgs, SendEmailResult } from '../modules/email/index.js';
|
||||||
|
export { submitForm } from '../modules/forms/index.js';
|
||||||
|
export type { SubmitFormArgs, SubmitFormResult } from '../modules/forms/index.js';
|
||||||
Vendored
+16
@@ -0,0 +1,16 @@
|
|||||||
|
/**
|
||||||
|
* Entry point: ipal-kit/server
|
||||||
|
*
|
||||||
|
* Server-only runtime functions — these import 'server-only' (Turnstile secret,
|
||||||
|
* SMTP password, nodemailer). Kept OUT of the main entry so that loading the
|
||||||
|
* Payload config or running `payload generate:importmap` (both Node scripts,
|
||||||
|
* no bundler) never pulls in 'server-only', which throws outside a bundled
|
||||||
|
* server context.
|
||||||
|
*
|
||||||
|
* Import these only from Server Actions, route handlers, or server components —
|
||||||
|
* never from the Payload config or client code.
|
||||||
|
*/ export { verifyTurnstile } from '../modules/turnstile/index.js';
|
||||||
|
export { sendEmail } from '../modules/email/index.js';
|
||||||
|
export { submitForm } from '../modules/forms/index.js';
|
||||||
|
|
||||||
|
//# sourceMappingURL=server.js.map
|
||||||
Vendored
+1
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"sources":["../../src/exports/server.ts"],"sourcesContent":["/**\n * Entry point: ipal-kit/server\n *\n * Server-only runtime functions — these import 'server-only' (Turnstile secret,\n * SMTP password, nodemailer). Kept OUT of the main entry so that loading the\n * Payload config or running `payload generate:importmap` (both Node scripts,\n * no bundler) never pulls in 'server-only', which throws outside a bundled\n * server context.\n *\n * Import these only from Server Actions, route handlers, or server components —\n * never from the Payload config or client code.\n */\nexport { verifyTurnstile } from '../modules/turnstile/index.js'\nexport { sendEmail } from '../modules/email/index.js'\nexport type { SendEmailArgs, SendEmailResult } from '../modules/email/index.js'\nexport { submitForm } from '../modules/forms/index.js'\nexport type { SubmitFormArgs, SubmitFormResult } from '../modules/forms/index.js'\n"],"names":["verifyTurnstile","sendEmail","submitForm"],"mappings":"AAAA;;;;;;;;;;;CAWC,GACD,SAASA,eAAe,QAAQ,gCAA+B;AAC/D,SAASC,SAAS,QAAQ,4BAA2B;AAErD,SAASC,UAAU,QAAQ,4BAA2B"}
|
||||||
+7
@@ -0,0 +1,7 @@
|
|||||||
|
import type { Field } from 'payload';
|
||||||
|
/**
|
||||||
|
* Fields for the CookieSettings global — GDPR consent banner copy, editable
|
||||||
|
* per locale. The privacy-policy link is NOT stored here: it comes from the
|
||||||
|
* pages module (system page role 'privacyPolicy'), keeping one source of truth.
|
||||||
|
*/
|
||||||
|
export declare const cookieSettingsFields: Field[];
|
||||||
+87
@@ -0,0 +1,87 @@
|
|||||||
|
import { CONSENT_CATEGORIES } from '../../modules/consent/index.js';
|
||||||
|
/**
|
||||||
|
* Fields for the CookieSettings global — GDPR consent banner copy, editable
|
||||||
|
* per locale. The privacy-policy link is NOT stored here: it comes from the
|
||||||
|
* pages module (system page role 'privacyPolicy'), keeping one source of truth.
|
||||||
|
*/ export const cookieSettingsFields = [
|
||||||
|
{
|
||||||
|
name: 'message',
|
||||||
|
type: 'textarea',
|
||||||
|
admin: {
|
||||||
|
description: 'Main consent message shown in the banner.'
|
||||||
|
},
|
||||||
|
localized: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'settingsTitle',
|
||||||
|
type: 'text',
|
||||||
|
admin: {
|
||||||
|
description: 'Heading for the detailed settings panel.'
|
||||||
|
},
|
||||||
|
localized: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'buttons',
|
||||||
|
type: 'group',
|
||||||
|
admin: {
|
||||||
|
description: 'Button labels.'
|
||||||
|
},
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: 'acceptAll',
|
||||||
|
type: 'text',
|
||||||
|
localized: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'reject',
|
||||||
|
type: 'text',
|
||||||
|
localized: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'settings',
|
||||||
|
type: 'text',
|
||||||
|
localized: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'save',
|
||||||
|
type: 'text',
|
||||||
|
localized: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'back',
|
||||||
|
type: 'text',
|
||||||
|
localized: true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'categories',
|
||||||
|
type: 'array',
|
||||||
|
admin: {
|
||||||
|
description: 'Per-category titles and descriptions.'
|
||||||
|
},
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: 'key',
|
||||||
|
type: 'select',
|
||||||
|
options: CONSENT_CATEGORIES.map((c)=>({
|
||||||
|
label: c,
|
||||||
|
value: c
|
||||||
|
})),
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'title',
|
||||||
|
type: 'text',
|
||||||
|
localized: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'description',
|
||||||
|
type: 'textarea',
|
||||||
|
localized: true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
//# sourceMappingURL=fields.js.map
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"sources":["../../../src/globals/CookieSettings/fields.ts"],"sourcesContent":["import type { Field } from 'payload'\n\nimport { CONSENT_CATEGORIES } from '../../modules/consent/index.js'\n\n/**\n * Fields for the CookieSettings global — GDPR consent banner copy, editable\n * per locale. The privacy-policy link is NOT stored here: it comes from the\n * pages module (system page role 'privacyPolicy'), keeping one source of truth.\n */\nexport const cookieSettingsFields: Field[] = [\n {\n name: 'message',\n type: 'textarea',\n admin: {\n description: 'Main consent message shown in the banner.',\n },\n localized: true,\n },\n {\n name: 'settingsTitle',\n type: 'text',\n admin: {\n description: 'Heading for the detailed settings panel.',\n },\n localized: true,\n },\n {\n name: 'buttons',\n type: 'group',\n admin: {\n description: 'Button labels.',\n },\n fields: [\n { name: 'acceptAll', type: 'text', localized: true },\n { name: 'reject', type: 'text', localized: true },\n { name: 'settings', type: 'text', localized: true },\n { name: 'save', type: 'text', localized: true },\n { name: 'back', type: 'text', localized: true },\n ],\n },\n {\n name: 'categories',\n type: 'array',\n admin: {\n description: 'Per-category titles and descriptions.',\n },\n fields: [\n {\n name: 'key',\n type: 'select',\n options: CONSENT_CATEGORIES.map((c) => ({ label: c, value: c })),\n required: true,\n },\n { name: 'title', type: 'text', localized: true },\n { name: 'description', type: 'textarea', localized: true },\n ],\n },\n]\n"],"names":["CONSENT_CATEGORIES","cookieSettingsFields","name","type","admin","description","localized","fields","options","map","c","label","value","required"],"mappings":"AAEA,SAASA,kBAAkB,QAAQ,iCAAgC;AAEnE;;;;CAIC,GACD,OAAO,MAAMC,uBAAgC;IAC3C;QACEC,MAAM;QACNC,MAAM;QACNC,OAAO;YACLC,aAAa;QACf;QACAC,WAAW;IACb;IACA;QACEJ,MAAM;QACNC,MAAM;QACNC,OAAO;YACLC,aAAa;QACf;QACAC,WAAW;IACb;IACA;QACEJ,MAAM;QACNC,MAAM;QACNC,OAAO;YACLC,aAAa;QACf;QACAE,QAAQ;YACN;gBAAEL,MAAM;gBAAaC,MAAM;gBAAQG,WAAW;YAAK;YACnD;gBAAEJ,MAAM;gBAAUC,MAAM;gBAAQG,WAAW;YAAK;YAChD;gBAAEJ,MAAM;gBAAYC,MAAM;gBAAQG,WAAW;YAAK;YAClD;gBAAEJ,MAAM;gBAAQC,MAAM;gBAAQG,WAAW;YAAK;YAC9C;gBAAEJ,MAAM;gBAAQC,MAAM;gBAAQG,WAAW;YAAK;SAC/C;IACH;IACA;QACEJ,MAAM;QACNC,MAAM;QACNC,OAAO;YACLC,aAAa;QACf;QACAE,QAAQ;YACN;gBACEL,MAAM;gBACNC,MAAM;gBACNK,SAASR,mBAAmBS,GAAG,CAAC,CAACC,IAAO,CAAA;wBAAEC,OAAOD;wBAAGE,OAAOF;oBAAE,CAAA;gBAC7DG,UAAU;YACZ;YACA;gBAAEX,MAAM;gBAASC,MAAM;gBAAQG,WAAW;YAAK;YAC/C;gBAAEJ,MAAM;gBAAeC,MAAM;gBAAYG,WAAW;YAAK;SAC1D;IACH;CACD,CAAA"}
|
||||||
+6
@@ -0,0 +1,6 @@
|
|||||||
|
import type { GlobalConfig } from 'payload';
|
||||||
|
/**
|
||||||
|
* Builds the CookieSettings global — consent banner copy managed by editors.
|
||||||
|
* Public read (the banner needs it on the frontend for anonymous visitors).
|
||||||
|
*/
|
||||||
|
export declare function buildCookieSettings(): GlobalConfig;
|
||||||
Vendored
+19
@@ -0,0 +1,19 @@
|
|||||||
|
import { cookieSettingsFields } from './fields.js';
|
||||||
|
/**
|
||||||
|
* Builds the CookieSettings global — consent banner copy managed by editors.
|
||||||
|
* Public read (the banner needs it on the frontend for anonymous visitors).
|
||||||
|
*/ export function buildCookieSettings() {
|
||||||
|
return {
|
||||||
|
slug: 'cookie-settings',
|
||||||
|
access: {
|
||||||
|
read: ()=>true
|
||||||
|
},
|
||||||
|
admin: {
|
||||||
|
group: 'Settings'
|
||||||
|
},
|
||||||
|
fields: cookieSettingsFields,
|
||||||
|
label: 'Cookie Settings'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
//# sourceMappingURL=index.js.map
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"sources":["../../../src/globals/CookieSettings/index.ts"],"sourcesContent":["import type { GlobalConfig } from 'payload'\n\nimport { cookieSettingsFields } from './fields.js'\n\n/**\n * Builds the CookieSettings global — consent banner copy managed by editors.\n * Public read (the banner needs it on the frontend for anonymous visitors).\n */\nexport function buildCookieSettings(): GlobalConfig {\n return {\n slug: 'cookie-settings',\n access: {\n read: () => true,\n },\n admin: {\n group: 'Settings',\n },\n fields: cookieSettingsFields,\n label: 'Cookie Settings',\n }\n}\n"],"names":["cookieSettingsFields","buildCookieSettings","slug","access","read","admin","group","fields","label"],"mappings":"AAEA,SAASA,oBAAoB,QAAQ,cAAa;AAElD;;;CAGC,GACD,OAAO,SAASC;IACd,OAAO;QACLC,MAAM;QACNC,QAAQ;YACNC,MAAM,IAAM;QACd;QACAC,OAAO;YACLC,OAAO;QACT;QACAC,QAAQP;QACRQ,OAAO;IACT;AACF"}
|
||||||
+13
@@ -0,0 +1,13 @@
|
|||||||
|
import type { Field } from 'payload';
|
||||||
|
/**
|
||||||
|
* Fields for the Notifications global — localized user-facing texts for action
|
||||||
|
* results (form submission outcomes, and future contexts). Every text is
|
||||||
|
* localized: true so each language has its own value. Empty fields fall back to
|
||||||
|
* built-in English defaults (see modules/notifications/defaults).
|
||||||
|
*
|
||||||
|
* Grouped per context. `form` holds the outcomes of submitForm; more groups
|
||||||
|
* (e.g. `newsletter`, `system`) can be added the same way without touching
|
||||||
|
* consumers — getNotificationTexts resolves whatever exists, falling back
|
||||||
|
* per field.
|
||||||
|
*/
|
||||||
|
export declare const notificationsFields: Field[];
|
||||||
Vendored
+82
@@ -0,0 +1,82 @@
|
|||||||
|
/**
|
||||||
|
* Fields for the Notifications global — localized user-facing texts for action
|
||||||
|
* results (form submission outcomes, and future contexts). Every text is
|
||||||
|
* localized: true so each language has its own value. Empty fields fall back to
|
||||||
|
* built-in English defaults (see modules/notifications/defaults).
|
||||||
|
*
|
||||||
|
* Grouped per context. `form` holds the outcomes of submitForm; more groups
|
||||||
|
* (e.g. `newsletter`, `system`) can be added the same way without touching
|
||||||
|
* consumers — getNotificationTexts resolves whatever exists, falling back
|
||||||
|
* per field.
|
||||||
|
*/ export const notificationsFields = [
|
||||||
|
{
|
||||||
|
name: 'form',
|
||||||
|
type: 'group',
|
||||||
|
admin: {
|
||||||
|
description: 'Messages shown after a form is submitted. Leave a field empty to use the built-in default.'
|
||||||
|
},
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: 'success',
|
||||||
|
type: 'text',
|
||||||
|
admin: {
|
||||||
|
placeholder: 'Thank you — your message has been sent.'
|
||||||
|
},
|
||||||
|
localized: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'error',
|
||||||
|
type: 'text',
|
||||||
|
admin: {
|
||||||
|
placeholder: 'Something went wrong. Please try again later.'
|
||||||
|
},
|
||||||
|
localized: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'rateLimited',
|
||||||
|
type: 'text',
|
||||||
|
admin: {
|
||||||
|
placeholder: 'Too many attempts. Please wait a moment and try again.'
|
||||||
|
},
|
||||||
|
localized: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'turnstile',
|
||||||
|
type: 'text',
|
||||||
|
admin: {
|
||||||
|
placeholder: 'Captcha verification failed. Please try again.'
|
||||||
|
},
|
||||||
|
localized: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'validation',
|
||||||
|
type: 'text',
|
||||||
|
admin: {
|
||||||
|
description: 'Shown on a validation error. Use {field} to insert the offending field name.',
|
||||||
|
placeholder: 'Please check the {field} field and try again.'
|
||||||
|
},
|
||||||
|
localized: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'consent',
|
||||||
|
type: 'text',
|
||||||
|
admin: {
|
||||||
|
description: 'Shown when the GDPR consent checkbox is left unchecked.',
|
||||||
|
placeholder: 'Please accept the privacy policy to continue.'
|
||||||
|
},
|
||||||
|
localized: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'notFound',
|
||||||
|
type: 'text',
|
||||||
|
admin: {
|
||||||
|
placeholder: 'This form is no longer available.'
|
||||||
|
},
|
||||||
|
localized: true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
label: 'Form messages'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
//# sourceMappingURL=fields.js.map
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"sources":["../../../src/globals/Notifications/fields.ts"],"sourcesContent":["import type { Field } from 'payload'\n\n/**\n * Fields for the Notifications global — localized user-facing texts for action\n * results (form submission outcomes, and future contexts). Every text is\n * localized: true so each language has its own value. Empty fields fall back to\n * built-in English defaults (see modules/notifications/defaults).\n *\n * Grouped per context. `form` holds the outcomes of submitForm; more groups\n * (e.g. `newsletter`, `system`) can be added the same way without touching\n * consumers — getNotificationTexts resolves whatever exists, falling back\n * per field.\n */\nexport const notificationsFields: Field[] = [\n {\n name: 'form',\n type: 'group',\n admin: {\n description:\n 'Messages shown after a form is submitted. Leave a field empty to use the built-in default.',\n },\n fields: [\n {\n name: 'success',\n type: 'text',\n admin: { placeholder: 'Thank you — your message has been sent.' },\n localized: true,\n },\n {\n name: 'error',\n type: 'text',\n admin: { placeholder: 'Something went wrong. Please try again later.' },\n localized: true,\n },\n {\n name: 'rateLimited',\n type: 'text',\n admin: { placeholder: 'Too many attempts. Please wait a moment and try again.' },\n localized: true,\n },\n {\n name: 'turnstile',\n type: 'text',\n admin: { placeholder: 'Captcha verification failed. Please try again.' },\n localized: true,\n },\n {\n name: 'validation',\n type: 'text',\n admin: {\n description:\n 'Shown on a validation error. Use {field} to insert the offending field name.',\n placeholder: 'Please check the {field} field and try again.',\n },\n localized: true,\n },\n {\n name: 'consent',\n type: 'text',\n admin: {\n description: 'Shown when the GDPR consent checkbox is left unchecked.',\n placeholder: 'Please accept the privacy policy to continue.',\n },\n localized: true,\n },\n {\n name: 'notFound',\n type: 'text',\n admin: { placeholder: 'This form is no longer available.' },\n localized: true,\n },\n ],\n label: 'Form messages',\n },\n]\n"],"names":["notificationsFields","name","type","admin","description","fields","placeholder","localized","label"],"mappings":"AAEA;;;;;;;;;;CAUC,GACD,OAAO,MAAMA,sBAA+B;IAC1C;QACEC,MAAM;QACNC,MAAM;QACNC,OAAO;YACLC,aACE;QACJ;QACAC,QAAQ;YACN;gBACEJ,MAAM;gBACNC,MAAM;gBACNC,OAAO;oBAAEG,aAAa;gBAA0C;gBAChEC,WAAW;YACb;YACA;gBACEN,MAAM;gBACNC,MAAM;gBACNC,OAAO;oBAAEG,aAAa;gBAAgD;gBACtEC,WAAW;YACb;YACA;gBACEN,MAAM;gBACNC,MAAM;gBACNC,OAAO;oBAAEG,aAAa;gBAAyD;gBAC/EC,WAAW;YACb;YACA;gBACEN,MAAM;gBACNC,MAAM;gBACNC,OAAO;oBAAEG,aAAa;gBAAiD;gBACvEC,WAAW;YACb;YACA;gBACEN,MAAM;gBACNC,MAAM;gBACNC,OAAO;oBACLC,aACE;oBACFE,aAAa;gBACf;gBACAC,WAAW;YACb;YACA;gBACEN,MAAM;gBACNC,MAAM;gBACNC,OAAO;oBACLC,aAAa;oBACbE,aAAa;gBACf;gBACAC,WAAW;YACb;YACA;gBACEN,MAAM;gBACNC,MAAM;gBACNC,OAAO;oBAAEG,aAAa;gBAAoC;gBAC1DC,WAAW;YACb;SACD;QACDC,OAAO;IACT;CACD,CAAA"}
|
||||||
+7
@@ -0,0 +1,7 @@
|
|||||||
|
import type { GlobalConfig } from 'payload';
|
||||||
|
/**
|
||||||
|
* Builds the Notifications global — localized action-result texts. Readable by
|
||||||
|
* any authenticated panel user; server-side helpers read it with overrideAccess
|
||||||
|
* so the frontend can resolve texts without a session.
|
||||||
|
*/
|
||||||
|
export declare function buildNotifications(): GlobalConfig;
|
||||||
Vendored
+17
@@ -0,0 +1,17 @@
|
|||||||
|
import { notificationsFields } from './fields.js';
|
||||||
|
/**
|
||||||
|
* Builds the Notifications global — localized action-result texts. Readable by
|
||||||
|
* any authenticated panel user; server-side helpers read it with overrideAccess
|
||||||
|
* so the frontend can resolve texts without a session.
|
||||||
|
*/ export function buildNotifications() {
|
||||||
|
return {
|
||||||
|
slug: 'notifications',
|
||||||
|
label: 'Notifications',
|
||||||
|
access: {
|
||||||
|
read: ()=>true
|
||||||
|
},
|
||||||
|
fields: notificationsFields
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
//# sourceMappingURL=index.js.map
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"sources":["../../../src/globals/Notifications/index.ts"],"sourcesContent":["import type { GlobalConfig } from 'payload'\nimport { notificationsFields } from './fields.js'\n\n/**\n * Builds the Notifications global — localized action-result texts. Readable by\n * any authenticated panel user; server-side helpers read it with overrideAccess\n * so the frontend can resolve texts without a session.\n */\nexport function buildNotifications(): GlobalConfig {\n return {\n slug: 'notifications',\n label: 'Notifications',\n access: {\n read: () => true, // texts are public-facing (shown to end users)\n },\n fields: notificationsFields,\n }\n}\n"],"names":["notificationsFields","buildNotifications","slug","label","access","read","fields"],"mappings":"AACA,SAASA,mBAAmB,QAAQ,cAAa;AAEjD;;;;CAIC,GACD,OAAO,SAASC;IACd,OAAO;QACLC,MAAM;QACNC,OAAO;QACPC,QAAQ;YACNC,MAAM,IAAM;QACd;QACAC,QAAQN;IACV;AACF"}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
import type { TextFieldClientComponent } from 'payload';
|
||||||
|
export declare const MaskedField: TextFieldClientComponent;
|
||||||
|
export default MaskedField;
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
'use client';
|
||||||
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
||||||
|
import { useState } from 'react';
|
||||||
|
export const MaskedField = (props)=>{
|
||||||
|
const { field, path, value: propValue, setValue: propSetValue, onChange: propOnChange } = props || {};
|
||||||
|
const [internalValue, setInternalValue] = useState(propValue ?? '');
|
||||||
|
const [revealed, setRevealed] = useState(false);
|
||||||
|
const label = typeof field?.label === 'string' ? field.label : field?.name ?? path;
|
||||||
|
const handleChange = (e)=>{
|
||||||
|
const newVal = e.target.value;
|
||||||
|
setInternalValue(newVal);
|
||||||
|
if (typeof propSetValue === 'function') {
|
||||||
|
propSetValue(newVal);
|
||||||
|
}
|
||||||
|
if (typeof propOnChange === 'function') {
|
||||||
|
propOnChange(e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const currentValue = propValue !== undefined ? propValue : internalValue;
|
||||||
|
return /*#__PURE__*/ _jsxs("div", {
|
||||||
|
className: "field-type text",
|
||||||
|
children: [
|
||||||
|
label && /*#__PURE__*/ _jsx("label", {
|
||||||
|
className: "field-label",
|
||||||
|
children: label
|
||||||
|
}),
|
||||||
|
/*#__PURE__*/ _jsxs("div", {
|
||||||
|
style: {
|
||||||
|
display: 'flex',
|
||||||
|
gap: '.5rem'
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
/*#__PURE__*/ _jsx("input", {
|
||||||
|
autoComplete: "off",
|
||||||
|
onChange: handleChange,
|
||||||
|
style: {
|
||||||
|
flex: 1
|
||||||
|
},
|
||||||
|
type: revealed ? 'text' : 'password',
|
||||||
|
value: currentValue ?? ''
|
||||||
|
}),
|
||||||
|
/*#__PURE__*/ _jsx("button", {
|
||||||
|
onClick: ()=>setRevealed((r)=>!r),
|
||||||
|
type: "button",
|
||||||
|
children: revealed ? 'Hide' : 'Reveal'
|
||||||
|
})
|
||||||
|
]
|
||||||
|
})
|
||||||
|
]
|
||||||
|
});
|
||||||
|
};
|
||||||
|
export default MaskedField;
|
||||||
|
|
||||||
|
//# sourceMappingURL=MaskedField.js.map
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"sources":["../../../../src/globals/SiteIntegrations/components/MaskedField.tsx"],"sourcesContent":["'use client'\nimport type { TextFieldClientComponent } from 'payload'\nimport { useState } from 'react'\n\nexport const MaskedField: TextFieldClientComponent = (props: any) => {\n const { field, path, value: propValue, setValue: propSetValue, onChange: propOnChange } = props || {}\n const [internalValue, setInternalValue] = useState(propValue ?? '')\n const [revealed, setRevealed] = useState(false)\n const label = typeof field?.label === 'string' ? field.label : (field?.name ?? path)\n\n const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n const newVal = e.target.value\n setInternalValue(newVal)\n if (typeof propSetValue === 'function') {\n propSetValue(newVal)\n }\n if (typeof propOnChange === 'function') {\n propOnChange(e)\n }\n }\n\n const currentValue = propValue !== undefined ? propValue : internalValue\n\n return (\n <div className=\"field-type text\">\n {label && <label className=\"field-label\">{label}</label>}\n <div style={{ display: 'flex', gap: '.5rem' }}>\n <input\n autoComplete=\"off\"\n onChange={handleChange}\n style={{ flex: 1 }}\n type={revealed ? 'text' : 'password'}\n value={currentValue ?? ''}\n />\n <button onClick={() => setRevealed((r) => !r)} type=\"button\">\n {revealed ? 'Hide' : 'Reveal'}\n </button>\n </div>\n </div>\n )\n}\n\nexport default MaskedField\n"],"names":["useState","MaskedField","props","field","path","value","propValue","setValue","propSetValue","onChange","propOnChange","internalValue","setInternalValue","revealed","setRevealed","label","name","handleChange","e","newVal","target","currentValue","undefined","div","className","style","display","gap","input","autoComplete","flex","type","button","onClick","r"],"mappings":"AAAA;;AAEA,SAASA,QAAQ,QAAQ,QAAO;AAEhC,OAAO,MAAMC,cAAwC,CAACC;IACpD,MAAM,EAAEC,KAAK,EAAEC,IAAI,EAAEC,OAAOC,SAAS,EAAEC,UAAUC,YAAY,EAAEC,UAAUC,YAAY,EAAE,GAAGR,SAAS,CAAC;IACpG,MAAM,CAACS,eAAeC,iBAAiB,GAAGZ,SAASM,aAAa;IAChE,MAAM,CAACO,UAAUC,YAAY,GAAGd,SAAS;IACzC,MAAMe,QAAQ,OAAOZ,OAAOY,UAAU,WAAWZ,MAAMY,KAAK,GAAIZ,OAAOa,QAAQZ;IAE/E,MAAMa,eAAe,CAACC;QACpB,MAAMC,SAASD,EAAEE,MAAM,CAACf,KAAK;QAC7BO,iBAAiBO;QACjB,IAAI,OAAOX,iBAAiB,YAAY;YACtCA,aAAaW;QACf;QACA,IAAI,OAAOT,iBAAiB,YAAY;YACtCA,aAAaQ;QACf;IACF;IAEA,MAAMG,eAAef,cAAcgB,YAAYhB,YAAYK;IAE3D,qBACE,MAACY;QAAIC,WAAU;;YACZT,uBAAS,KAACA;gBAAMS,WAAU;0BAAeT;;0BAC1C,MAACQ;gBAAIE,OAAO;oBAAEC,SAAS;oBAAQC,KAAK;gBAAQ;;kCAC1C,KAACC;wBACCC,cAAa;wBACbpB,UAAUQ;wBACVQ,OAAO;4BAAEK,MAAM;wBAAE;wBACjBC,MAAMlB,WAAW,SAAS;wBAC1BR,OAAOgB,gBAAgB;;kCAEzB,KAACW;wBAAOC,SAAS,IAAMnB,YAAY,CAACoB,IAAM,CAACA;wBAAIH,MAAK;kCACjDlB,WAAW,SAAS;;;;;;AAK/B,EAAC;AAED,eAAeZ,YAAW"}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
/**
|
||||||
|
* Admin UI: a small "send test email" tool for the SiteIntegrations email tab.
|
||||||
|
* Enter an address, click Send, and it POSTs to /api/ipal/test-email, which
|
||||||
|
* sends through the currently-selected transport (SMTP or Graph). Shows the
|
||||||
|
* result inline so you can confirm delivery — or read the exact error — without
|
||||||
|
* leaving the panel.
|
||||||
|
*
|
||||||
|
* Assigned via a `ui` field's admin.components.Field.
|
||||||
|
*/
|
||||||
|
export declare const TestEmailButton: () => import("react/jsx-runtime").JSX.Element;
|
||||||
|
export default TestEmailButton;
|
||||||
@@ -0,0 +1,131 @@
|
|||||||
|
'use client';
|
||||||
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
||||||
|
import { useState } from 'react';
|
||||||
|
/**
|
||||||
|
* Admin UI: a small "send test email" tool for the SiteIntegrations email tab.
|
||||||
|
* Enter an address, click Send, and it POSTs to /api/ipal/test-email, which
|
||||||
|
* sends through the currently-selected transport (SMTP or Graph). Shows the
|
||||||
|
* result inline so you can confirm delivery — or read the exact error — without
|
||||||
|
* leaving the panel.
|
||||||
|
*
|
||||||
|
* Assigned via a `ui` field's admin.components.Field.
|
||||||
|
*/ export const TestEmailButton = ()=>{
|
||||||
|
const [to, setTo] = useState('');
|
||||||
|
const [status, setStatus] = useState({
|
||||||
|
kind: 'idle'
|
||||||
|
});
|
||||||
|
const send = async ()=>{
|
||||||
|
if (!to.trim()) {
|
||||||
|
setStatus({
|
||||||
|
kind: 'error',
|
||||||
|
msg: 'Enter a recipient address.'
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setStatus({
|
||||||
|
kind: 'sending'
|
||||||
|
});
|
||||||
|
try {
|
||||||
|
const res = await fetch('/api/ipal/test-email', {
|
||||||
|
body: JSON.stringify({
|
||||||
|
to: to.trim()
|
||||||
|
}),
|
||||||
|
credentials: 'include',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
method: 'POST'
|
||||||
|
});
|
||||||
|
const data = await res.json();
|
||||||
|
if (data.ok) {
|
||||||
|
setStatus({
|
||||||
|
kind: 'ok',
|
||||||
|
msg: data.message ?? 'Test email sent.'
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
setStatus({
|
||||||
|
kind: 'error',
|
||||||
|
msg: data.error ?? 'Send failed.'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
setStatus({
|
||||||
|
kind: 'error',
|
||||||
|
msg: 'Request failed. Is the server running?'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return /*#__PURE__*/ _jsxs("div", {
|
||||||
|
className: "field-type",
|
||||||
|
style: {
|
||||||
|
marginTop: '1rem'
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
/*#__PURE__*/ _jsx("label", {
|
||||||
|
className: "field-label",
|
||||||
|
children: "Send a test email"
|
||||||
|
}),
|
||||||
|
/*#__PURE__*/ _jsx("p", {
|
||||||
|
style: {
|
||||||
|
fontSize: '.85rem',
|
||||||
|
marginTop: 0,
|
||||||
|
opacity: 0.7
|
||||||
|
},
|
||||||
|
children: "Sends through the transport selected above. Save your changes first."
|
||||||
|
}),
|
||||||
|
/*#__PURE__*/ _jsxs("div", {
|
||||||
|
style: {
|
||||||
|
alignItems: 'center',
|
||||||
|
display: 'flex',
|
||||||
|
flexWrap: 'wrap',
|
||||||
|
gap: '.5rem'
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
/*#__PURE__*/ _jsx("input", {
|
||||||
|
onChange: (e)=>setTo(e.target.value),
|
||||||
|
placeholder: "[email protected]",
|
||||||
|
style: {
|
||||||
|
flex: 1,
|
||||||
|
minWidth: '220px'
|
||||||
|
},
|
||||||
|
type: "email",
|
||||||
|
value: to
|
||||||
|
}),
|
||||||
|
/*#__PURE__*/ _jsx("button", {
|
||||||
|
className: "btn btn--style-secondary",
|
||||||
|
disabled: status.kind === 'sending',
|
||||||
|
onClick: send,
|
||||||
|
style: {
|
||||||
|
whiteSpace: 'nowrap'
|
||||||
|
},
|
||||||
|
type: "button",
|
||||||
|
children: status.kind === 'sending' ? 'Sending…' : 'Send test'
|
||||||
|
})
|
||||||
|
]
|
||||||
|
}),
|
||||||
|
status.kind === 'ok' && /*#__PURE__*/ _jsxs("p", {
|
||||||
|
style: {
|
||||||
|
color: 'var(--theme-success-500, green)',
|
||||||
|
marginTop: '.5rem'
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
"✓ ",
|
||||||
|
status.msg
|
||||||
|
]
|
||||||
|
}),
|
||||||
|
status.kind === 'error' && /*#__PURE__*/ _jsxs("p", {
|
||||||
|
style: {
|
||||||
|
color: 'var(--theme-error-500, crimson)',
|
||||||
|
marginTop: '.5rem'
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
"✗ ",
|
||||||
|
status.msg
|
||||||
|
]
|
||||||
|
})
|
||||||
|
]
|
||||||
|
});
|
||||||
|
};
|
||||||
|
export default TestEmailButton;
|
||||||
|
|
||||||
|
//# sourceMappingURL=TestEmailButton.js.map
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"sources":["../../../../src/globals/SiteIntegrations/components/TestEmailButton.tsx"],"sourcesContent":["'use client'\n\nimport { useState } from 'react'\n\n/**\n * Admin UI: a small \"send test email\" tool for the SiteIntegrations email tab.\n * Enter an address, click Send, and it POSTs to /api/ipal/test-email, which\n * sends through the currently-selected transport (SMTP or Graph). Shows the\n * result inline so you can confirm delivery — or read the exact error — without\n * leaving the panel.\n *\n * Assigned via a `ui` field's admin.components.Field.\n */\nexport const TestEmailButton = () => {\n const [to, setTo] = useState('')\n const [status, setStatus] = useState<\n | { kind: 'error'; msg: string }\n | { kind: 'idle' }\n | { kind: 'ok'; msg: string }\n | { kind: 'sending' }\n >({ kind: 'idle' })\n\n const send = async () => {\n if (!to.trim()) {\n setStatus({ kind: 'error', msg: 'Enter a recipient address.' })\n return\n }\n setStatus({ kind: 'sending' })\n try {\n const res = await fetch('/api/ipal/test-email', {\n body: JSON.stringify({ to: to.trim() }),\n credentials: 'include',\n headers: { 'Content-Type': 'application/json' },\n method: 'POST',\n })\n const data = await res.json()\n if (data.ok) {\n setStatus({ kind: 'ok', msg: data.message ?? 'Test email sent.' })\n } else {\n setStatus({ kind: 'error', msg: data.error ?? 'Send failed.' })\n }\n } catch {\n setStatus({ kind: 'error', msg: 'Request failed. Is the server running?' })\n }\n }\n\n return (\n <div className=\"field-type\" style={{ marginTop: '1rem' }}>\n <label className=\"field-label\">Send a test email</label>\n <p style={{ fontSize: '.85rem', marginTop: 0, opacity: 0.7 }}>\n Sends through the transport selected above. Save your changes first.\n </p>\n <div style={{ alignItems: 'center', display: 'flex', flexWrap: 'wrap', gap: '.5rem' }}>\n <input\n onChange={(e) => setTo(e.target.value)}\n placeholder=\"[email protected]\"\n style={{ flex: 1, minWidth: '220px' }}\n type=\"email\"\n value={to}\n />\n <button\n className=\"btn btn--style-secondary\"\n disabled={status.kind === 'sending'}\n onClick={send}\n style={{ whiteSpace: 'nowrap' }}\n type=\"button\"\n >\n {status.kind === 'sending' ? 'Sending…' : 'Send test'}\n </button>\n </div>\n {status.kind === 'ok' && (\n <p style={{ color: 'var(--theme-success-500, green)', marginTop: '.5rem' }}>\n ✓ {status.msg}\n </p>\n )}\n {status.kind === 'error' && (\n <p style={{ color: 'var(--theme-error-500, crimson)', marginTop: '.5rem' }}>\n ✗ {status.msg}\n </p>\n )}\n </div>\n )\n}\n\nexport default TestEmailButton\n"],"names":["useState","TestEmailButton","to","setTo","status","setStatus","kind","send","trim","msg","res","fetch","body","JSON","stringify","credentials","headers","method","data","json","ok","message","error","div","className","style","marginTop","label","p","fontSize","opacity","alignItems","display","flexWrap","gap","input","onChange","e","target","value","placeholder","flex","minWidth","type","button","disabled","onClick","whiteSpace","color"],"mappings":"AAAA;;AAEA,SAASA,QAAQ,QAAQ,QAAO;AAEhC;;;;;;;;CAQC,GACD,OAAO,MAAMC,kBAAkB;IAC7B,MAAM,CAACC,IAAIC,MAAM,GAAGH,SAAS;IAC7B,MAAM,CAACI,QAAQC,UAAU,GAAGL,SAK1B;QAAEM,MAAM;IAAO;IAEjB,MAAMC,OAAO;QACX,IAAI,CAACL,GAAGM,IAAI,IAAI;YACdH,UAAU;gBAAEC,MAAM;gBAASG,KAAK;YAA6B;YAC7D;QACF;QACAJ,UAAU;YAAEC,MAAM;QAAU;QAC5B,IAAI;YACF,MAAMI,MAAM,MAAMC,MAAM,wBAAwB;gBAC9CC,MAAMC,KAAKC,SAAS,CAAC;oBAAEZ,IAAIA,GAAGM,IAAI;gBAAG;gBACrCO,aAAa;gBACbC,SAAS;oBAAE,gBAAgB;gBAAmB;gBAC9CC,QAAQ;YACV;YACA,MAAMC,OAAO,MAAMR,IAAIS,IAAI;YAC3B,IAAID,KAAKE,EAAE,EAAE;gBACXf,UAAU;oBAAEC,MAAM;oBAAMG,KAAKS,KAAKG,OAAO,IAAI;gBAAmB;YAClE,OAAO;gBACLhB,UAAU;oBAAEC,MAAM;oBAASG,KAAKS,KAAKI,KAAK,IAAI;gBAAe;YAC/D;QACF,EAAE,OAAM;YACNjB,UAAU;gBAAEC,MAAM;gBAASG,KAAK;YAAyC;QAC3E;IACF;IAEA,qBACE,MAACc;QAAIC,WAAU;QAAaC,OAAO;YAAEC,WAAW;QAAO;;0BACrD,KAACC;gBAAMH,WAAU;0BAAc;;0BAC/B,KAACI;gBAAEH,OAAO;oBAAEI,UAAU;oBAAUH,WAAW;oBAAGI,SAAS;gBAAI;0BAAG;;0BAG9D,MAACP;gBAAIE,OAAO;oBAAEM,YAAY;oBAAUC,SAAS;oBAAQC,UAAU;oBAAQC,KAAK;gBAAQ;;kCAClF,KAACC;wBACCC,UAAU,CAACC,IAAMlC,MAAMkC,EAAEC,MAAM,CAACC,KAAK;wBACrCC,aAAY;wBACZf,OAAO;4BAAEgB,MAAM;4BAAGC,UAAU;wBAAQ;wBACpCC,MAAK;wBACLJ,OAAOrC;;kCAET,KAAC0C;wBACCpB,WAAU;wBACVqB,UAAUzC,OAAOE,IAAI,KAAK;wBAC1BwC,SAASvC;wBACTkB,OAAO;4BAAEsB,YAAY;wBAAS;wBAC9BJ,MAAK;kCAEJvC,OAAOE,IAAI,KAAK,YAAY,aAAa;;;;YAG7CF,OAAOE,IAAI,KAAK,sBACf,MAACsB;gBAAEH,OAAO;oBAAEuB,OAAO;oBAAmCtB,WAAW;gBAAQ;;oBAAG;oBACvEtB,OAAOK,GAAG;;;YAGhBL,OAAOE,IAAI,KAAK,yBACf,MAACsB;gBAAEH,OAAO;oBAAEuB,OAAO;oBAAmCtB,WAAW;gBAAQ;;oBAAG;oBACvEtB,OAAOK,GAAG;;;;;AAKvB,EAAC;AAED,eAAeR,gBAAe"}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
import type { Field } from 'payload';
|
||||||
|
/**
|
||||||
|
* Analytics configuration — GA4 measurement ID and GTM container ID.
|
||||||
|
* IDs are public (exposed client-side), so no read restriction needed.
|
||||||
|
*/
|
||||||
|
export declare const analyticsFields: Field[];
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
/**
|
||||||
|
* Analytics configuration — GA4 measurement ID and GTM container ID.
|
||||||
|
* IDs are public (exposed client-side), so no read restriction needed.
|
||||||
|
*/ export const analyticsFields = [
|
||||||
|
{
|
||||||
|
name: 'ga4MeasurementId',
|
||||||
|
type: 'text',
|
||||||
|
admin: {
|
||||||
|
description: 'Google Analytics 4 Measurement ID.',
|
||||||
|
placeholder: 'G-XXXXXXXXXX'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'gtmContainerId',
|
||||||
|
type: 'text',
|
||||||
|
admin: {
|
||||||
|
description: 'Google Tag Manager container ID.',
|
||||||
|
placeholder: 'GTM-XXXXXXX'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
//# sourceMappingURL=analytics.js.map
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"sources":["../../../../src/globals/SiteIntegrations/fields/analytics.ts"],"sourcesContent":["import type { Field } from 'payload'\n\n/**\n * Analytics configuration — GA4 measurement ID and GTM container ID.\n * IDs are public (exposed client-side), so no read restriction needed.\n */\nexport const analyticsFields: Field[] = [\n {\n name: 'ga4MeasurementId',\n type: 'text',\n admin: {\n description: 'Google Analytics 4 Measurement ID.',\n placeholder: 'G-XXXXXXXXXX',\n },\n },\n {\n name: 'gtmContainerId',\n type: 'text',\n admin: {\n description: 'Google Tag Manager container ID.',\n placeholder: 'GTM-XXXXXXX',\n },\n },\n]\n"],"names":["analyticsFields","name","type","admin","description","placeholder"],"mappings":"AAEA;;;CAGC,GACD,OAAO,MAAMA,kBAA2B;IACtC;QACEC,MAAM;QACNC,MAAM;QACNC,OAAO;YACLC,aAAa;YACbC,aAAa;QACf;IACF;IACA;QACEJ,MAAM;QACNC,MAAM;QACNC,OAAO;YACLC,aAAa;YACbC,aAAa;QACf;IACF;CACD,CAAA"}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import type { Field } from 'payload';
|
||||||
|
/**
|
||||||
|
* SMTP transport settings for outbound email.
|
||||||
|
*
|
||||||
|
* Protected at the global level (SiteIntegrations requires an authenticated
|
||||||
|
* user), so all fields — including the password — stay editable in the admin
|
||||||
|
* panel while remaining inaccessible to anonymous API requests.
|
||||||
|
*/
|
||||||
|
export declare const smtpFields: Field[];
|
||||||
+94
@@ -0,0 +1,94 @@
|
|||||||
|
/**
|
||||||
|
* SMTP transport settings for outbound email.
|
||||||
|
*
|
||||||
|
* Protected at the global level (SiteIntegrations requires an authenticated
|
||||||
|
* user), so all fields — including the password — stay editable in the admin
|
||||||
|
* panel while remaining inaccessible to anonymous API requests.
|
||||||
|
*/ export const smtpFields = [
|
||||||
|
{
|
||||||
|
name: 'emailTransport',
|
||||||
|
type: 'select',
|
||||||
|
admin: {
|
||||||
|
description: 'How outbound email is sent. "Microsoft Graph" is only available when configured by the administrator (Intecion).'
|
||||||
|
},
|
||||||
|
defaultValue: 'smtp',
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
label: 'SMTP',
|
||||||
|
value: 'smtp'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Microsoft Graph (Exchange)',
|
||||||
|
value: 'graph'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'row',
|
||||||
|
admin: {
|
||||||
|
// Hide SMTP fields when Graph is selected — they're not used then.
|
||||||
|
condition: (_, siblingData)=>siblingData?.emailTransport !== 'graph'
|
||||||
|
},
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: 'smtpHost',
|
||||||
|
type: 'text',
|
||||||
|
admin: {
|
||||||
|
placeholder: 'smtp.example.com',
|
||||||
|
width: '70%'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'smtpPort',
|
||||||
|
type: 'number',
|
||||||
|
admin: {
|
||||||
|
width: '30%'
|
||||||
|
},
|
||||||
|
defaultValue: 587
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'smtpUser',
|
||||||
|
type: 'text',
|
||||||
|
admin: {
|
||||||
|
description: 'SMTP account username.'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'smtpPassword',
|
||||||
|
type: 'text',
|
||||||
|
admin: {
|
||||||
|
description: 'SMTP account password.',
|
||||||
|
// Masked in the UI (••••) — stored plaintext, readable for SMTP auth.
|
||||||
|
components: {
|
||||||
|
Field: '@intecion/ipal-kit/client#MaskedField'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'smtpFromAddress',
|
||||||
|
type: 'email',
|
||||||
|
admin: {
|
||||||
|
description: 'Default "from" address for outgoing mail.'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'smtpFromName',
|
||||||
|
type: 'text',
|
||||||
|
admin: {
|
||||||
|
description: 'Default "from" display name.'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'emailTest',
|
||||||
|
type: 'ui',
|
||||||
|
admin: {
|
||||||
|
components: {
|
||||||
|
Field: '@intecion/ipal-kit/client#TestEmailButton'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
//# sourceMappingURL=smtp.js.map
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"sources":["../../../../src/globals/SiteIntegrations/fields/smtp.ts"],"sourcesContent":["import type { Field } from 'payload'\n\n/**\n * SMTP transport settings for outbound email.\n *\n * Protected at the global level (SiteIntegrations requires an authenticated\n * user), so all fields — including the password — stay editable in the admin\n * panel while remaining inaccessible to anonymous API requests.\n */\nexport const smtpFields: Field[] = [\n {\n name: 'emailTransport',\n type: 'select',\n admin: {\n description:\n 'How outbound email is sent. \"Microsoft Graph\" is only available when configured by the administrator (Intecion).',\n // The Graph option only makes sense when agency credentials exist in env.\n // We can't read process.env in the admin UI directly, so a client project\n // that hasn't set up Graph should filter this option via integrationsFields\n // override, or simply leave it on 'smtp'. The adapter enforces the real\n // availability at send time regardless of what's selected here.\n },\n defaultValue: 'smtp',\n options: [\n { label: 'SMTP', value: 'smtp' },\n { label: 'Microsoft Graph (Exchange)', value: 'graph' },\n ],\n },\n {\n type: 'row',\n admin: {\n // Hide SMTP fields when Graph is selected — they're not used then.\n condition: (_, siblingData) => siblingData?.emailTransport !== 'graph',\n },\n fields: [\n {\n name: 'smtpHost',\n type: 'text',\n admin: { placeholder: 'smtp.example.com', width: '70%' },\n },\n {\n name: 'smtpPort',\n type: 'number',\n admin: { width: '30%' },\n defaultValue: 587,\n },\n ],\n },\n {\n name: 'smtpUser',\n type: 'text',\n admin: {\n description: 'SMTP account username.',\n },\n },\n {\n name: 'smtpPassword',\n type: 'text',\n admin: {\n description: 'SMTP account password.',\n // Masked in the UI (••••) — stored plaintext, readable for SMTP auth.\n components: {\n Field: '@intecion/ipal-kit/client#MaskedField',\n },\n },\n },\n {\n name: 'smtpFromAddress',\n type: 'email',\n admin: {\n description: 'Default \"from\" address for outgoing mail.',\n },\n },\n {\n name: 'smtpFromName',\n type: 'text',\n admin: {\n description: 'Default \"from\" display name.',\n },\n },\n {\n name: 'emailTest',\n type: 'ui',\n admin: {\n components: {\n Field: '@intecion/ipal-kit/client#TestEmailButton',\n },\n },\n },\n]\n"],"names":["smtpFields","name","type","admin","description","defaultValue","options","label","value","condition","_","siblingData","emailTransport","fields","placeholder","width","components","Field"],"mappings":"AAEA;;;;;;CAMC,GACD,OAAO,MAAMA,aAAsB;IACjC;QACEC,MAAM;QACNC,MAAM;QACNC,OAAO;YACLC,aACE;QAMJ;QACAC,cAAc;QACdC,SAAS;YACP;gBAAEC,OAAO;gBAAQC,OAAO;YAAO;YAC/B;gBAAED,OAAO;gBAA8BC,OAAO;YAAQ;SACvD;IACH;IACA;QACEN,MAAM;QACNC,OAAO;YACL,mEAAmE;YACnEM,WAAW,CAACC,GAAGC,cAAgBA,aAAaC,mBAAmB;QACjE;QACAC,QAAQ;YACN;gBACEZ,MAAM;gBACNC,MAAM;gBACNC,OAAO;oBAAEW,aAAa;oBAAoBC,OAAO;gBAAM;YACzD;YACA;gBACEd,MAAM;gBACNC,MAAM;gBACNC,OAAO;oBAAEY,OAAO;gBAAM;gBACtBV,cAAc;YAChB;SACD;IACH;IACA;QACEJ,MAAM;QACNC,MAAM;QACNC,OAAO;YACLC,aAAa;QACf;IACF;IACA;QACEH,MAAM;QACNC,MAAM;QACNC,OAAO;YACLC,aAAa;YACb,sEAAsE;YACtEY,YAAY;gBACVC,OAAO;YACT;QACF;IACF;IACA;QACEhB,MAAM;QACNC,MAAM;QACNC,OAAO;YACLC,aAAa;QACf;IACF;IACA;QACEH,MAAM;QACNC,MAAM;QACNC,OAAO;YACLC,aAAa;QACf;IACF;IACA;QACEH,MAAM;QACNC,MAAM;QACNC,OAAO;YACLa,YAAY;gBACVC,OAAO;YACT;QACF;IACF;CACD,CAAA"}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
import type { Field } from 'payload';
|
||||||
|
/**
|
||||||
|
* Cloudflare R2 storage credentials.
|
||||||
|
* Reserved for future use — media offloading to R2.
|
||||||
|
*
|
||||||
|
* Protected at the global level (SiteIntegrations requires an authenticated
|
||||||
|
* user), so the access keys stay editable in the admin panel while remaining
|
||||||
|
* inaccessible to anonymous API requests.
|
||||||
|
*/
|
||||||
|
export declare const storageFields: Field[];
|
||||||
+43
@@ -0,0 +1,43 @@
|
|||||||
|
/**
|
||||||
|
* Cloudflare R2 storage credentials.
|
||||||
|
* Reserved for future use — media offloading to R2.
|
||||||
|
*
|
||||||
|
* Protected at the global level (SiteIntegrations requires an authenticated
|
||||||
|
* user), so the access keys stay editable in the admin panel while remaining
|
||||||
|
* inaccessible to anonymous API requests.
|
||||||
|
*/ export const storageFields = [
|
||||||
|
{
|
||||||
|
name: 'r2Bucket',
|
||||||
|
type: 'text',
|
||||||
|
admin: {
|
||||||
|
description: 'R2 bucket name.'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'r2Endpoint',
|
||||||
|
type: 'text',
|
||||||
|
admin: {
|
||||||
|
description: 'R2 S3-compatible endpoint URL.'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'r2AccessKeyId',
|
||||||
|
type: 'text',
|
||||||
|
admin: {
|
||||||
|
description: 'R2 access key ID.'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'r2SecretAccessKey',
|
||||||
|
type: 'text',
|
||||||
|
admin: {
|
||||||
|
description: 'R2 secret access key.',
|
||||||
|
// Masked in the UI (••••) — stored plaintext, readable for R2 auth.
|
||||||
|
components: {
|
||||||
|
Field: '@intecion/ipal-kit/client#MaskedField'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
//# sourceMappingURL=storage.js.map
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"sources":["../../../../src/globals/SiteIntegrations/fields/storage.ts"],"sourcesContent":["import type { Field } from 'payload'\n\n/**\n * Cloudflare R2 storage credentials.\n * Reserved for future use — media offloading to R2.\n *\n * Protected at the global level (SiteIntegrations requires an authenticated\n * user), so the access keys stay editable in the admin panel while remaining\n * inaccessible to anonymous API requests.\n */\nexport const storageFields: Field[] = [\n {\n name: 'r2Bucket',\n type: 'text',\n admin: {\n description: 'R2 bucket name.',\n },\n },\n {\n name: 'r2Endpoint',\n type: 'text',\n admin: {\n description: 'R2 S3-compatible endpoint URL.',\n },\n },\n {\n name: 'r2AccessKeyId',\n type: 'text',\n admin: {\n description: 'R2 access key ID.',\n },\n },\n {\n name: 'r2SecretAccessKey',\n type: 'text',\n admin: {\n description: 'R2 secret access key.',\n // Masked in the UI (••••) — stored plaintext, readable for R2 auth.\n components: {\n Field: '@intecion/ipal-kit/client#MaskedField',\n },\n },\n },\n]\n"],"names":["storageFields","name","type","admin","description","components","Field"],"mappings":"AAEA;;;;;;;CAOC,GACD,OAAO,MAAMA,gBAAyB;IACpC;QACEC,MAAM;QACNC,MAAM;QACNC,OAAO;YACLC,aAAa;QACf;IACF;IACA;QACEH,MAAM;QACNC,MAAM;QACNC,OAAO;YACLC,aAAa;QACf;IACF;IACA;QACEH,MAAM;QACNC,MAAM;QACNC,OAAO;YACLC,aAAa;QACf;IACF;IACA;QACEH,MAAM;QACNC,MAAM;QACNC,OAAO;YACLC,aAAa;YACb,oEAAoE;YACpEC,YAAY;gBACVC,OAAO;YACT;QACF;IACF;CACD,CAAA"}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
import type { Field } from 'payload';
|
||||||
|
/**
|
||||||
|
* Cloudflare Turnstile credentials.
|
||||||
|
*
|
||||||
|
* siteKey is public (rendered in the widget); secretKey is used for
|
||||||
|
* server-side verification. Both are protected at the global level
|
||||||
|
* (SiteIntegrations requires an authenticated user) rather than per-field,
|
||||||
|
* so they remain editable in the admin panel.
|
||||||
|
*/
|
||||||
|
export declare const turnstileFields: Field[];
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
/**
|
||||||
|
* Cloudflare Turnstile credentials.
|
||||||
|
*
|
||||||
|
* siteKey is public (rendered in the widget); secretKey is used for
|
||||||
|
* server-side verification. Both are protected at the global level
|
||||||
|
* (SiteIntegrations requires an authenticated user) rather than per-field,
|
||||||
|
* so they remain editable in the admin panel.
|
||||||
|
*/ export const turnstileFields = [
|
||||||
|
{
|
||||||
|
name: 'turnstileSiteKey',
|
||||||
|
type: 'text',
|
||||||
|
admin: {
|
||||||
|
description: 'Public site key rendered in the Turnstile widget.'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'turnstileSecretKey',
|
||||||
|
type: 'text',
|
||||||
|
admin: {
|
||||||
|
description: 'Secret key used for server-side verification.',
|
||||||
|
// Masked in the UI (••••) — stored plaintext, readable for verification.
|
||||||
|
components: {
|
||||||
|
Field: '@intecion/ipal-kit/client#MaskedField'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
//# sourceMappingURL=turnstile.js.map
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"sources":["../../../../src/globals/SiteIntegrations/fields/turnstile.ts"],"sourcesContent":["import type { Field } from 'payload'\n\n/**\n * Cloudflare Turnstile credentials.\n *\n * siteKey is public (rendered in the widget); secretKey is used for\n * server-side verification. Both are protected at the global level\n * (SiteIntegrations requires an authenticated user) rather than per-field,\n * so they remain editable in the admin panel.\n */\nexport const turnstileFields: Field[] = [\n {\n name: 'turnstileSiteKey',\n type: 'text',\n admin: {\n description: 'Public site key rendered in the Turnstile widget.',\n },\n },\n {\n name: 'turnstileSecretKey',\n type: 'text',\n admin: {\n description: 'Secret key used for server-side verification.',\n // Masked in the UI (••••) — stored plaintext, readable for verification.\n components: {\n Field: '@intecion/ipal-kit/client#MaskedField',\n },\n },\n },\n]\n"],"names":["turnstileFields","name","type","admin","description","components","Field"],"mappings":"AAEA;;;;;;;CAOC,GACD,OAAO,MAAMA,kBAA2B;IACtC;QACEC,MAAM;QACNC,MAAM;QACNC,OAAO;YACLC,aAAa;QACf;IACF;IACA;QACEH,MAAM;QACNC,MAAM;QACNC,OAAO;YACLC,aAAa;YACb,yEAAyE;YACzEC,YAAY;gBACVC,OAAO;YACT;QACF;IACF;CACD,CAAA"}
|
||||||
+23
@@ -0,0 +1,23 @@
|
|||||||
|
import type { Field, GlobalConfig } from 'payload';
|
||||||
|
type BuildSiteIntegrationsArgs = {
|
||||||
|
/** Extra fields injected by the client project */
|
||||||
|
additionalFields?: Field[];
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Builds the SiteIntegrations global.
|
||||||
|
*
|
||||||
|
* Holds third-party service credentials. Access is enforced at the global
|
||||||
|
* level — the whole global requires an authenticated user — so secrets stay
|
||||||
|
* out of anonymous API responses while remaining editable in the admin panel
|
||||||
|
* and readable via the server-side Local API. (Field-level read:false was
|
||||||
|
* avoided because it also hides fields from the admin UI, making them
|
||||||
|
* impossible to enter.)
|
||||||
|
*
|
||||||
|
* Unnamed tabs keep data flat (siteIntegrations.ga4MeasurementId).
|
||||||
|
*
|
||||||
|
* Note: R2 storage credentials are NOT here — storage is infrastructure and
|
||||||
|
* binds at boot, so its config lives in .env (R2_BUCKET, R2_ENDPOINT, ...),
|
||||||
|
* consumed by buildR2Storage. See docs/storage.md.
|
||||||
|
*/
|
||||||
|
export declare function buildSiteIntegrations({ additionalFields, }?: BuildSiteIntegrationsArgs): GlobalConfig;
|
||||||
|
export {};
|
||||||
+61
@@ -0,0 +1,61 @@
|
|||||||
|
import { isAdmin } from '../../modules/access/index.js';
|
||||||
|
import { analyticsFields } from './fields/analytics.js';
|
||||||
|
import { smtpFields } from './fields/smtp.js';
|
||||||
|
import { turnstileFields } from './fields/turnstile.js';
|
||||||
|
/**
|
||||||
|
* Builds the SiteIntegrations global.
|
||||||
|
*
|
||||||
|
* Holds third-party service credentials. Access is enforced at the global
|
||||||
|
* level — the whole global requires an authenticated user — so secrets stay
|
||||||
|
* out of anonymous API responses while remaining editable in the admin panel
|
||||||
|
* and readable via the server-side Local API. (Field-level read:false was
|
||||||
|
* avoided because it also hides fields from the admin UI, making them
|
||||||
|
* impossible to enter.)
|
||||||
|
*
|
||||||
|
* Unnamed tabs keep data flat (siteIntegrations.ga4MeasurementId).
|
||||||
|
*
|
||||||
|
* Note: R2 storage credentials are NOT here — storage is infrastructure and
|
||||||
|
* binds at boot, so its config lives in .env (R2_BUCKET, R2_ENDPOINT, ...),
|
||||||
|
* consumed by buildR2Storage. See docs/storage.md.
|
||||||
|
*/ export function buildSiteIntegrations({ additionalFields } = {}) {
|
||||||
|
return {
|
||||||
|
slug: 'site-integrations',
|
||||||
|
access: {
|
||||||
|
// Admin-only — secrets live here. Anonymous and non-admin users get
|
||||||
|
// nothing through the API; admins read/edit in the panel and via Local API.
|
||||||
|
read: ({ req: { user } })=>isAdmin(user),
|
||||||
|
update: ({ req: { user } })=>isAdmin(user)
|
||||||
|
},
|
||||||
|
admin: {
|
||||||
|
group: 'Settings'
|
||||||
|
},
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
type: 'tabs',
|
||||||
|
tabs: [
|
||||||
|
{
|
||||||
|
fields: analyticsFields,
|
||||||
|
label: 'Analytics'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fields: turnstileFields,
|
||||||
|
label: 'Turnstile'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fields: smtpFields,
|
||||||
|
label: 'SMTP'
|
||||||
|
},
|
||||||
|
...additionalFields?.length ? [
|
||||||
|
{
|
||||||
|
fields: additionalFields,
|
||||||
|
label: 'Custom'
|
||||||
|
}
|
||||||
|
] : []
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
label: 'Site Integrations'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
//# sourceMappingURL=index.js.map
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"sources":["../../../src/globals/SiteIntegrations/index.ts"],"sourcesContent":["import type { Field, GlobalConfig } from 'payload'\n\nimport { isAdmin } from '../../modules/access/index.js'\nimport { analyticsFields } from './fields/analytics.js'\nimport { smtpFields } from './fields/smtp.js'\nimport { turnstileFields } from './fields/turnstile.js'\n\ntype BuildSiteIntegrationsArgs = {\n /** Extra fields injected by the client project */\n additionalFields?: Field[]\n}\n\n/**\n * Builds the SiteIntegrations global.\n *\n * Holds third-party service credentials. Access is enforced at the global\n * level — the whole global requires an authenticated user — so secrets stay\n * out of anonymous API responses while remaining editable in the admin panel\n * and readable via the server-side Local API. (Field-level read:false was\n * avoided because it also hides fields from the admin UI, making them\n * impossible to enter.)\n *\n * Unnamed tabs keep data flat (siteIntegrations.ga4MeasurementId).\n *\n * Note: R2 storage credentials are NOT here — storage is infrastructure and\n * binds at boot, so its config lives in .env (R2_BUCKET, R2_ENDPOINT, ...),\n * consumed by buildR2Storage. See docs/storage.md.\n */\nexport function buildSiteIntegrations({\n additionalFields,\n}: BuildSiteIntegrationsArgs = {}): GlobalConfig {\n return {\n slug: 'site-integrations',\n access: {\n // Admin-only — secrets live here. Anonymous and non-admin users get\n // nothing through the API; admins read/edit in the panel and via Local API.\n read: ({ req: { user } }) => isAdmin(user),\n update: ({ req: { user } }) => isAdmin(user),\n },\n admin: {\n group: 'Settings',\n },\n fields: [\n {\n type: 'tabs',\n tabs: [\n { fields: analyticsFields, label: 'Analytics' },\n { fields: turnstileFields, label: 'Turnstile' },\n { fields: smtpFields, label: 'SMTP' },\n ...(additionalFields?.length ? [{ fields: additionalFields, label: 'Custom' }] : []),\n ],\n },\n ],\n label: 'Site Integrations',\n }\n}\n"],"names":["isAdmin","analyticsFields","smtpFields","turnstileFields","buildSiteIntegrations","additionalFields","slug","access","read","req","user","update","admin","group","fields","type","tabs","label","length"],"mappings":"AAEA,SAASA,OAAO,QAAQ,gCAA+B;AACvD,SAASC,eAAe,QAAQ,wBAAuB;AACvD,SAASC,UAAU,QAAQ,mBAAkB;AAC7C,SAASC,eAAe,QAAQ,wBAAuB;AAOvD;;;;;;;;;;;;;;;CAeC,GACD,OAAO,SAASC,sBAAsB,EACpCC,gBAAgB,EACU,GAAG,CAAC,CAAC;IAC/B,OAAO;QACLC,MAAM;QACNC,QAAQ;YACN,oEAAoE;YACpE,4EAA4E;YAC5EC,MAAM,CAAC,EAAEC,KAAK,EAAEC,IAAI,EAAE,EAAE,GAAKV,QAAQU;YACrCC,QAAQ,CAAC,EAAEF,KAAK,EAAEC,IAAI,EAAE,EAAE,GAAKV,QAAQU;QACzC;QACAE,OAAO;YACLC,OAAO;QACT;QACAC,QAAQ;YACN;gBACEC,MAAM;gBACNC,MAAM;oBACJ;wBAAEF,QAAQb;wBAAiBgB,OAAO;oBAAY;oBAC9C;wBAAEH,QAAQX;wBAAiBc,OAAO;oBAAY;oBAC9C;wBAAEH,QAAQZ;wBAAYe,OAAO;oBAAO;uBAChCZ,kBAAkBa,SAAS;wBAAC;4BAAEJ,QAAQT;4BAAkBY,OAAO;wBAAS;qBAAE,GAAG,EAAE;iBACpF;YACH;SACD;QACDA,OAAO;IACT;AACF"}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
import type { Field } from 'payload';
|
||||||
|
/**
|
||||||
|
* General site identity fields.
|
||||||
|
* Consumed by SEO/OG (siteName, defaultShareImage) and frontend (logo).
|
||||||
|
*/
|
||||||
|
export declare const generalFields: Field[];
|
||||||
+88
@@ -0,0 +1,88 @@
|
|||||||
|
/**
|
||||||
|
* General site identity fields.
|
||||||
|
* Consumed by SEO/OG (siteName, defaultShareImage) and frontend (logo).
|
||||||
|
*/ export const generalFields = [
|
||||||
|
{
|
||||||
|
name: 'siteName',
|
||||||
|
type: 'text',
|
||||||
|
admin: {
|
||||||
|
description: 'Used in page titles and Open Graph metadata.'
|
||||||
|
},
|
||||||
|
localized: true,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'titleOrder',
|
||||||
|
type: 'select',
|
||||||
|
admin: {
|
||||||
|
description: 'Which comes first in browser tabs.'
|
||||||
|
},
|
||||||
|
defaultValue: 'page-first',
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
label: 'Page first — About Us | Acme',
|
||||||
|
value: 'page-first'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Site first — Acme | About Us',
|
||||||
|
value: 'site-first'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'titleSeparator',
|
||||||
|
type: 'select',
|
||||||
|
admin: {
|
||||||
|
description: 'Separates the page title from the site name in browser tabs.'
|
||||||
|
},
|
||||||
|
defaultValue: '|',
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
label: 'Pipe — Page | Site',
|
||||||
|
value: '|'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Dash — Page – Site',
|
||||||
|
value: '–'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Hyphen — Page - Site',
|
||||||
|
value: '-'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Bullet — Page · Site',
|
||||||
|
value: '·'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Slash — Page / Site',
|
||||||
|
value: '/'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'logo',
|
||||||
|
type: 'upload',
|
||||||
|
admin: {
|
||||||
|
description: 'Primary site logo.'
|
||||||
|
},
|
||||||
|
relationTo: 'media'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'defaultShareImage',
|
||||||
|
type: 'upload',
|
||||||
|
admin: {
|
||||||
|
description: 'Fallback Open Graph image when a page has none.'
|
||||||
|
},
|
||||||
|
relationTo: 'media'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'favicon',
|
||||||
|
type: 'upload',
|
||||||
|
admin: {
|
||||||
|
description: 'Square source icon (PNG or SVG) for the browser tab. Rendered by the frontend.'
|
||||||
|
},
|
||||||
|
relationTo: 'media'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
//# sourceMappingURL=general.js.map
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"sources":["../../../../src/globals/SiteSettings/fields/general.ts"],"sourcesContent":["import type { Field } from 'payload'\n\n/**\n * General site identity fields.\n * Consumed by SEO/OG (siteName, defaultShareImage) and frontend (logo).\n */\nexport const generalFields: Field[] = [\n {\n name: 'siteName',\n type: 'text',\n admin: {\n description: 'Used in page titles and Open Graph metadata.',\n },\n localized: true,\n required: true,\n },\n {\n name: 'titleOrder',\n type: 'select',\n admin: {\n description: 'Which comes first in browser tabs.',\n },\n defaultValue: 'page-first',\n options: [\n { label: 'Page first — About Us | Acme', value: 'page-first' },\n { label: 'Site first — Acme | About Us', value: 'site-first' },\n ],\n },\n {\n name: 'titleSeparator',\n type: 'select',\n admin: {\n description: 'Separates the page title from the site name in browser tabs.',\n },\n defaultValue: '|',\n options: [\n { label: 'Pipe — Page | Site', value: '|' },\n { label: 'Dash — Page – Site', value: '–' },\n { label: 'Hyphen — Page - Site', value: '-' },\n { label: 'Bullet — Page · Site', value: '·' },\n { label: 'Slash — Page / Site', value: '/' },\n ],\n },\n {\n name: 'logo',\n type: 'upload',\n admin: {\n description: 'Primary site logo.',\n },\n relationTo: 'media',\n },\n {\n name: 'defaultShareImage',\n type: 'upload',\n admin: {\n description: 'Fallback Open Graph image when a page has none.',\n },\n relationTo: 'media',\n },\n {\n name: 'favicon',\n type: 'upload',\n admin: {\n description: 'Square source icon (PNG or SVG) for the browser tab. Rendered by the frontend.',\n },\n relationTo: 'media',\n },\n]\n"],"names":["generalFields","name","type","admin","description","localized","required","defaultValue","options","label","value","relationTo"],"mappings":"AAEA;;;CAGC,GACD,OAAO,MAAMA,gBAAyB;IACpC;QACEC,MAAM;QACNC,MAAM;QACNC,OAAO;YACLC,aAAa;QACf;QACAC,WAAW;QACXC,UAAU;IACZ;IACA;QACEL,MAAM;QACNC,MAAM;QACNC,OAAO;YACLC,aAAa;QACf;QACAG,cAAc;QACdC,SAAS;YACP;gBAAEC,OAAO;gBAAkCC,OAAO;YAAa;YAC/D;gBAAED,OAAO;gBAAkCC,OAAO;YAAa;SAChE;IACH;IACA;QACET,MAAM;QACNC,MAAM;QACNC,OAAO;YACLC,aAAa;QACf;QACAG,cAAc;QACdC,SAAS;YACP;gBAAEC,OAAO;gBAAwBC,OAAO;YAAI;YAC5C;gBAAED,OAAO;gBAAwBC,OAAO;YAAI;YAC5C;gBAAED,OAAO;gBAA0BC,OAAO;YAAI;YAC9C;gBAAED,OAAO;gBAA0BC,OAAO;YAAI;YAC9C;gBAAED,OAAO;gBAAyBC,OAAO;YAAI;SAC9C;IACH;IACA;QACET,MAAM;QACNC,MAAM;QACNC,OAAO;YACLC,aAAa;QACf;QACAO,YAAY;IACd;IACA;QACEV,MAAM;QACNC,MAAM;QACNC,OAAO;YACLC,aAAa;QACf;QACAO,YAAY;IACd;IACA;QACEV,MAAM;QACNC,MAAM;QACNC,OAAO;YACLC,aAAa;QACf;QACAO,YAAY;IACd;CACD,CAAA"}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export {};
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
|
||||||
|
//# sourceMappingURL=navigation.js.map
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"sources":["../../../../src/globals/SiteSettings/fields/navigation.ts"],"names":[],"mappings":""}
|
||||||
+9
@@ -0,0 +1,9 @@
|
|||||||
|
import type { Field } from 'payload';
|
||||||
|
/**
|
||||||
|
* Theme behavior configuration.
|
||||||
|
*
|
||||||
|
* These are decisions, not visuals — the plugin stores them, the client
|
||||||
|
* template reads them and decides whether to render a toggle and which
|
||||||
|
* mode to start in. Appearance itself stays in the template.
|
||||||
|
*/
|
||||||
|
export declare const themeFields: Field[];
|
||||||
+37
@@ -0,0 +1,37 @@
|
|||||||
|
/**
|
||||||
|
* Theme behavior configuration.
|
||||||
|
*
|
||||||
|
* These are decisions, not visuals — the plugin stores them, the client
|
||||||
|
* template reads them and decides whether to render a toggle and which
|
||||||
|
* mode to start in. Appearance itself stays in the template.
|
||||||
|
*/ export const themeFields = [
|
||||||
|
{
|
||||||
|
name: 'defaultTheme',
|
||||||
|
type: 'select',
|
||||||
|
admin: {
|
||||||
|
description: 'Theme applied on a visitor’s first visit.'
|
||||||
|
},
|
||||||
|
defaultValue: 'light',
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
label: 'Light',
|
||||||
|
value: 'light'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Dark',
|
||||||
|
value: 'dark'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'allowThemeToggle',
|
||||||
|
type: 'checkbox',
|
||||||
|
admin: {
|
||||||
|
description: 'Show a light/dark switch on the site.'
|
||||||
|
},
|
||||||
|
defaultValue: true
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
//# sourceMappingURL=theme.js.map
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"sources":["../../../../src/globals/SiteSettings/fields/theme.ts"],"sourcesContent":["import type { Field } from 'payload'\n\n/**\n * Theme behavior configuration.\n *\n * These are decisions, not visuals — the plugin stores them, the client\n * template reads them and decides whether to render a toggle and which\n * mode to start in. Appearance itself stays in the template.\n */\nexport const themeFields: Field[] = [\n {\n name: 'defaultTheme',\n type: 'select',\n admin: {\n description: 'Theme applied on a visitor’s first visit.',\n },\n defaultValue: 'light',\n options: [\n { label: 'Light', value: 'light' },\n { label: 'Dark', value: 'dark' },\n ],\n required: true,\n },\n {\n name: 'allowThemeToggle',\n type: 'checkbox',\n admin: {\n description: 'Show a light/dark switch on the site.',\n },\n defaultValue: true,\n },\n]\n"],"names":["themeFields","name","type","admin","description","defaultValue","options","label","value","required"],"mappings":"AAEA;;;;;;CAMC,GACD,OAAO,MAAMA,cAAuB;IAClC;QACEC,MAAM;QACNC,MAAM;QACNC,OAAO;YACLC,aAAa;QACf;QACAC,cAAc;QACdC,SAAS;YACP;gBAAEC,OAAO;gBAASC,OAAO;YAAQ;YACjC;gBAAED,OAAO;gBAAQC,OAAO;YAAO;SAChC;QACDC,UAAU;IACZ;IACA;QACER,MAAM;QACNC,MAAM;QACNC,OAAO;YACLC,aAAa;QACf;QACAC,cAAc;IAChB;CACD,CAAA"}
|
||||||
Vendored
+27
@@ -0,0 +1,27 @@
|
|||||||
|
import type { Field, GlobalConfig } from 'payload';
|
||||||
|
type BuildSiteSettingsArgs = {
|
||||||
|
/** Extra fields injected by the client project */
|
||||||
|
additionalFields?: Field[];
|
||||||
|
/**
|
||||||
|
* Fields for the "System Pages" tab — page assignments by role (homepage,
|
||||||
|
* privacy policy, collection archives). Assembled by the caller, which is the
|
||||||
|
* only place that knows which modules are enabled. The tab is omitted when
|
||||||
|
* this is empty.
|
||||||
|
*/
|
||||||
|
systemPageFields?: Field[];
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Builds the SiteSettings global.
|
||||||
|
*
|
||||||
|
* Composes tabs out of field groups; it deliberately doesn't know where those
|
||||||
|
* groups come from. Earlier it imported the pages and content modules directly,
|
||||||
|
* which meant a global — a presentation-layer concern — had an opinion about
|
||||||
|
* blogs. Now the plugin's composition root passes fields in, and adding a new
|
||||||
|
* kind of page assignment needs no change here.
|
||||||
|
*
|
||||||
|
* Uses unnamed tabs — data stays flat (siteSettings.siteName, not
|
||||||
|
* siteSettings.general.siteName). Client-provided fields land in their own
|
||||||
|
* "Custom" tab so core data paths never shift.
|
||||||
|
*/
|
||||||
|
export declare function buildSiteSettings({ additionalFields, systemPageFields, }?: BuildSiteSettingsArgs): GlobalConfig;
|
||||||
|
export {};
|
||||||
Vendored
+55
@@ -0,0 +1,55 @@
|
|||||||
|
import { generalFields } from './fields/general.js';
|
||||||
|
import { themeFields } from './fields/theme.js';
|
||||||
|
/**
|
||||||
|
* Builds the SiteSettings global.
|
||||||
|
*
|
||||||
|
* Composes tabs out of field groups; it deliberately doesn't know where those
|
||||||
|
* groups come from. Earlier it imported the pages and content modules directly,
|
||||||
|
* which meant a global — a presentation-layer concern — had an opinion about
|
||||||
|
* blogs. Now the plugin's composition root passes fields in, and adding a new
|
||||||
|
* kind of page assignment needs no change here.
|
||||||
|
*
|
||||||
|
* Uses unnamed tabs — data stays flat (siteSettings.siteName, not
|
||||||
|
* siteSettings.general.siteName). Client-provided fields land in their own
|
||||||
|
* "Custom" tab so core data paths never shift.
|
||||||
|
*/ export function buildSiteSettings({ additionalFields, systemPageFields } = {}) {
|
||||||
|
return {
|
||||||
|
slug: 'site-settings',
|
||||||
|
access: {
|
||||||
|
read: ()=>true
|
||||||
|
},
|
||||||
|
admin: {
|
||||||
|
group: 'Settings'
|
||||||
|
},
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
type: 'tabs',
|
||||||
|
tabs: [
|
||||||
|
{
|
||||||
|
fields: generalFields,
|
||||||
|
label: 'General'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fields: themeFields,
|
||||||
|
label: 'Theme'
|
||||||
|
},
|
||||||
|
...systemPageFields?.length ? [
|
||||||
|
{
|
||||||
|
fields: systemPageFields,
|
||||||
|
label: 'System Pages'
|
||||||
|
}
|
||||||
|
] : [],
|
||||||
|
...additionalFields?.length ? [
|
||||||
|
{
|
||||||
|
fields: additionalFields,
|
||||||
|
label: 'Custom'
|
||||||
|
}
|
||||||
|
] : []
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
label: 'Site Settings'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
//# sourceMappingURL=index.js.map
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"sources":["../../../src/globals/SiteSettings/index.ts"],"sourcesContent":["import type { Field, GlobalConfig } from 'payload'\n\nimport { generalFields } from './fields/general.js'\nimport { themeFields } from './fields/theme.js'\n\ntype BuildSiteSettingsArgs = {\n /** Extra fields injected by the client project */\n additionalFields?: Field[]\n /**\n * Fields for the \"System Pages\" tab — page assignments by role (homepage,\n * privacy policy, collection archives). Assembled by the caller, which is the\n * only place that knows which modules are enabled. The tab is omitted when\n * this is empty.\n */\n systemPageFields?: Field[]\n}\n\n/**\n * Builds the SiteSettings global.\n *\n * Composes tabs out of field groups; it deliberately doesn't know where those\n * groups come from. Earlier it imported the pages and content modules directly,\n * which meant a global — a presentation-layer concern — had an opinion about\n * blogs. Now the plugin's composition root passes fields in, and adding a new\n * kind of page assignment needs no change here.\n *\n * Uses unnamed tabs — data stays flat (siteSettings.siteName, not\n * siteSettings.general.siteName). Client-provided fields land in their own\n * \"Custom\" tab so core data paths never shift.\n */\nexport function buildSiteSettings({\n additionalFields,\n systemPageFields,\n}: BuildSiteSettingsArgs = {}): GlobalConfig {\n return {\n slug: 'site-settings',\n access: {\n read: () => true,\n },\n admin: {\n group: 'Settings',\n },\n fields: [\n {\n type: 'tabs',\n tabs: [\n {\n fields: generalFields,\n label: 'General',\n },\n {\n fields: themeFields,\n label: 'Theme',\n },\n ...(systemPageFields?.length\n ? [{ fields: systemPageFields, label: 'System Pages' }]\n : []),\n ...(additionalFields?.length ? [{ fields: additionalFields, label: 'Custom' }] : []),\n ],\n },\n ],\n label: 'Site Settings',\n }\n}\n"],"names":["generalFields","themeFields","buildSiteSettings","additionalFields","systemPageFields","slug","access","read","admin","group","fields","type","tabs","label","length"],"mappings":"AAEA,SAASA,aAAa,QAAQ,sBAAqB;AACnD,SAASC,WAAW,QAAQ,oBAAmB;AAc/C;;;;;;;;;;;;CAYC,GACD,OAAO,SAASC,kBAAkB,EAChCC,gBAAgB,EAChBC,gBAAgB,EACM,GAAG,CAAC,CAAC;IAC3B,OAAO;QACLC,MAAM;QACNC,QAAQ;YACNC,MAAM,IAAM;QACd;QACAC,OAAO;YACLC,OAAO;QACT;QACAC,QAAQ;YACN;gBACEC,MAAM;gBACNC,MAAM;oBACJ;wBACEF,QAAQV;wBACRa,OAAO;oBACT;oBACA;wBACEH,QAAQT;wBACRY,OAAO;oBACT;uBACIT,kBAAkBU,SAClB;wBAAC;4BAAEJ,QAAQN;4BAAkBS,OAAO;wBAAe;qBAAE,GACrD,EAAE;uBACFV,kBAAkBW,SAAS;wBAAC;4BAAEJ,QAAQP;4BAAkBU,OAAO;wBAAS;qBAAE,GAAG,EAAE;iBACpF;YACH;SACD;QACDA,OAAO;IACT;AACF"}
|
||||||
Vendored
+39
@@ -0,0 +1,39 @@
|
|||||||
|
export type { AccessOption, Role } from './modules/access/index.js';
|
||||||
|
export { adminOnly, adminOnlyField, adminOrEditor, adminOrEditorField, adminOrSelf, authenticated, hasMinimumRole, isAdmin, isEditor, requireRole, requireRoleField, ROLE_HIERARCHY, } from './modules/access/index.js';
|
||||||
|
export type { AnalyticsConfig } from './modules/analytics/index.js';
|
||||||
|
export { getAnalyticsConfig } from './modules/analytics/index.js';
|
||||||
|
export { ACCEPT_ALL_CONSENT, CONSENT_CATEGORIES, CONSENT_COOKIE, CONSENT_MAX_AGE, CONSENT_VERSION, DEFAULT_CONSENT, getConsentTexts, parseConsent, REJECT_ALL_CONSENT, serializeConsent, setDefaultConsent, updateConsent, } from './modules/consent/index.js';
|
||||||
|
export type { ConsentCategory, ConsentState, ConsentTexts } from './modules/consent/index.js';
|
||||||
|
export type { ContentCollectionOption, ContentOption, ResolvedRoute, } from './modules/content/index.js';
|
||||||
|
export { archiveFieldName, buildArchivePath, buildEntryPath, getArchiveEntries, parsePageParam, resolveRoute, } from './modules/content/index.js';
|
||||||
|
export type { ArchiveEntries } from './modules/content/index.js';
|
||||||
|
export { graphAdapter } from './modules/email/graphAdapter.js';
|
||||||
|
export type { GraphAdapterArgs } from './modules/email/graphAdapter.js';
|
||||||
|
export { mailAdapter } from './modules/email/mailAdapter.js';
|
||||||
|
export type { MailAdapterArgs } from './modules/email/mailAdapter.js';
|
||||||
|
export { panelSmtpAdapter } from './modules/email/panelSmtpAdapter.js';
|
||||||
|
export type { PanelSmtpAdapterArgs } from './modules/email/panelSmtpAdapter.js';
|
||||||
|
export { buildFormsPlugin } from './modules/forms/formsPluginConfig.js';
|
||||||
|
export type { FormsCollectionOverrides, FormsFieldsOverride, FormsOption, } from './modules/forms/types.js';
|
||||||
|
export { createContentHelpers } from './modules/frontend/index.js';
|
||||||
|
export type { I18nConfig, LocaleDefinition, LocalizedSlugs } from './modules/i18n/index.js';
|
||||||
|
export { buildLocalizedPath, getDefaultLocale, getLocaleCodes, getLocaleDefinition, getLocalizedSlugs, isValidLocale, LOCALE_COOKIE_NAME, matchAcceptLanguage, negotiateLocale, switchLocalePath, } from './modules/i18n/index.js';
|
||||||
|
export type { LocaleMiddlewareResult } from './modules/i18n/index.js';
|
||||||
|
export { createLocaleMiddleware, DEFAULT_MIDDLEWARE_MATCHER } from './modules/i18n/index.js';
|
||||||
|
export { normalizeFilename, normalizeFilenameHook } from './modules/media/index.js';
|
||||||
|
export { getNotificationTexts, NOTIFICATION_FALLBACK, resolveFormMessage, } from './modules/notifications/index.js';
|
||||||
|
export type { FormNotificationTexts, NotificationsData, NotificationTexts, } from './modules/notifications/index.js';
|
||||||
|
export type { PagesOption, SystemPageRole } from './modules/pages/index.js';
|
||||||
|
export { ALL_SYSTEM_PAGE_ROLES, getSystemPagePath } from './modules/pages/index.js';
|
||||||
|
export type { GlobalQueryOptions } from './modules/payload/index.js';
|
||||||
|
export { getGlobal, getSiteIntegrations, getSiteSettings, SITE_INTEGRATIONS_SLUG, SITE_SETTINGS_SLUG, } from './modules/payload/index.js';
|
||||||
|
export { buildSecurityHeaders } from './modules/security/index.js';
|
||||||
|
export type { BuildSecurityHeadersArgs, SecurityHeader } from './modules/security/index.js';
|
||||||
|
export type { PageMetadata, SeoMeta, SeoOption } from './modules/seo/index.js';
|
||||||
|
export { buildHreflangAlternates, buildMetadata, composeTitle } from './modules/seo/index.js';
|
||||||
|
export type { AutoFillMapping, RobotsRules, SitemapEntry } from './modules/seo/index.js';
|
||||||
|
export { buildAutoFillMetaHook, buildRobots, buildSitemapEntries, createMetadataGenerator, createPageMetadata, injectAutoFillMeta, } from './modules/seo/index.js';
|
||||||
|
export { buildSlugField, toSlug } from './modules/slug/index.js';
|
||||||
|
export { buildR2Storage } from './modules/storage/index.js';
|
||||||
|
export { ipalKit } from './plugin.js';
|
||||||
|
export type { IpalOptions } from './types.js';
|
||||||
Vendored
+28
@@ -0,0 +1,28 @@
|
|||||||
|
export { adminOnly, adminOnlyField, adminOrEditor, adminOrEditorField, adminOrSelf, authenticated, hasMinimumRole, isAdmin, isEditor, requireRole, requireRoleField, ROLE_HIERARCHY } from './modules/access/index.js';
|
||||||
|
export { getAnalyticsConfig } from './modules/analytics/index.js';
|
||||||
|
export { ACCEPT_ALL_CONSENT, CONSENT_CATEGORIES, CONSENT_COOKIE, CONSENT_MAX_AGE, CONSENT_VERSION, DEFAULT_CONSENT, getConsentTexts, parseConsent, REJECT_ALL_CONSENT, serializeConsent, setDefaultConsent, updateConsent } from './modules/consent/index.js';
|
||||||
|
export { archiveFieldName, buildArchivePath, buildEntryPath, getArchiveEntries, parsePageParam, resolveRoute } from './modules/content/index.js';
|
||||||
|
export { graphAdapter } from './modules/email/graphAdapter.js';
|
||||||
|
export { mailAdapter } from './modules/email/mailAdapter.js';
|
||||||
|
// Imported straight from the file, NOT from ./modules/email/index.js — that
|
||||||
|
// barrel re-exports sendEmail, which imports 'server-only' and would crash when
|
||||||
|
// Payload loads the config (or runs generate:importmap) as a plain Node script.
|
||||||
|
export { panelSmtpAdapter } from './modules/email/panelSmtpAdapter.js';
|
||||||
|
export { buildFormsPlugin } from './modules/forms/formsPluginConfig.js';
|
||||||
|
export { createContentHelpers } from './modules/frontend/index.js';
|
||||||
|
export { buildLocalizedPath, getDefaultLocale, getLocaleCodes, getLocaleDefinition, getLocalizedSlugs, isValidLocale, LOCALE_COOKIE_NAME, matchAcceptLanguage, negotiateLocale, switchLocalePath } from './modules/i18n/index.js';
|
||||||
|
export { createLocaleMiddleware, DEFAULT_MIDDLEWARE_MATCHER } from './modules/i18n/index.js';
|
||||||
|
// Media — filename normalization hook for upload collections (Media).
|
||||||
|
export { normalizeFilename, normalizeFilenameHook } from './modules/media/index.js';
|
||||||
|
export { getNotificationTexts, NOTIFICATION_FALLBACK, resolveFormMessage } from './modules/notifications/index.js';
|
||||||
|
export { ALL_SYSTEM_PAGE_ROLES, getSystemPagePath } from './modules/pages/index.js';
|
||||||
|
export { getGlobal, getSiteIntegrations, getSiteSettings, SITE_INTEGRATIONS_SLUG, SITE_SETTINGS_SLUG } from './modules/payload/index.js';
|
||||||
|
export { buildSecurityHeaders } from './modules/security/index.js';
|
||||||
|
export { buildHreflangAlternates, buildMetadata, composeTitle } from './modules/seo/index.js';
|
||||||
|
export { buildAutoFillMetaHook, buildRobots, buildSitemapEntries, createMetadataGenerator, createPageMetadata, injectAutoFillMeta } from './modules/seo/index.js';
|
||||||
|
export { buildSlugField, toSlug } from './modules/slug/index.js';
|
||||||
|
// Storage — Cloudflare R2 media offload, configured from .env.
|
||||||
|
export { buildR2Storage } from './modules/storage/index.js';
|
||||||
|
export { ipalKit } from './plugin.js';
|
||||||
|
|
||||||
|
//# sourceMappingURL=index.js.map
|
||||||
Vendored
+1
File diff suppressed because one or more lines are too long
Vendored
+21
@@ -0,0 +1,21 @@
|
|||||||
|
import type { Access, FieldAccess } from 'payload';
|
||||||
|
import type { Role } from './types.js';
|
||||||
|
/**
|
||||||
|
* Collection-level access (returns boolean | Where).
|
||||||
|
* Use in collection `access.read/create/update/delete`.
|
||||||
|
*/
|
||||||
|
export declare const adminOnly: Access;
|
||||||
|
export declare const adminOrEditor: Access;
|
||||||
|
export declare const authenticated: Access;
|
||||||
|
/** Requires at least the given role. */
|
||||||
|
export declare const requireRole: (minimum: Role) => Access;
|
||||||
|
/** Admins see all; others are constrained to their own document. */
|
||||||
|
export declare const adminOrSelf: Access;
|
||||||
|
/**
|
||||||
|
* Field-level access (returns boolean only — no Where support).
|
||||||
|
* Use in field `access.read/update`.
|
||||||
|
*/
|
||||||
|
export declare const adminOnlyField: FieldAccess;
|
||||||
|
export declare const adminOrEditorField: FieldAccess;
|
||||||
|
/** Requires at least the given role, for field-level access. */
|
||||||
|
export declare const requireRoleField: (minimum: Role) => FieldAccess;
|
||||||
Vendored
+29
@@ -0,0 +1,29 @@
|
|||||||
|
import { hasMinimumRole, isAdmin } from './predicates.js';
|
||||||
|
/**
|
||||||
|
* Collection-level access (returns boolean | Where).
|
||||||
|
* Use in collection `access.read/create/update/delete`.
|
||||||
|
*/ export const adminOnly = ({ req: { user } })=>isAdmin(user);
|
||||||
|
export const adminOrEditor = ({ req: { user } })=>hasMinimumRole(user, 'editor');
|
||||||
|
export const authenticated = ({ req: { user } })=>Boolean(user);
|
||||||
|
/** Requires at least the given role. */ export const requireRole = (minimum)=>({ req: { user } })=>hasMinimumRole(user, minimum);
|
||||||
|
/** Admins see all; others are constrained to their own document. */ export const adminOrSelf = ({ req: { user } })=>{
|
||||||
|
if (isAdmin(user)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (!user) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
id: {
|
||||||
|
equals: user.id
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Field-level access (returns boolean only — no Where support).
|
||||||
|
* Use in field `access.read/update`.
|
||||||
|
*/ export const adminOnlyField = ({ req: { user } })=>isAdmin(user);
|
||||||
|
export const adminOrEditorField = ({ req: { user } })=>hasMinimumRole(user, 'editor');
|
||||||
|
/** Requires at least the given role, for field-level access. */ export const requireRoleField = (minimum)=>({ req: { user } })=>hasMinimumRole(user, minimum);
|
||||||
|
|
||||||
|
//# sourceMappingURL=access.js.map
|
||||||
Vendored
+1
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"sources":["../../../src/modules/access/access.ts"],"sourcesContent":["import type { Access, FieldAccess } from 'payload'\n\nimport type { Role } from './types.js'\n\nimport { hasMinimumRole, isAdmin } from './predicates.js'\n\n/**\n * Collection-level access (returns boolean | Where).\n * Use in collection `access.read/create/update/delete`.\n */\nexport const adminOnly: Access = ({ req: { user } }) => isAdmin(user)\n\nexport const adminOrEditor: Access = ({ req: { user } }) => hasMinimumRole(user, 'editor')\n\nexport const authenticated: Access = ({ req: { user } }) => Boolean(user)\n\n/** Requires at least the given role. */\nexport const requireRole =\n (minimum: Role): Access =>\n ({ req: { user } }) =>\n hasMinimumRole(user, minimum)\n\n/** Admins see all; others are constrained to their own document. */\nexport const adminOrSelf: Access = ({ req: { user } }) => {\n if (isAdmin(user)) {return true}\n if (!user) {return false}\n return { id: { equals: user.id } }\n}\n\n/**\n * Field-level access (returns boolean only — no Where support).\n * Use in field `access.read/update`.\n */\nexport const adminOnlyField: FieldAccess = ({ req: { user } }) => isAdmin(user)\n\nexport const adminOrEditorField: FieldAccess = ({ req: { user } }) => hasMinimumRole(user, 'editor')\n\n/** Requires at least the given role, for field-level access. */\nexport const requireRoleField =\n (minimum: Role): FieldAccess =>\n ({ req: { user } }) =>\n hasMinimumRole(user, minimum)\n"],"names":["hasMinimumRole","isAdmin","adminOnly","req","user","adminOrEditor","authenticated","Boolean","requireRole","minimum","adminOrSelf","id","equals","adminOnlyField","adminOrEditorField","requireRoleField"],"mappings":"AAIA,SAASA,cAAc,EAAEC,OAAO,QAAQ,kBAAiB;AAEzD;;;CAGC,GACD,OAAO,MAAMC,YAAoB,CAAC,EAAEC,KAAK,EAAEC,IAAI,EAAE,EAAE,GAAKH,QAAQG,MAAK;AAErE,OAAO,MAAMC,gBAAwB,CAAC,EAAEF,KAAK,EAAEC,IAAI,EAAE,EAAE,GAAKJ,eAAeI,MAAM,UAAS;AAE1F,OAAO,MAAME,gBAAwB,CAAC,EAAEH,KAAK,EAAEC,IAAI,EAAE,EAAE,GAAKG,QAAQH,MAAK;AAEzE,sCAAsC,GACtC,OAAO,MAAMI,cACX,CAACC,UACD,CAAC,EAAEN,KAAK,EAAEC,IAAI,EAAE,EAAE,GAChBJ,eAAeI,MAAMK,SAAQ;AAEjC,kEAAkE,GAClE,OAAO,MAAMC,cAAsB,CAAC,EAAEP,KAAK,EAAEC,IAAI,EAAE,EAAE;IACnD,IAAIH,QAAQG,OAAO;QAAC,OAAO;IAAI;IAC/B,IAAI,CAACA,MAAM;QAAC,OAAO;IAAK;IACxB,OAAO;QAAEO,IAAI;YAAEC,QAAQR,KAAKO,EAAE;QAAC;IAAE;AACnC,EAAC;AAED;;;CAGC,GACD,OAAO,MAAME,iBAA8B,CAAC,EAAEV,KAAK,EAAEC,IAAI,EAAE,EAAE,GAAKH,QAAQG,MAAK;AAE/E,OAAO,MAAMU,qBAAkC,CAAC,EAAEX,KAAK,EAAEC,IAAI,EAAE,EAAE,GAAKJ,eAAeI,MAAM,UAAS;AAEpG,8DAA8D,GAC9D,OAAO,MAAMW,mBACX,CAACN,UACD,CAAC,EAAEN,KAAK,EAAEC,IAAI,EAAE,EAAE,GAChBJ,eAAeI,MAAMK,SAAQ"}
|
||||||
Vendored
+6
@@ -0,0 +1,6 @@
|
|||||||
|
export { adminOnly, adminOnlyField, adminOrEditor, adminOrEditorField, adminOrSelf, authenticated, requireRole, requireRoleField, } from './access.js';
|
||||||
|
export { injectRoles } from './injectRoles.js';
|
||||||
|
export { hasMinimumRole, isAdmin, isEditor } from './predicates.js';
|
||||||
|
export { buildRolesField } from './rolesField.js';
|
||||||
|
export type { AccessOption, Role } from './types.js';
|
||||||
|
export { ROLE_HIERARCHY } from './types.js';
|
||||||
Vendored
+7
@@ -0,0 +1,7 @@
|
|||||||
|
export { adminOnly, adminOnlyField, adminOrEditor, adminOrEditorField, adminOrSelf, authenticated, requireRole, requireRoleField } from './access.js';
|
||||||
|
export { injectRoles } from './injectRoles.js';
|
||||||
|
export { hasMinimumRole, isAdmin, isEditor } from './predicates.js';
|
||||||
|
export { buildRolesField } from './rolesField.js';
|
||||||
|
export { ROLE_HIERARCHY } from './types.js';
|
||||||
|
|
||||||
|
//# sourceMappingURL=index.js.map
|
||||||
Vendored
+1
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"sources":["../../../src/modules/access/index.ts"],"sourcesContent":["export {\n adminOnly,\n adminOnlyField,\n adminOrEditor,\n adminOrEditorField,\n adminOrSelf,\n authenticated,\n requireRole,\n requireRoleField,\n} from './access.js'\nexport { injectRoles } from './injectRoles.js'\nexport { hasMinimumRole, isAdmin, isEditor } from './predicates.js'\nexport { buildRolesField } from './rolesField.js'\nexport type { AccessOption, Role } from './types.js'\nexport { ROLE_HIERARCHY } from './types.js'\n"],"names":["adminOnly","adminOnlyField","adminOrEditor","adminOrEditorField","adminOrSelf","authenticated","requireRole","requireRoleField","injectRoles","hasMinimumRole","isAdmin","isEditor","buildRolesField","ROLE_HIERARCHY"],"mappings":"AAAA,SACEA,SAAS,EACTC,cAAc,EACdC,aAAa,EACbC,kBAAkB,EAClBC,WAAW,EACXC,aAAa,EACbC,WAAW,EACXC,gBAAgB,QACX,cAAa;AACpB,SAASC,WAAW,QAAQ,mBAAkB;AAC9C,SAASC,cAAc,EAAEC,OAAO,EAAEC,QAAQ,QAAQ,kBAAiB;AACnE,SAASC,eAAe,QAAQ,kBAAiB;AAEjD,SAASC,cAAc,QAAQ,aAAY"}
|
||||||
Vendored
+10
@@ -0,0 +1,10 @@
|
|||||||
|
import type { Config } from 'payload';
|
||||||
|
import type { AccessOption } from './types.js';
|
||||||
|
/**
|
||||||
|
* Injects the fixed `roles` field into the client's auth collection.
|
||||||
|
*
|
||||||
|
* The plugin owns the role definition; the client owns the collection. This
|
||||||
|
* finds the collection by slug and appends the field. We control the whole
|
||||||
|
* stack, so no conflict handling is needed — the field is simply added.
|
||||||
|
*/
|
||||||
|
export declare function injectRoles(config: Config, access: AccessOption): Config;
|
||||||
Vendored
+27
@@ -0,0 +1,27 @@
|
|||||||
|
import { buildRolesField } from './rolesField.js';
|
||||||
|
/**
|
||||||
|
* Injects the fixed `roles` field into the client's auth collection.
|
||||||
|
*
|
||||||
|
* The plugin owns the role definition; the client owns the collection. This
|
||||||
|
* finds the collection by slug and appends the field. We control the whole
|
||||||
|
* stack, so no conflict handling is needed — the field is simply added.
|
||||||
|
*/ export function injectRoles(config, access) {
|
||||||
|
const rolesField = buildRolesField(access.defaultRole);
|
||||||
|
return {
|
||||||
|
...config,
|
||||||
|
collections: (config.collections ?? []).map((collection)=>{
|
||||||
|
if (collection.slug !== access.authCollection) {
|
||||||
|
return collection;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
...collection,
|
||||||
|
fields: [
|
||||||
|
...collection.fields,
|
||||||
|
rolesField
|
||||||
|
]
|
||||||
|
};
|
||||||
|
})
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
//# sourceMappingURL=injectRoles.js.map
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"sources":["../../../src/modules/access/injectRoles.ts"],"sourcesContent":["import type { Config } from 'payload'\n\nimport type { AccessOption } from './types.js'\n\nimport { buildRolesField } from './rolesField.js'\n\n/**\n * Injects the fixed `roles` field into the client's auth collection.\n *\n * The plugin owns the role definition; the client owns the collection. This\n * finds the collection by slug and appends the field. We control the whole\n * stack, so no conflict handling is needed — the field is simply added.\n */\nexport function injectRoles(config: Config, access: AccessOption): Config {\n const rolesField = buildRolesField(access.defaultRole)\n\n return {\n ...config,\n collections: (config.collections ?? []).map((collection) => {\n if (collection.slug !== access.authCollection) {\n return collection\n }\n return {\n ...collection,\n fields: [...collection.fields, rolesField],\n }\n }),\n }\n}\n"],"names":["buildRolesField","injectRoles","config","access","rolesField","defaultRole","collections","map","collection","slug","authCollection","fields"],"mappings":"AAIA,SAASA,eAAe,QAAQ,kBAAiB;AAEjD;;;;;;CAMC,GACD,OAAO,SAASC,YAAYC,MAAc,EAAEC,MAAoB;IAC9D,MAAMC,aAAaJ,gBAAgBG,OAAOE,WAAW;IAErD,OAAO;QACL,GAAGH,MAAM;QACTI,aAAa,AAACJ,CAAAA,OAAOI,WAAW,IAAI,EAAE,AAAD,EAAGC,GAAG,CAAC,CAACC;YAC3C,IAAIA,WAAWC,IAAI,KAAKN,OAAOO,cAAc,EAAE;gBAC7C,OAAOF;YACT;YACA,OAAO;gBACL,GAAGA,UAAU;gBACbG,QAAQ;uBAAIH,WAAWG,MAAM;oBAAEP;iBAAW;YAC5C;QACF;IACF;AACF"}
|
||||||
Vendored
+20
@@ -0,0 +1,20 @@
|
|||||||
|
import type { Role } from './types.js';
|
||||||
|
/**
|
||||||
|
* The plugin can't know the client's generated User type, and Payload types
|
||||||
|
* `req.user` loosely (UntypedUser | null). Predicates therefore accept an
|
||||||
|
* unknown-ish user and read `roles` defensively — no assumptions about shape
|
||||||
|
* beyond an optional roles array.
|
||||||
|
*/
|
||||||
|
type MaybeUser = {
|
||||||
|
roles?: null | Role[];
|
||||||
|
} | null | Record<string, unknown> | undefined;
|
||||||
|
/**
|
||||||
|
* True if the user holds at least the given role in the hierarchy.
|
||||||
|
* admin satisfies 'editor' and 'user'; editor satisfies 'user'.
|
||||||
|
*/
|
||||||
|
export declare function hasMinimumRole(user: MaybeUser, minimum: Role): boolean;
|
||||||
|
/** True if the user is an admin. */
|
||||||
|
export declare function isAdmin(user: MaybeUser): boolean;
|
||||||
|
/** True if the user is an editor or higher (editor, admin). */
|
||||||
|
export declare function isEditor(user: MaybeUser): boolean;
|
||||||
|
export {};
|
||||||
Vendored
+32
@@ -0,0 +1,32 @@
|
|||||||
|
import { ROLE_HIERARCHY } from './types.js';
|
||||||
|
/** Safely extracts the roles array from a loosely-typed user. */ function getRoles(user) {
|
||||||
|
if (!user || typeof user !== 'object') {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
const roles = user.roles;
|
||||||
|
if (!Array.isArray(roles)) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
return roles.filter((role)=>ROLE_HIERARCHY.includes(role));
|
||||||
|
}
|
||||||
|
/** Highest-privilege role index the user holds, or -1 if none. */ function highestRoleIndex(user) {
|
||||||
|
const roles = getRoles(user);
|
||||||
|
if (!roles.length) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return Math.max(...roles.map((role)=>ROLE_HIERARCHY.indexOf(role)));
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* True if the user holds at least the given role in the hierarchy.
|
||||||
|
* admin satisfies 'editor' and 'user'; editor satisfies 'user'.
|
||||||
|
*/ export function hasMinimumRole(user, minimum) {
|
||||||
|
return highestRoleIndex(user) >= ROLE_HIERARCHY.indexOf(minimum);
|
||||||
|
}
|
||||||
|
/** True if the user is an admin. */ export function isAdmin(user) {
|
||||||
|
return hasMinimumRole(user, 'admin');
|
||||||
|
}
|
||||||
|
/** True if the user is an editor or higher (editor, admin). */ export function isEditor(user) {
|
||||||
|
return hasMinimumRole(user, 'editor');
|
||||||
|
}
|
||||||
|
|
||||||
|
//# sourceMappingURL=predicates.js.map
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"sources":["../../../src/modules/access/predicates.ts"],"sourcesContent":["import type { Role } from './types.js'\n\nimport { ROLE_HIERARCHY } from './types.js'\n\n/**\n * The plugin can't know the client's generated User type, and Payload types\n * `req.user` loosely (UntypedUser | null). Predicates therefore accept an\n * unknown-ish user and read `roles` defensively — no assumptions about shape\n * beyond an optional roles array.\n */\ntype MaybeUser = { roles?: null | Role[] } | null | Record<string, unknown> | undefined\n\n/** Safely extracts the roles array from a loosely-typed user. */\nfunction getRoles(user: MaybeUser): Role[] {\n if (!user || typeof user !== 'object') {return []}\n const roles = (user as { roles?: unknown }).roles\n if (!Array.isArray(roles)) {return []}\n return roles.filter((role): role is Role => ROLE_HIERARCHY.includes(role as Role))\n}\n\n/** Highest-privilege role index the user holds, or -1 if none. */\nfunction highestRoleIndex(user: MaybeUser): number {\n const roles = getRoles(user)\n if (!roles.length) {return -1}\n return Math.max(...roles.map((role) => ROLE_HIERARCHY.indexOf(role)))\n}\n\n/**\n * True if the user holds at least the given role in the hierarchy.\n * admin satisfies 'editor' and 'user'; editor satisfies 'user'.\n */\nexport function hasMinimumRole(user: MaybeUser, minimum: Role): boolean {\n return highestRoleIndex(user) >= ROLE_HIERARCHY.indexOf(minimum)\n}\n\n/** True if the user is an admin. */\nexport function isAdmin(user: MaybeUser): boolean {\n return hasMinimumRole(user, 'admin')\n}\n\n/** True if the user is an editor or higher (editor, admin). */\nexport function isEditor(user: MaybeUser): boolean {\n return hasMinimumRole(user, 'editor')\n}\n"],"names":["ROLE_HIERARCHY","getRoles","user","roles","Array","isArray","filter","role","includes","highestRoleIndex","length","Math","max","map","indexOf","hasMinimumRole","minimum","isAdmin","isEditor"],"mappings":"AAEA,SAASA,cAAc,QAAQ,aAAY;AAU3C,+DAA+D,GAC/D,SAASC,SAASC,IAAe;IAC/B,IAAI,CAACA,QAAQ,OAAOA,SAAS,UAAU;QAAC,OAAO,EAAE;IAAA;IACjD,MAAMC,QAAQ,AAACD,KAA6BC,KAAK;IACjD,IAAI,CAACC,MAAMC,OAAO,CAACF,QAAQ;QAAC,OAAO,EAAE;IAAA;IACrC,OAAOA,MAAMG,MAAM,CAAC,CAACC,OAAuBP,eAAeQ,QAAQ,CAACD;AACtE;AAEA,gEAAgE,GAChE,SAASE,iBAAiBP,IAAe;IACvC,MAAMC,QAAQF,SAASC;IACvB,IAAI,CAACC,MAAMO,MAAM,EAAE;QAAC,OAAO,CAAC;IAAC;IAC7B,OAAOC,KAAKC,GAAG,IAAIT,MAAMU,GAAG,CAAC,CAACN,OAASP,eAAec,OAAO,CAACP;AAChE;AAEA;;;CAGC,GACD,OAAO,SAASQ,eAAeb,IAAe,EAAEc,OAAa;IAC3D,OAAOP,iBAAiBP,SAASF,eAAec,OAAO,CAACE;AAC1D;AAEA,kCAAkC,GAClC,OAAO,SAASC,QAAQf,IAAe;IACrC,OAAOa,eAAeb,MAAM;AAC9B;AAEA,6DAA6D,GAC7D,OAAO,SAASgB,SAAShB,IAAe;IACtC,OAAOa,eAAeb,MAAM;AAC9B"}
|
||||||
Vendored
+9
@@ -0,0 +1,9 @@
|
|||||||
|
import type { Field } from 'payload';
|
||||||
|
import type { Role } from './types.js';
|
||||||
|
/**
|
||||||
|
* Builds the fixed `roles` field the plugin injects into the auth collection.
|
||||||
|
*
|
||||||
|
* Saved to the JWT so role checks avoid a database lookup. Only admins can
|
||||||
|
* change roles, preventing privilege escalation by lower-privilege users.
|
||||||
|
*/
|
||||||
|
export declare function buildRolesField(defaultRole?: Role): Field;
|
||||||
Vendored
+32
@@ -0,0 +1,32 @@
|
|||||||
|
import { isAdmin } from './predicates.js';
|
||||||
|
import { ROLE_HIERARCHY } from './types.js';
|
||||||
|
/**
|
||||||
|
* Builds the fixed `roles` field the plugin injects into the auth collection.
|
||||||
|
*
|
||||||
|
* Saved to the JWT so role checks avoid a database lookup. Only admins can
|
||||||
|
* change roles, preventing privilege escalation by lower-privilege users.
|
||||||
|
*/ export function buildRolesField(defaultRole = 'user') {
|
||||||
|
return {
|
||||||
|
name: 'roles',
|
||||||
|
type: 'select',
|
||||||
|
access: {
|
||||||
|
// Only admins may assign or change roles
|
||||||
|
update: ({ req: { user } })=>isAdmin(user)
|
||||||
|
},
|
||||||
|
admin: {
|
||||||
|
description: 'Role hierarchy: admin > editor > user.'
|
||||||
|
},
|
||||||
|
defaultValue: [
|
||||||
|
defaultRole
|
||||||
|
],
|
||||||
|
hasMany: true,
|
||||||
|
options: ROLE_HIERARCHY.map((role)=>({
|
||||||
|
label: role.charAt(0).toUpperCase() + role.slice(1),
|
||||||
|
value: role
|
||||||
|
})),
|
||||||
|
required: true,
|
||||||
|
saveToJWT: true
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
//# sourceMappingURL=rolesField.js.map
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"sources":["../../../src/modules/access/rolesField.ts"],"sourcesContent":["import type { Field } from 'payload'\n\nimport type { Role } from './types.js'\n\nimport { isAdmin } from './predicates.js'\nimport { ROLE_HIERARCHY } from './types.js'\n\n/**\n * Builds the fixed `roles` field the plugin injects into the auth collection.\n *\n * Saved to the JWT so role checks avoid a database lookup. Only admins can\n * change roles, preventing privilege escalation by lower-privilege users.\n */\nexport function buildRolesField(defaultRole: Role = 'user'): Field {\n return {\n name: 'roles',\n type: 'select',\n access: {\n // Only admins may assign or change roles\n update: ({ req: { user } }) => isAdmin(user),\n },\n admin: {\n description: 'Role hierarchy: admin > editor > user.',\n },\n defaultValue: [defaultRole],\n hasMany: true,\n options: ROLE_HIERARCHY.map((role) => ({\n label: role.charAt(0).toUpperCase() + role.slice(1),\n value: role,\n })),\n required: true,\n saveToJWT: true,\n }\n}\n"],"names":["isAdmin","ROLE_HIERARCHY","buildRolesField","defaultRole","name","type","access","update","req","user","admin","description","defaultValue","hasMany","options","map","role","label","charAt","toUpperCase","slice","value","required","saveToJWT"],"mappings":"AAIA,SAASA,OAAO,QAAQ,kBAAiB;AACzC,SAASC,cAAc,QAAQ,aAAY;AAE3C;;;;;CAKC,GACD,OAAO,SAASC,gBAAgBC,cAAoB,MAAM;IACxD,OAAO;QACLC,MAAM;QACNC,MAAM;QACNC,QAAQ;YACN,yCAAyC;YACzCC,QAAQ,CAAC,EAAEC,KAAK,EAAEC,IAAI,EAAE,EAAE,GAAKT,QAAQS;QACzC;QACAC,OAAO;YACLC,aAAa;QACf;QACAC,cAAc;YAACT;SAAY;QAC3BU,SAAS;QACTC,SAASb,eAAec,GAAG,CAAC,CAACC,OAAU,CAAA;gBACrCC,OAAOD,KAAKE,MAAM,CAAC,GAAGC,WAAW,KAAKH,KAAKI,KAAK,CAAC;gBACjDC,OAAOL;YACT,CAAA;QACAM,UAAU;QACVC,WAAW;IACb;AACF"}
|
||||||
Vendored
+19
@@ -0,0 +1,19 @@
|
|||||||
|
/**
|
||||||
|
* Role hierarchy, lowest to highest privilege.
|
||||||
|
* A higher role satisfies any requirement met by a lower one.
|
||||||
|
*/
|
||||||
|
export declare const ROLE_HIERARCHY: readonly ["user", "editor", "admin"];
|
||||||
|
export type Role = (typeof ROLE_HIERARCHY)[number];
|
||||||
|
/**
|
||||||
|
* Access-control options.
|
||||||
|
*
|
||||||
|
* The plugin injects a fixed `roles` field into the client's auth collection
|
||||||
|
* — the collection itself belongs to the client (create-payload-app), the
|
||||||
|
* role definition belongs to the plugin.
|
||||||
|
*/
|
||||||
|
export type AccessOption = {
|
||||||
|
/** Slug of the client's auth collection, e.g. 'users'. */
|
||||||
|
authCollection: string;
|
||||||
|
/** Role assigned to new users. Defaults to 'user'. */
|
||||||
|
defaultRole?: Role;
|
||||||
|
};
|
||||||
Vendored
+10
@@ -0,0 +1,10 @@
|
|||||||
|
/**
|
||||||
|
* Role hierarchy, lowest to highest privilege.
|
||||||
|
* A higher role satisfies any requirement met by a lower one.
|
||||||
|
*/ export const ROLE_HIERARCHY = [
|
||||||
|
'user',
|
||||||
|
'editor',
|
||||||
|
'admin'
|
||||||
|
];
|
||||||
|
|
||||||
|
//# sourceMappingURL=types.js.map
|
||||||
Vendored
+1
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"sources":["../../../src/modules/access/types.ts"],"sourcesContent":["/**\n * Role hierarchy, lowest to highest privilege.\n * A higher role satisfies any requirement met by a lower one.\n */\nexport const ROLE_HIERARCHY = ['user', 'editor', 'admin'] as const\n\nexport type Role = (typeof ROLE_HIERARCHY)[number]\n\n/**\n * Access-control options.\n *\n * The plugin injects a fixed `roles` field into the client's auth collection\n * — the collection itself belongs to the client (create-payload-app), the\n * role definition belongs to the plugin.\n */\nexport type AccessOption = {\n /** Slug of the client's auth collection, e.g. 'users'. */\n authCollection: string\n /** Role assigned to new users. Defaults to 'user'. */\n defaultRole?: Role\n}\n"],"names":["ROLE_HIERARCHY"],"mappings":"AAAA;;;CAGC,GACD,OAAO,MAAMA,iBAAiB;IAAC;IAAQ;IAAU;CAAQ,CAAS"}
|
||||||
+19
@@ -0,0 +1,19 @@
|
|||||||
|
import type { AnalyticsConfig } from './types.js';
|
||||||
|
declare global {
|
||||||
|
interface Window {
|
||||||
|
dataLayer?: unknown[];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Loads Google Analytics — GTM if a container ID is set, otherwise GA4 gtag.
|
||||||
|
*
|
||||||
|
* Consent Mode is initialised to the visitor's stored consent BEFORE the tag
|
||||||
|
* loads (via setDefaultConsent from the consent module), so tags respect the
|
||||||
|
* choice from the first hit; useConsent sends an update the moment the visitor
|
||||||
|
* decides. IDs are public and come from SiteIntegrations, passed in as props
|
||||||
|
* (the client project reads them server-side).
|
||||||
|
*
|
||||||
|
* Renders nothing — it only injects the scripts. Mount once, high in the tree
|
||||||
|
* (e.g. root layout), inside ConsentProvider.
|
||||||
|
*/
|
||||||
|
export declare function Analytics({ ga4MeasurementId, gtmContainerId }: AnalyticsConfig): null;
|
||||||
Vendored
+63
@@ -0,0 +1,63 @@
|
|||||||
|
'use client';
|
||||||
|
import { useEffect } from 'react';
|
||||||
|
import { gtag } from '../consent/googleConsent.js';
|
||||||
|
import { CONSENT_COOKIE, DEFAULT_CONSENT, parseConsent, setDefaultConsent } from '../consent/index.js';
|
||||||
|
const GTM_SCRIPT = (id)=>`https://www.googletagmanager.com/gtm.js?id=${id}`;
|
||||||
|
const GA4_SCRIPT = (id)=>`https://www.googletagmanager.com/gtag/js?id=${id}`;
|
||||||
|
/** Reads the current consent state from cookie, or defaults if none. */ function readConsent() {
|
||||||
|
if (typeof document === 'undefined') {
|
||||||
|
return DEFAULT_CONSENT;
|
||||||
|
}
|
||||||
|
const raw = document.cookie.split('; ').find((c)=>c.startsWith(`${CONSENT_COOKIE}=`))?.split('=')[1];
|
||||||
|
return parseConsent(raw) ?? DEFAULT_CONSENT;
|
||||||
|
}
|
||||||
|
function injectScript(src) {
|
||||||
|
if (document.querySelector(`script[src="${src}"]`)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const s = document.createElement('script');
|
||||||
|
s.src = src;
|
||||||
|
s.async = true;
|
||||||
|
document.head.appendChild(s);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Loads Google Analytics — GTM if a container ID is set, otherwise GA4 gtag.
|
||||||
|
*
|
||||||
|
* Consent Mode is initialised to the visitor's stored consent BEFORE the tag
|
||||||
|
* loads (via setDefaultConsent from the consent module), so tags respect the
|
||||||
|
* choice from the first hit; useConsent sends an update the moment the visitor
|
||||||
|
* decides. IDs are public and come from SiteIntegrations, passed in as props
|
||||||
|
* (the client project reads them server-side).
|
||||||
|
*
|
||||||
|
* Renders nothing — it only injects the scripts. Mount once, high in the tree
|
||||||
|
* (e.g. root layout), inside ConsentProvider.
|
||||||
|
*/ export function Analytics({ ga4MeasurementId, gtmContainerId }) {
|
||||||
|
useEffect(()=>{
|
||||||
|
if (!ga4MeasurementId && !gtmContainerId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 1. Consent Mode default = the visitor's stored choice, before tags load.
|
||||||
|
setDefaultConsent(readConsent());
|
||||||
|
// 2. Load the tag.
|
||||||
|
if (gtmContainerId) {
|
||||||
|
window.dataLayer = window.dataLayer || [];
|
||||||
|
window.dataLayer.push({
|
||||||
|
event: 'gtm.js',
|
||||||
|
'gtm.start': Date.now()
|
||||||
|
});
|
||||||
|
injectScript(GTM_SCRIPT(gtmContainerId));
|
||||||
|
} else if (ga4MeasurementId) {
|
||||||
|
injectScript(GA4_SCRIPT(ga4MeasurementId));
|
||||||
|
// Uses the shared gtag (pushes `arguments`, as Google's tags expect);
|
||||||
|
// real gtag.js wires itself up once the script loads.
|
||||||
|
gtag('js', new Date());
|
||||||
|
gtag('config', ga4MeasurementId);
|
||||||
|
}
|
||||||
|
}, [
|
||||||
|
ga4MeasurementId,
|
||||||
|
gtmContainerId
|
||||||
|
]);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
//# sourceMappingURL=Analytics.js.map
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"sources":["../../../src/modules/analytics/Analytics.tsx"],"sourcesContent":["'use client'\nimport { useEffect } from 'react'\n\nimport type { AnalyticsConfig } from './types.js'\n\nimport { gtag } from '../consent/googleConsent.js'\nimport { CONSENT_COOKIE, DEFAULT_CONSENT, parseConsent, setDefaultConsent } from '../consent/index.js'\n\ndeclare global {\n interface Window {\n dataLayer?: unknown[]\n }\n}\n\nconst GTM_SCRIPT = (id: string) => `https://www.googletagmanager.com/gtm.js?id=${id}`\nconst GA4_SCRIPT = (id: string) => `https://www.googletagmanager.com/gtag/js?id=${id}`\n\n/** Reads the current consent state from cookie, or defaults if none. */\nfunction readConsent() {\n if (typeof document === 'undefined') {return DEFAULT_CONSENT}\n const raw = document.cookie\n .split('; ')\n .find((c) => c.startsWith(`${CONSENT_COOKIE}=`))\n ?.split('=')[1]\n return parseConsent(raw) ?? DEFAULT_CONSENT\n}\n\nfunction injectScript(src: string) {\n if (document.querySelector(`script[src=\"${src}\"]`)) {return}\n const s = document.createElement('script')\n s.src = src\n s.async = true\n document.head.appendChild(s)\n}\n\n/**\n * Loads Google Analytics — GTM if a container ID is set, otherwise GA4 gtag.\n *\n * Consent Mode is initialised to the visitor's stored consent BEFORE the tag\n * loads (via setDefaultConsent from the consent module), so tags respect the\n * choice from the first hit; useConsent sends an update the moment the visitor\n * decides. IDs are public and come from SiteIntegrations, passed in as props\n * (the client project reads them server-side).\n *\n * Renders nothing — it only injects the scripts. Mount once, high in the tree\n * (e.g. root layout), inside ConsentProvider.\n */\nexport function Analytics({ ga4MeasurementId, gtmContainerId }: AnalyticsConfig) {\n useEffect(() => {\n if (!ga4MeasurementId && !gtmContainerId) {return}\n\n // 1. Consent Mode default = the visitor's stored choice, before tags load.\n setDefaultConsent(readConsent())\n\n // 2. Load the tag.\n if (gtmContainerId) {\n window.dataLayer = window.dataLayer || []\n window.dataLayer.push({ event: 'gtm.js', 'gtm.start': Date.now() })\n injectScript(GTM_SCRIPT(gtmContainerId))\n } else if (ga4MeasurementId) {\n injectScript(GA4_SCRIPT(ga4MeasurementId))\n // Uses the shared gtag (pushes `arguments`, as Google's tags expect);\n // real gtag.js wires itself up once the script loads.\n gtag('js', new Date())\n gtag('config', ga4MeasurementId)\n }\n }, [ga4MeasurementId, gtmContainerId])\n\n return null\n}\n"],"names":["useEffect","gtag","CONSENT_COOKIE","DEFAULT_CONSENT","parseConsent","setDefaultConsent","GTM_SCRIPT","id","GA4_SCRIPT","readConsent","document","raw","cookie","split","find","c","startsWith","injectScript","src","querySelector","s","createElement","async","head","appendChild","Analytics","ga4MeasurementId","gtmContainerId","window","dataLayer","push","event","Date","now"],"mappings":"AAAA;AACA,SAASA,SAAS,QAAQ,QAAO;AAIjC,SAASC,IAAI,QAAQ,8BAA6B;AAClD,SAASC,cAAc,EAAEC,eAAe,EAAEC,YAAY,EAAEC,iBAAiB,QAAS,sBAAqB;AAQvG,MAAMC,aAAa,CAACC,KAAe,CAAC,2CAA2C,EAAEA,IAAI;AACrF,MAAMC,aAAa,CAACD,KAAe,CAAC,4CAA4C,EAAEA,IAAI;AAEtF,sEAAsE,GACtE,SAASE;IACP,IAAI,OAAOC,aAAa,aAAa;QAAC,OAAOP;IAAe;IAC5D,MAAMQ,MAAMD,SAASE,MAAM,CACxBC,KAAK,CAAC,MACNC,IAAI,CAAC,CAACC,IAAMA,EAAEC,UAAU,CAAC,GAAGd,eAAe,CAAC,CAAC,IAC5CW,MAAM,IAAI,CAAC,EAAE;IACjB,OAAOT,aAAaO,QAAQR;AAC9B;AAEA,SAASc,aAAaC,GAAW;IAC/B,IAAIR,SAASS,aAAa,CAAC,CAAC,YAAY,EAAED,IAAI,EAAE,CAAC,GAAG;QAAC;IAAM;IAC3D,MAAME,IAAIV,SAASW,aAAa,CAAC;IACjCD,EAAEF,GAAG,GAAGA;IACRE,EAAEE,KAAK,GAAG;IACVZ,SAASa,IAAI,CAACC,WAAW,CAACJ;AAC5B;AAEA;;;;;;;;;;;CAWC,GACD,OAAO,SAASK,UAAU,EAAEC,gBAAgB,EAAEC,cAAc,EAAmB;IAC7E3B,UAAU;QACR,IAAI,CAAC0B,oBAAoB,CAACC,gBAAgB;YAAC;QAAM;QAEjD,2EAA2E;QAC3EtB,kBAAkBI;QAElB,mBAAmB;QACnB,IAAIkB,gBAAgB;YAClBC,OAAOC,SAAS,GAAGD,OAAOC,SAAS,IAAI,EAAE;YACzCD,OAAOC,SAAS,CAACC,IAAI,CAAC;gBAAEC,OAAO;gBAAU,aAAaC,KAAKC,GAAG;YAAG;YACjEhB,aAAaX,WAAWqB;QAC1B,OAAO,IAAID,kBAAkB;YAC3BT,aAAaT,WAAWkB;YACxB,sEAAsE;YACtE,sDAAsD;YACtDzB,KAAK,MAAM,IAAI+B;YACf/B,KAAK,UAAUyB;QACjB;IACF,GAAG;QAACA;QAAkBC;KAAe;IAErC,OAAO;AACT"}
|
||||||
Vendored
+1
@@ -0,0 +1 @@
|
|||||||
|
export { Analytics } from './Analytics.js';
|
||||||
Vendored
+4
@@ -0,0 +1,4 @@
|
|||||||
|
'use client';
|
||||||
|
export { Analytics } from './Analytics.js';
|
||||||
|
|
||||||
|
//# sourceMappingURL=client.js.map
|
||||||
Vendored
+1
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"sources":["../../../src/modules/analytics/client.ts"],"sourcesContent":["'use client'\nexport { Analytics } from './Analytics.js'\n"],"names":["Analytics"],"mappings":"AAAA;AACA,SAASA,SAAS,QAAQ,iBAAgB"}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import type { BasePayload } from 'payload';
|
||||||
|
import type { AnalyticsConfig } from './types.js';
|
||||||
|
/**
|
||||||
|
* Reads the public analytics IDs from SiteIntegrations, server-side.
|
||||||
|
*
|
||||||
|
* Returns only the two public IDs (GA4 / GTM) — safe to pass to the client
|
||||||
|
* <Analytics> component. Does NOT expose any secret from SiteIntegrations.
|
||||||
|
*/
|
||||||
|
export declare function getAnalyticsConfig(payload: BasePayload): Promise<AnalyticsConfig>;
|
||||||
+15
@@ -0,0 +1,15 @@
|
|||||||
|
import { getSiteIntegrations } from '../payload/index.js';
|
||||||
|
/**
|
||||||
|
* Reads the public analytics IDs from SiteIntegrations, server-side.
|
||||||
|
*
|
||||||
|
* Returns only the two public IDs (GA4 / GTM) — safe to pass to the client
|
||||||
|
* <Analytics> component. Does NOT expose any secret from SiteIntegrations.
|
||||||
|
*/ export async function getAnalyticsConfig(payload) {
|
||||||
|
const integrations = await getSiteIntegrations(payload);
|
||||||
|
return {
|
||||||
|
ga4MeasurementId: integrations.ga4MeasurementId ?? null,
|
||||||
|
gtmContainerId: integrations.gtmContainerId ?? null
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
//# sourceMappingURL=getAnalyticsConfig.js.map
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"sources":["../../../src/modules/analytics/getAnalyticsConfig.ts"],"sourcesContent":["import type { BasePayload } from 'payload'\n\nimport type { AnalyticsConfig } from './types.js'\n\nimport { getSiteIntegrations } from '../payload/index.js'\n\n/**\n * Reads the public analytics IDs from SiteIntegrations, server-side.\n *\n * Returns only the two public IDs (GA4 / GTM) — safe to pass to the client\n * <Analytics> component. Does NOT expose any secret from SiteIntegrations.\n */\nexport async function getAnalyticsConfig(payload: BasePayload): Promise<AnalyticsConfig> {\n const integrations = await getSiteIntegrations<AnalyticsConfig>(payload)\n return {\n ga4MeasurementId: integrations.ga4MeasurementId ?? null,\n gtmContainerId: integrations.gtmContainerId ?? null,\n }\n}\n"],"names":["getSiteIntegrations","getAnalyticsConfig","payload","integrations","ga4MeasurementId","gtmContainerId"],"mappings":"AAIA,SAASA,mBAAmB,QAAQ,sBAAqB;AAEzD;;;;;CAKC,GACD,OAAO,eAAeC,mBAAmBC,OAAoB;IAC3D,MAAMC,eAAe,MAAMH,oBAAqCE;IAChE,OAAO;QACLE,kBAAkBD,aAAaC,gBAAgB,IAAI;QACnDC,gBAAgBF,aAAaE,cAAc,IAAI;IACjD;AACF"}
|
||||||
Vendored
+2
@@ -0,0 +1,2 @@
|
|||||||
|
export { getAnalyticsConfig } from './getAnalyticsConfig.js';
|
||||||
|
export type { AnalyticsConfig } from './types.js';
|
||||||
Vendored
+3
@@ -0,0 +1,3 @@
|
|||||||
|
export { getAnalyticsConfig } from './getAnalyticsConfig.js';
|
||||||
|
|
||||||
|
//# sourceMappingURL=index.js.map
|
||||||
Vendored
+1
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"sources":["../../../src/modules/analytics/index.ts"],"sourcesContent":["export { getAnalyticsConfig } from './getAnalyticsConfig.js'\n// Server-safe: config type + the helper that reads IDs from SiteIntegrations.\n// The <Analytics> component is client-side — exported via ./client.\nexport type { AnalyticsConfig } from './types.js'\n"],"names":["getAnalyticsConfig"],"mappings":"AAAA,SAASA,kBAAkB,QAAQ,0BAAyB"}
|
||||||
Vendored
+9
@@ -0,0 +1,9 @@
|
|||||||
|
/**
|
||||||
|
* Analytics IDs, read from SiteIntegrations (public — safe on the client).
|
||||||
|
*/
|
||||||
|
export type AnalyticsConfig = {
|
||||||
|
/** GA4 Measurement ID, e.g. 'G-XXXXXXXXXX'. */
|
||||||
|
ga4MeasurementId?: null | string;
|
||||||
|
/** GTM Container ID, e.g. 'GTM-XXXXXXX'. */
|
||||||
|
gtmContainerId?: null | string;
|
||||||
|
};
|
||||||
Vendored
+5
@@ -0,0 +1,5 @@
|
|||||||
|
/**
|
||||||
|
* Analytics IDs, read from SiteIntegrations (public — safe on the client).
|
||||||
|
*/ export { };
|
||||||
|
|
||||||
|
//# sourceMappingURL=types.js.map
|
||||||
Vendored
+1
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"sources":["../../../src/modules/analytics/types.ts"],"sourcesContent":["/**\n * Analytics IDs, read from SiteIntegrations (public — safe on the client).\n */\nexport type AnalyticsConfig = {\n /** GA4 Measurement ID, e.g. 'G-XXXXXXXXXX'. */\n ga4MeasurementId?: null | string\n /** GTM Container ID, e.g. 'GTM-XXXXXXX'. */\n gtmContainerId?: null | string\n}\n"],"names":[],"mappings":"AAAA;;CAEC,GACD,WAKC"}
|
||||||
+32
@@ -0,0 +1,32 @@
|
|||||||
|
import type { BlockComponentMap, BlockData, EnhanceProps } from './types.js';
|
||||||
|
export type RenderBlocksProps = {
|
||||||
|
/** Block data array from a Payload document (e.g. page.layout). */
|
||||||
|
blocks: BlockData[] | null | undefined;
|
||||||
|
/** Client-provided map of blockType → component. */
|
||||||
|
components: BlockComponentMap;
|
||||||
|
/**
|
||||||
|
* Optional client hook to inject block-specific props (nav anchors, etc.).
|
||||||
|
* Keeps the engine generic — the plugin knows no concrete block types.
|
||||||
|
*/
|
||||||
|
enhanceProps?: EnhanceProps;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Generic, server-side block renderer.
|
||||||
|
*
|
||||||
|
* Iterates a document's blocks and renders each via the client's component
|
||||||
|
* map. Unknown blocks are skipped (null), never thrown. Block-specific logic is
|
||||||
|
* delegated to the client's optional `enhanceProps` — the plugin itself is
|
||||||
|
* block-agnostic.
|
||||||
|
*
|
||||||
|
* The client owns components and block schemas (they pass `components` and
|
||||||
|
* define blocks in their config); the plugin owns only the iteration and
|
||||||
|
* wiring. No wrapper markup is added — spacing/layout belong to the client's
|
||||||
|
* components.
|
||||||
|
*
|
||||||
|
* Nested blocks: a block that needs to render child blocks should render its
|
||||||
|
* own <RenderBlocks> and import the registry itself, rather than receiving the
|
||||||
|
* component map as a prop. Passing the map as a prop breaks React Server
|
||||||
|
* Components — functions (client components) can't cross the server→client
|
||||||
|
* boundary via props.
|
||||||
|
*/
|
||||||
|
export declare function RenderBlocks({ blocks, components, enhanceProps }: RenderBlocksProps): import("react/jsx-runtime").JSX.Element | null;
|
||||||
Vendored
+44
@@ -0,0 +1,44 @@
|
|||||||
|
import { jsx as _jsx } from "react/jsx-runtime";
|
||||||
|
import { Fragment } from 'react';
|
||||||
|
/**
|
||||||
|
* Generic, server-side block renderer.
|
||||||
|
*
|
||||||
|
* Iterates a document's blocks and renders each via the client's component
|
||||||
|
* map. Unknown blocks are skipped (null), never thrown. Block-specific logic is
|
||||||
|
* delegated to the client's optional `enhanceProps` — the plugin itself is
|
||||||
|
* block-agnostic.
|
||||||
|
*
|
||||||
|
* The client owns components and block schemas (they pass `components` and
|
||||||
|
* define blocks in their config); the plugin owns only the iteration and
|
||||||
|
* wiring. No wrapper markup is added — spacing/layout belong to the client's
|
||||||
|
* components.
|
||||||
|
*
|
||||||
|
* Nested blocks: a block that needs to render child blocks should render its
|
||||||
|
* own <RenderBlocks> and import the registry itself, rather than receiving the
|
||||||
|
* component map as a prop. Passing the map as a prop breaks React Server
|
||||||
|
* Components — functions (client components) can't cross the server→client
|
||||||
|
* boundary via props.
|
||||||
|
*/ export function RenderBlocks({ blocks, components, enhanceProps }) {
|
||||||
|
if (!blocks || !Array.isArray(blocks) || blocks.length === 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return /*#__PURE__*/ _jsx(Fragment, {
|
||||||
|
children: blocks.map((block, index)=>{
|
||||||
|
const Component = components[block.blockType];
|
||||||
|
if (!Component) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const extra = enhanceProps ? enhanceProps({
|
||||||
|
allBlocks: blocks,
|
||||||
|
block,
|
||||||
|
index
|
||||||
|
}) : {};
|
||||||
|
return /*#__PURE__*/ _jsx(Component, {
|
||||||
|
...block,
|
||||||
|
...extra
|
||||||
|
}, index);
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
//# sourceMappingURL=RenderBlocks.js.map
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"sources":["../../../src/modules/blocks/RenderBlocks.tsx"],"sourcesContent":["import { Fragment } from 'react'\n\nimport type { BlockComponentMap, BlockData, EnhanceProps } from './types.js'\n\nexport type RenderBlocksProps = {\n /** Block data array from a Payload document (e.g. page.layout). */\n blocks: BlockData[] | null | undefined\n /** Client-provided map of blockType → component. */\n components: BlockComponentMap\n /**\n * Optional client hook to inject block-specific props (nav anchors, etc.).\n * Keeps the engine generic — the plugin knows no concrete block types.\n */\n enhanceProps?: EnhanceProps\n}\n\n/**\n * Generic, server-side block renderer.\n *\n * Iterates a document's blocks and renders each via the client's component\n * map. Unknown blocks are skipped (null), never thrown. Block-specific logic is\n * delegated to the client's optional `enhanceProps` — the plugin itself is\n * block-agnostic.\n *\n * The client owns components and block schemas (they pass `components` and\n * define blocks in their config); the plugin owns only the iteration and\n * wiring. No wrapper markup is added — spacing/layout belong to the client's\n * components.\n *\n * Nested blocks: a block that needs to render child blocks should render its\n * own <RenderBlocks> and import the registry itself, rather than receiving the\n * component map as a prop. Passing the map as a prop breaks React Server\n * Components — functions (client components) can't cross the server→client\n * boundary via props.\n */\nexport function RenderBlocks({ blocks, components, enhanceProps }: RenderBlocksProps) {\n if (!blocks || !Array.isArray(blocks) || blocks.length === 0) {\n return null\n }\n\n return (\n <Fragment>\n {blocks.map((block, index) => {\n const Component = components[block.blockType]\n if (!Component) {return null}\n\n const extra = enhanceProps ? enhanceProps({ allBlocks: blocks, block, index }) : {}\n\n return <Component key={index} {...block} {...extra} />\n })}\n </Fragment>\n )\n}\n"],"names":["Fragment","RenderBlocks","blocks","components","enhanceProps","Array","isArray","length","map","block","index","Component","blockType","extra","allBlocks"],"mappings":";AAAA,SAASA,QAAQ,QAAQ,QAAO;AAgBhC;;;;;;;;;;;;;;;;;;CAkBC,GACD,OAAO,SAASC,aAAa,EAAEC,MAAM,EAAEC,UAAU,EAAEC,YAAY,EAAqB;IAClF,IAAI,CAACF,UAAU,CAACG,MAAMC,OAAO,CAACJ,WAAWA,OAAOK,MAAM,KAAK,GAAG;QAC5D,OAAO;IACT;IAEA,qBACE,KAACP;kBACEE,OAAOM,GAAG,CAAC,CAACC,OAAOC;YAClB,MAAMC,YAAYR,UAAU,CAACM,MAAMG,SAAS,CAAC;YAC7C,IAAI,CAACD,WAAW;gBAAC,OAAO;YAAI;YAE5B,MAAME,QAAQT,eAAeA,aAAa;gBAAEU,WAAWZ;gBAAQO;gBAAOC;YAAM,KAAK,CAAC;YAElF,qBAAO,KAACC;gBAAuB,GAAGF,KAAK;gBAAG,GAAGI,KAAK;eAA3BH;QACzB;;AAGN"}
|
||||||
Vendored
+3
@@ -0,0 +1,3 @@
|
|||||||
|
export { RenderBlocks } from './RenderBlocks.js';
|
||||||
|
export type { RenderBlocksProps } from './RenderBlocks.js';
|
||||||
|
export type { BlockComponentMap, BlockData, EnhanceProps } from './types.js';
|
||||||
Vendored
+3
@@ -0,0 +1,3 @@
|
|||||||
|
export { RenderBlocks } from './RenderBlocks.js';
|
||||||
|
|
||||||
|
//# sourceMappingURL=index.js.map
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user