@pro-laico/payload-seed 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/SeedButton.d.ts +5 -6
- package/dist/components/SeedButton.d.ts.map +1 -1
- package/dist/components/SeedButton.js +8 -78
- package/dist/components/SeedButton.js.map +1 -1
- package/dist/components/SeedButtonClient.d.ts +11 -0
- package/dist/components/SeedButtonClient.d.ts.map +1 -0
- package/dist/components/SeedButtonClient.js +81 -0
- package/dist/components/SeedButtonClient.js.map +1 -0
- package/dist/endpoint.d.ts.map +1 -1
- package/dist/endpoint.js +9 -1
- package/dist/endpoint.js.map +1 -1
- package/dist/engine/run.d.ts +4 -0
- package/dist/engine/run.d.ts.map +1 -1
- package/dist/engine/run.js +88 -30
- package/dist/engine/run.js.map +1 -1
- package/dist/engine/validate.d.ts +7 -0
- package/dist/engine/validate.d.ts.map +1 -1
- package/dist/engine/validate.js +9 -0
- package/dist/engine/validate.js.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -1
- package/dist/listeners.d.ts +22 -0
- package/dist/listeners.d.ts.map +1 -0
- package/dist/listeners.js +22 -0
- package/dist/listeners.js.map +1 -0
- package/package.json +1 -1
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import type React from 'react';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
endpoint?: string;
|
|
5
|
-
}
|
|
2
|
+
import { type SeedButtonProps } from './SeedButtonClient';
|
|
3
|
+
export type { SeedButtonProps };
|
|
6
4
|
/** Admin dashboard button that triggers `POST /api/seed`. Injected via the plugin's
|
|
7
|
-
* `adminButton` option, or registered manually in `admin.components.
|
|
8
|
-
*
|
|
5
|
+
* `adminButton` option, or registered manually in `admin.components.actions`.
|
|
6
|
+
* Server component: renders nothing unless the `ENABLE_SEED` guard allows seeding,
|
|
7
|
+
* so environments where the endpoint would refuse anyway never show the button. */
|
|
9
8
|
export declare const SeedButton: React.FC<SeedButtonProps>;
|
|
10
9
|
export default SeedButton;
|
|
11
10
|
//# sourceMappingURL=SeedButton.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SeedButton.d.ts","sourceRoot":"","sources":["../../src/components/SeedButton.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"SeedButton.d.ts","sourceRoot":"","sources":["../../src/components/SeedButton.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAE9B,OAAO,EAAoB,KAAK,eAAe,EAAE,MAAM,oBAAoB,CAAA;AAE3E,YAAY,EAAE,eAAe,EAAE,CAAA;AAE/B;;;oFAGoF;AACpF,eAAO,MAAM,UAAU,EAAE,KAAK,CAAC,EAAE,CAAC,eAAe,CAA0E,CAAA;AAE3H,eAAe,UAAU,CAAA"}
|
|
@@ -1,82 +1,12 @@
|
|
|
1
|
-
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { useCallback, useState } from "react";
|
|
5
|
-
const SuccessMessage = ()=>/*#__PURE__*/ _jsxs("div", {
|
|
6
|
-
children: [
|
|
7
|
-
"Database seeded! You can now",
|
|
8
|
-
' ',
|
|
9
|
-
/*#__PURE__*/ _jsx("a", {
|
|
10
|
-
target: "_blank",
|
|
11
|
-
href: "/",
|
|
12
|
-
rel: "noreferrer",
|
|
13
|
-
children: "visit your website"
|
|
14
|
-
})
|
|
15
|
-
]
|
|
16
|
-
});
|
|
17
|
-
const MAX_SHOWN_ISSUES = 5;
|
|
18
|
-
const summarizeIssues = (issues)=>{
|
|
19
|
-
const shown = issues.slice(0, MAX_SHOWN_ISSUES);
|
|
20
|
-
const more = issues.length - shown.length;
|
|
21
|
-
return shown.join('\n') + (more > 0 ? `\n…and ${more} more` : '');
|
|
22
|
-
};
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { seedingEnabled } from "../guard.js";
|
|
3
|
+
import { SeedButtonClient } from "./SeedButtonClient.js";
|
|
23
4
|
/** Admin dashboard button that triggers `POST /api/seed`. Injected via the plugin's
|
|
24
|
-
* `adminButton` option, or registered manually in `admin.components.
|
|
25
|
-
*
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
const [loading, setLoading] = useState(false);
|
|
30
|
-
const [error, setError] = useState(null);
|
|
31
|
-
const [confirming, setConfirming] = useState(false);
|
|
32
|
-
const handleClick = useCallback((e)=>{
|
|
33
|
-
e.preventDefault();
|
|
34
|
-
if (loading) return toast.info('Seeding already in progress.');
|
|
35
|
-
if (!confirming) return setConfirming(true);
|
|
36
|
-
setConfirming(false);
|
|
37
|
-
setError(null);
|
|
38
|
-
setLoading(true);
|
|
39
|
-
const run = fetch(url, {
|
|
40
|
-
method: 'POST',
|
|
41
|
-
credentials: 'include'
|
|
42
|
-
}).then(async (res)=>{
|
|
43
|
-
const body = await res.json().catch(()=>({}));
|
|
44
|
-
if (!res.ok) {
|
|
45
|
-
const base = body.error ?? 'An error occurred while seeding.';
|
|
46
|
-
throw new Error(body.issues?.length ? `${base}\n${summarizeIssues(body.issues)}` : base);
|
|
47
|
-
}
|
|
48
|
-
setSeeded(true);
|
|
49
|
-
return body;
|
|
50
|
-
}).catch((err)=>{
|
|
51
|
-
const wrapped = err instanceof Error ? err : new Error(String(err));
|
|
52
|
-
setError(wrapped.message);
|
|
53
|
-
throw wrapped;
|
|
54
|
-
}).finally(()=>setLoading(false));
|
|
55
|
-
toast.promise(run, {
|
|
56
|
-
loading: 'Seeding with data....',
|
|
57
|
-
success: (body)=>body.message ?? /*#__PURE__*/ _jsx(SuccessMessage, {}),
|
|
58
|
-
error: (err)=>err instanceof Error ? err.message : 'An error occurred while seeding.'
|
|
59
|
-
});
|
|
60
|
-
}, [
|
|
61
|
-
loading,
|
|
62
|
-
confirming,
|
|
63
|
-
url
|
|
64
|
-
]);
|
|
65
|
-
let message = '';
|
|
66
|
-
if (seeded) message = ' (done — click to reseed)';
|
|
67
|
-
if (error) message = ` (error: ${error} — click to retry)`;
|
|
68
|
-
if (confirming) message = ' — click again to confirm: this wipes seeded collections';
|
|
69
|
-
if (loading) message = ' (seeding...)';
|
|
70
|
-
return /*#__PURE__*/ _jsxs("button", {
|
|
71
|
-
type: "button",
|
|
72
|
-
onClick: handleClick,
|
|
73
|
-
disabled: loading,
|
|
74
|
-
children: [
|
|
75
|
-
"Seed your database",
|
|
76
|
-
message
|
|
77
|
-
]
|
|
78
|
-
});
|
|
79
|
-
};
|
|
5
|
+
* `adminButton` option, or registered manually in `admin.components.actions`.
|
|
6
|
+
* Server component: renders nothing unless the `ENABLE_SEED` guard allows seeding,
|
|
7
|
+
* so environments where the endpoint would refuse anyway never show the button. */ export const SeedButton = (props)=>seedingEnabled() ? /*#__PURE__*/ _jsx(SeedButtonClient, {
|
|
8
|
+
...props
|
|
9
|
+
}) : null;
|
|
80
10
|
export default SeedButton;
|
|
81
11
|
|
|
82
12
|
//# sourceMappingURL=SeedButton.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/SeedButton.tsx"],"sourcesContent":["
|
|
1
|
+
{"version":3,"sources":["../../src/components/SeedButton.tsx"],"sourcesContent":["import type React from 'react'\nimport { seedingEnabled } from '../guard'\nimport { SeedButtonClient, type SeedButtonProps } from './SeedButtonClient'\n\nexport type { SeedButtonProps }\n\n/** Admin dashboard button that triggers `POST /api/seed`. Injected via the plugin's\n * `adminButton` option, or registered manually in `admin.components.actions`.\n * Server component: renders nothing unless the `ENABLE_SEED` guard allows seeding,\n * so environments where the endpoint would refuse anyway never show the button. */\nexport const SeedButton: React.FC<SeedButtonProps> = (props) => (seedingEnabled() ? <SeedButtonClient {...props} /> : null)\n\nexport default SeedButton\n"],"names":["seedingEnabled","SeedButtonClient","SeedButton","props"],"mappings":";AACA,SAASA,cAAc,QAAQ,cAAU;AACzC,SAASC,gBAAgB,QAA8B,wBAAoB;AAI3E;;;kFAGkF,GAClF,OAAO,MAAMC,aAAwC,CAACC,QAAWH,iCAAmB,KAACC;QAAkB,GAAGE,KAAK;SAAO,KAAK;AAE3H,eAAeD,WAAU"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type React from 'react';
|
|
2
|
+
export interface SeedButtonProps {
|
|
3
|
+
/** Endpoint URL the button POSTs to. Defaults to `<routes.api>/seed` from the admin config. */
|
|
4
|
+
endpoint?: string;
|
|
5
|
+
}
|
|
6
|
+
/** The client half of the seed button — assumes seeding is enabled (the `SeedButton` server
|
|
7
|
+
* gate checks `ENABLE_SEED` and renders this only when the endpoint would accept the run).
|
|
8
|
+
* The seed wipes seeded collections, so the first click arms a confirm and the second runs. */
|
|
9
|
+
export declare const SeedButtonClient: React.FC<SeedButtonProps>;
|
|
10
|
+
export default SeedButtonClient;
|
|
11
|
+
//# sourceMappingURL=SeedButtonClient.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SeedButtonClient.d.ts","sourceRoot":"","sources":["../../src/components/SeedButtonClient.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AA0B9B,MAAM,WAAW,eAAe;IAC9B,+FAA+F;IAC/F,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAED;;gGAEgG;AAChG,eAAO,MAAM,gBAAgB,EAAE,KAAK,CAAC,EAAE,CAAC,eAAe,CAuDtD,CAAA;AAED,eAAe,gBAAgB,CAAA"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { toast, useConfig } from "@payloadcms/ui";
|
|
4
|
+
import { useCallback, useState } from "react";
|
|
5
|
+
const SuccessMessage = ()=>/*#__PURE__*/ _jsxs("div", {
|
|
6
|
+
children: [
|
|
7
|
+
"Database seeded! You can now",
|
|
8
|
+
' ',
|
|
9
|
+
/*#__PURE__*/ _jsx("a", {
|
|
10
|
+
target: "_blank",
|
|
11
|
+
href: "/",
|
|
12
|
+
rel: "noreferrer",
|
|
13
|
+
children: "visit your website"
|
|
14
|
+
})
|
|
15
|
+
]
|
|
16
|
+
});
|
|
17
|
+
const MAX_SHOWN_ISSUES = 5;
|
|
18
|
+
const summarizeIssues = (issues)=>{
|
|
19
|
+
const shown = issues.slice(0, MAX_SHOWN_ISSUES);
|
|
20
|
+
const more = issues.length - shown.length;
|
|
21
|
+
return shown.join('\n') + (more > 0 ? `\n…and ${more} more` : '');
|
|
22
|
+
};
|
|
23
|
+
/** The client half of the seed button — assumes seeding is enabled (the `SeedButton` server
|
|
24
|
+
* gate checks `ENABLE_SEED` and renders this only when the endpoint would accept the run).
|
|
25
|
+
* The seed wipes seeded collections, so the first click arms a confirm and the second runs. */ export const SeedButtonClient = ({ endpoint })=>{
|
|
26
|
+
const { config } = useConfig();
|
|
27
|
+
const url = endpoint ?? `${config.serverURL ?? ''}${config.routes.api}/seed`;
|
|
28
|
+
const [seeded, setSeeded] = useState(false);
|
|
29
|
+
const [loading, setLoading] = useState(false);
|
|
30
|
+
const [failed, setFailed] = useState(false);
|
|
31
|
+
const [confirming, setConfirming] = useState(false);
|
|
32
|
+
const handleClick = useCallback((e)=>{
|
|
33
|
+
e.preventDefault();
|
|
34
|
+
if (loading) return toast.info('Seeding already in progress.');
|
|
35
|
+
if (!confirming) return setConfirming(true);
|
|
36
|
+
setConfirming(false);
|
|
37
|
+
setFailed(false);
|
|
38
|
+
setLoading(true);
|
|
39
|
+
const run = fetch(url, {
|
|
40
|
+
method: 'POST',
|
|
41
|
+
credentials: 'include'
|
|
42
|
+
}).then(async (res)=>{
|
|
43
|
+
const body = await res.json().catch(()=>({}));
|
|
44
|
+
if (!res.ok) {
|
|
45
|
+
const base = body.error ?? 'An error occurred while seeding.';
|
|
46
|
+
throw new Error(body.issues?.length ? `${base}\n${summarizeIssues(body.issues)}` : base);
|
|
47
|
+
}
|
|
48
|
+
setSeeded(true);
|
|
49
|
+
return body;
|
|
50
|
+
}).catch((err)=>{
|
|
51
|
+
setFailed(true);
|
|
52
|
+
throw err instanceof Error ? err : new Error(String(err));
|
|
53
|
+
}).finally(()=>setLoading(false));
|
|
54
|
+
toast.promise(run, {
|
|
55
|
+
loading: 'Seeding with data....',
|
|
56
|
+
success: (body)=>body.message ?? /*#__PURE__*/ _jsx(SuccessMessage, {}),
|
|
57
|
+
error: (err)=>err instanceof Error ? err.message : 'An error occurred while seeding.'
|
|
58
|
+
});
|
|
59
|
+
}, [
|
|
60
|
+
loading,
|
|
61
|
+
confirming,
|
|
62
|
+
url
|
|
63
|
+
]);
|
|
64
|
+
let message = '';
|
|
65
|
+
if (seeded) message = ' (done — click to reseed)';
|
|
66
|
+
if (failed) message = ' (failed — click to retry)';
|
|
67
|
+
if (confirming) message = ' — click again to confirm: this wipes seeded collections';
|
|
68
|
+
if (loading) message = ' (seeding...)';
|
|
69
|
+
return /*#__PURE__*/ _jsxs("button", {
|
|
70
|
+
type: "button",
|
|
71
|
+
onClick: handleClick,
|
|
72
|
+
disabled: loading,
|
|
73
|
+
children: [
|
|
74
|
+
"Seed your database",
|
|
75
|
+
message
|
|
76
|
+
]
|
|
77
|
+
});
|
|
78
|
+
};
|
|
79
|
+
export default SeedButtonClient;
|
|
80
|
+
|
|
81
|
+
//# sourceMappingURL=SeedButtonClient.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/components/SeedButtonClient.tsx"],"sourcesContent":["'use client'\n\nimport { toast, useConfig } from '@payloadcms/ui'\nimport type React from 'react'\nimport { useCallback, useState } from 'react'\n\nconst SuccessMessage: React.FC = () => (\n <div>\n Database seeded! You can now{' '}\n <a target=\"_blank\" href=\"/\" rel=\"noreferrer\">\n visit your website\n </a>\n </div>\n)\n\nconst MAX_SHOWN_ISSUES = 5\n\nconst summarizeIssues = (issues: string[]): string => {\n const shown = issues.slice(0, MAX_SHOWN_ISSUES)\n const more = issues.length - shown.length\n return shown.join('\\n') + (more > 0 ? `\\n…and ${more} more` : '')\n}\n\ninterface SeedResponseBody {\n error?: string\n issues?: string[]\n message?: string\n}\n\nexport interface SeedButtonProps {\n /** Endpoint URL the button POSTs to. Defaults to `<routes.api>/seed` from the admin config. */\n endpoint?: string\n}\n\n/** The client half of the seed button — assumes seeding is enabled (the `SeedButton` server\n * gate checks `ENABLE_SEED` and renders this only when the endpoint would accept the run).\n * The seed wipes seeded collections, so the first click arms a confirm and the second runs. */\nexport const SeedButtonClient: React.FC<SeedButtonProps> = ({ endpoint }) => {\n const { config } = useConfig()\n const url = endpoint ?? `${config.serverURL ?? ''}${config.routes.api}/seed`\n const [seeded, setSeeded] = useState(false)\n const [loading, setLoading] = useState(false)\n const [failed, setFailed] = useState(false)\n const [confirming, setConfirming] = useState(false)\n\n const handleClick = useCallback(\n (e: React.MouseEvent<HTMLButtonElement>) => {\n e.preventDefault()\n\n if (loading) return toast.info('Seeding already in progress.')\n if (!confirming) return setConfirming(true)\n\n setConfirming(false)\n setFailed(false)\n setLoading(true)\n\n const run = fetch(url, { method: 'POST', credentials: 'include' })\n .then(async (res) => {\n const body = (await res.json().catch(() => ({}))) as SeedResponseBody\n if (!res.ok) {\n const base = body.error ?? 'An error occurred while seeding.'\n throw new Error(body.issues?.length ? `${base}\\n${summarizeIssues(body.issues)}` : base)\n }\n setSeeded(true)\n return body\n })\n .catch((err) => {\n setFailed(true)\n throw err instanceof Error ? err : new Error(String(err))\n })\n .finally(() => setLoading(false))\n\n toast.promise(run, {\n loading: 'Seeding with data....',\n success: (body) => body.message ?? <SuccessMessage />,\n error: (err) => (err instanceof Error ? err.message : 'An error occurred while seeding.'),\n })\n },\n [loading, confirming, url],\n )\n\n let message = ''\n if (seeded) message = ' (done — click to reseed)'\n if (failed) message = ' (failed — click to retry)'\n if (confirming) message = ' — click again to confirm: this wipes seeded collections'\n if (loading) message = ' (seeding...)'\n\n return (\n <button type=\"button\" onClick={handleClick} disabled={loading}>\n Seed your database{message}\n </button>\n )\n}\n\nexport default SeedButtonClient\n"],"names":["toast","useConfig","useCallback","useState","SuccessMessage","div","a","target","href","rel","MAX_SHOWN_ISSUES","summarizeIssues","issues","shown","slice","more","length","join","SeedButtonClient","endpoint","config","url","serverURL","routes","api","seeded","setSeeded","loading","setLoading","failed","setFailed","confirming","setConfirming","handleClick","e","preventDefault","info","run","fetch","method","credentials","then","res","body","json","catch","ok","base","error","Error","err","String","finally","promise","success","message","button","type","onClick","disabled"],"mappings":"AAAA;;AAEA,SAASA,KAAK,EAAEC,SAAS,QAAQ,iBAAgB;AAEjD,SAASC,WAAW,EAAEC,QAAQ,QAAQ,QAAO;AAE7C,MAAMC,iBAA2B,kBAC/B,MAACC;;YAAI;YAC0B;0BAC7B,KAACC;gBAAEC,QAAO;gBAASC,MAAK;gBAAIC,KAAI;0BAAa;;;;AAMjD,MAAMC,mBAAmB;AAEzB,MAAMC,kBAAkB,CAACC;IACvB,MAAMC,QAAQD,OAAOE,KAAK,CAAC,GAAGJ;IAC9B,MAAMK,OAAOH,OAAOI,MAAM,GAAGH,MAAMG,MAAM;IACzC,OAAOH,MAAMI,IAAI,CAAC,QAASF,CAAAA,OAAO,IAAI,CAAC,OAAO,EAAEA,KAAK,KAAK,CAAC,GAAG,EAAC;AACjE;AAaA;;8FAE8F,GAC9F,OAAO,MAAMG,mBAA8C,CAAC,EAAEC,QAAQ,EAAE;IACtE,MAAM,EAAEC,MAAM,EAAE,GAAGnB;IACnB,MAAMoB,MAAMF,YAAY,GAAGC,OAAOE,SAAS,IAAI,KAAKF,OAAOG,MAAM,CAACC,GAAG,CAAC,KAAK,CAAC;IAC5E,MAAM,CAACC,QAAQC,UAAU,GAAGvB,SAAS;IACrC,MAAM,CAACwB,SAASC,WAAW,GAAGzB,SAAS;IACvC,MAAM,CAAC0B,QAAQC,UAAU,GAAG3B,SAAS;IACrC,MAAM,CAAC4B,YAAYC,cAAc,GAAG7B,SAAS;IAE7C,MAAM8B,cAAc/B,YAClB,CAACgC;QACCA,EAAEC,cAAc;QAEhB,IAAIR,SAAS,OAAO3B,MAAMoC,IAAI,CAAC;QAC/B,IAAI,CAACL,YAAY,OAAOC,cAAc;QAEtCA,cAAc;QACdF,UAAU;QACVF,WAAW;QAEX,MAAMS,MAAMC,MAAMjB,KAAK;YAAEkB,QAAQ;YAAQC,aAAa;QAAU,GAC7DC,IAAI,CAAC,OAAOC;YACX,MAAMC,OAAQ,MAAMD,IAAIE,IAAI,GAAGC,KAAK,CAAC,IAAO,CAAA,CAAC,CAAA;YAC7C,IAAI,CAACH,IAAII,EAAE,EAAE;gBACX,MAAMC,OAAOJ,KAAKK,KAAK,IAAI;gBAC3B,MAAM,IAAIC,MAAMN,KAAK/B,MAAM,EAAEI,SAAS,GAAG+B,KAAK,EAAE,EAAEpC,gBAAgBgC,KAAK/B,MAAM,GAAG,GAAGmC;YACrF;YACArB,UAAU;YACV,OAAOiB;QACT,GACCE,KAAK,CAAC,CAACK;YACNpB,UAAU;YACV,MAAMoB,eAAeD,QAAQC,MAAM,IAAID,MAAME,OAAOD;QACtD,GACCE,OAAO,CAAC,IAAMxB,WAAW;QAE5B5B,MAAMqD,OAAO,CAAChB,KAAK;YACjBV,SAAS;YACT2B,SAAS,CAACX,OAASA,KAAKY,OAAO,kBAAI,KAACnD;YACpC4C,OAAO,CAACE,MAASA,eAAeD,QAAQC,IAAIK,OAAO,GAAG;QACxD;IACF,GACA;QAAC5B;QAASI;QAAYV;KAAI;IAG5B,IAAIkC,UAAU;IACd,IAAI9B,QAAQ8B,UAAU;IACtB,IAAI1B,QAAQ0B,UAAU;IACtB,IAAIxB,YAAYwB,UAAU;IAC1B,IAAI5B,SAAS4B,UAAU;IAEvB,qBACE,MAACC;QAAOC,MAAK;QAASC,SAASzB;QAAa0B,UAAUhC;;YAAS;YAC1C4B;;;AAGzB,EAAC;AAED,eAAerC,iBAAgB"}
|
package/dist/endpoint.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"endpoint.d.ts","sourceRoot":"","sources":["../src/endpoint.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AAIvC,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAA;AAEpD;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,mBAAmB,GAAG,QAAQ,
|
|
1
|
+
{"version":3,"file":"endpoint.d.ts","sourceRoot":"","sources":["../src/endpoint.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AAIvC,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAA;AAEpD;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,mBAAmB,GAAG,QAAQ,CAqBzE"}
|
package/dist/endpoint.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { runSeed } from "./engine/run.js";
|
|
2
|
-
import { SeedValidationError } from "./engine/validate.js";
|
|
2
|
+
import { SeedRunError, SeedValidationError } from "./engine/validate.js";
|
|
3
3
|
import { SEED_DISABLED_MESSAGE, seedingEnabled } from "./guard.js";
|
|
4
4
|
/**
|
|
5
5
|
* Builds `POST /api/seed`. Gated by the `ENABLE_SEED` runtime guard and requires an
|
|
@@ -47,6 +47,14 @@ import { SEED_DISABLED_MESSAGE, seedingEnabled } from "./guard.js";
|
|
|
47
47
|
}, {
|
|
48
48
|
status: 400
|
|
49
49
|
});
|
|
50
|
+
if (e instanceof SeedRunError) return Response.json({
|
|
51
|
+
error: 'Error seeding data.',
|
|
52
|
+
issues: [
|
|
53
|
+
e.detail
|
|
54
|
+
]
|
|
55
|
+
}, {
|
|
56
|
+
status: 500
|
|
57
|
+
});
|
|
50
58
|
return Response.json({
|
|
51
59
|
error: 'Error seeding data.'
|
|
52
60
|
}, {
|
package/dist/endpoint.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/endpoint.ts"],"sourcesContent":["import type { Endpoint } from 'payload'\nimport { runSeed } from './engine/run'\nimport { SeedValidationError } from './engine/validate'\nimport { SEED_DISABLED_MESSAGE, seedingEnabled } from './guard'\nimport type { ResolvedSeedOptions } from './options'\n\n/**\n * Builds `POST /api/seed`. Gated by the `ENABLE_SEED` runtime guard and requires an\n * authenticated user (any user — not necessarily an admin). Each write sets\n * `context.disableRevalidate`, so app revalidate hooks skip during the run; the engine\n * does no final revalidation.\n */\nexport function createSeedEndpoint(options: ResolvedSeedOptions): Endpoint {\n return {\n path: '/seed',\n method: 'post',\n handler: async (req) => {\n if (!seedingEnabled()) return Response.json({ error: SEED_DISABLED_MESSAGE }, { status: 403 })\n if (!req.user)\n return Response.json({ error: 'Seeding requires an authenticated Payload user (any user) - log in first.' }, { status: 403 })\n\n try {\n const result = await runSeed({ payload: req.payload, req, options, definitions: options.definitions })\n const message = options.definitions?.length ? undefined : '0 documents created — no seed definitions registered'\n return Response.json({ success: true, ...(message ? { message } : {}), ...result })\n } catch (e) {\n req.payload.logger.error({ err: e, msg: 'Error seeding data' })\n if (e instanceof SeedValidationError) return Response.json({ error: 'Seed validation failed.', issues: e.issues }, { status: 400 })\n return Response.json({ error: 'Error seeding data.' }, { status: 500 })\n }\n },\n }\n}\n"],"names":["runSeed","SeedValidationError","SEED_DISABLED_MESSAGE","seedingEnabled","createSeedEndpoint","options","path","method","handler","req","Response","json","error","status","user","result","payload","definitions","message","length","undefined","success","e","logger","err","msg","issues"],"mappings":"AACA,SAASA,OAAO,QAAQ,kBAAc;AACtC,SAASC,mBAAmB,QAAQ,uBAAmB;
|
|
1
|
+
{"version":3,"sources":["../src/endpoint.ts"],"sourcesContent":["import type { Endpoint } from 'payload'\nimport { runSeed } from './engine/run'\nimport { SeedRunError, SeedValidationError } from './engine/validate'\nimport { SEED_DISABLED_MESSAGE, seedingEnabled } from './guard'\nimport type { ResolvedSeedOptions } from './options'\n\n/**\n * Builds `POST /api/seed`. Gated by the `ENABLE_SEED` runtime guard and requires an\n * authenticated user (any user — not necessarily an admin). Each write sets\n * `context.disableRevalidate`, so app revalidate hooks skip during the run; the engine\n * does no final revalidation.\n */\nexport function createSeedEndpoint(options: ResolvedSeedOptions): Endpoint {\n return {\n path: '/seed',\n method: 'post',\n handler: async (req) => {\n if (!seedingEnabled()) return Response.json({ error: SEED_DISABLED_MESSAGE }, { status: 403 })\n if (!req.user)\n return Response.json({ error: 'Seeding requires an authenticated Payload user (any user) - log in first.' }, { status: 403 })\n\n try {\n const result = await runSeed({ payload: req.payload, req, options, definitions: options.definitions })\n const message = options.definitions?.length ? undefined : '0 documents created — no seed definitions registered'\n return Response.json({ success: true, ...(message ? { message } : {}), ...result })\n } catch (e) {\n req.payload.logger.error({ err: e, msg: 'Error seeding data' })\n if (e instanceof SeedValidationError) return Response.json({ error: 'Seed validation failed.', issues: e.issues }, { status: 400 })\n if (e instanceof SeedRunError) return Response.json({ error: 'Error seeding data.', issues: [e.detail] }, { status: 500 })\n return Response.json({ error: 'Error seeding data.' }, { status: 500 })\n }\n },\n }\n}\n"],"names":["runSeed","SeedRunError","SeedValidationError","SEED_DISABLED_MESSAGE","seedingEnabled","createSeedEndpoint","options","path","method","handler","req","Response","json","error","status","user","result","payload","definitions","message","length","undefined","success","e","logger","err","msg","issues","detail"],"mappings":"AACA,SAASA,OAAO,QAAQ,kBAAc;AACtC,SAASC,YAAY,EAAEC,mBAAmB,QAAQ,uBAAmB;AACrE,SAASC,qBAAqB,EAAEC,cAAc,QAAQ,aAAS;AAG/D;;;;;CAKC,GACD,OAAO,SAASC,mBAAmBC,OAA4B;IAC7D,OAAO;QACLC,MAAM;QACNC,QAAQ;QACRC,SAAS,OAAOC;YACd,IAAI,CAACN,kBAAkB,OAAOO,SAASC,IAAI,CAAC;gBAAEC,OAAOV;YAAsB,GAAG;gBAAEW,QAAQ;YAAI;YAC5F,IAAI,CAACJ,IAAIK,IAAI,EACX,OAAOJ,SAASC,IAAI,CAAC;gBAAEC,OAAO;YAA4E,GAAG;gBAAEC,QAAQ;YAAI;YAE7H,IAAI;gBACF,MAAME,SAAS,MAAMhB,QAAQ;oBAAEiB,SAASP,IAAIO,OAAO;oBAAEP;oBAAKJ;oBAASY,aAAaZ,QAAQY,WAAW;gBAAC;gBACpG,MAAMC,UAAUb,QAAQY,WAAW,EAAEE,SAASC,YAAY;gBAC1D,OAAOV,SAASC,IAAI,CAAC;oBAAEU,SAAS;oBAAM,GAAIH,UAAU;wBAAEA;oBAAQ,IAAI,CAAC,CAAC;oBAAG,GAAGH,MAAM;gBAAC;YACnF,EAAE,OAAOO,GAAG;gBACVb,IAAIO,OAAO,CAACO,MAAM,CAACX,KAAK,CAAC;oBAAEY,KAAKF;oBAAGG,KAAK;gBAAqB;gBAC7D,IAAIH,aAAarB,qBAAqB,OAAOS,SAASC,IAAI,CAAC;oBAAEC,OAAO;oBAA2Bc,QAAQJ,EAAEI,MAAM;gBAAC,GAAG;oBAAEb,QAAQ;gBAAI;gBACjI,IAAIS,aAAatB,cAAc,OAAOU,SAASC,IAAI,CAAC;oBAAEC,OAAO;oBAAuBc,QAAQ;wBAACJ,EAAEK,MAAM;qBAAC;gBAAC,GAAG;oBAAEd,QAAQ;gBAAI;gBACxH,OAAOH,SAASC,IAAI,CAAC;oBAAEC,OAAO;gBAAsB,GAAG;oBAAEC,QAAQ;gBAAI;YACvE;QACF;IACF;AACF"}
|
package/dist/engine/run.d.ts
CHANGED
|
@@ -5,6 +5,10 @@ import { type DeferredField } from './graph';
|
|
|
5
5
|
export interface SeedResult {
|
|
6
6
|
/** Created doc counts keyed by collection slug. */
|
|
7
7
|
created: Record<string, number>;
|
|
8
|
+
/** Collection slugs the run touched — cleared and reseeded, even when zero records were created. */
|
|
9
|
+
collections: string[];
|
|
10
|
+
/** Global slugs the run seeded. */
|
|
11
|
+
globals: string[];
|
|
8
12
|
/** The computed topological create order (doc node ids, `collection:_key`). */
|
|
9
13
|
order: string[];
|
|
10
14
|
/** Fields deferred to break a `ref` cycle: created null, then set in a second pass. */
|
package/dist/engine/run.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../src/engine/run.ts"],"names":[],"mappings":"AAAA,OAAO,EAAuC,KAAK,OAAO,EAAE,KAAK,cAAc,EAAE,MAAM,SAAS,CAAA;
|
|
1
|
+
{"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../src/engine/run.ts"],"names":[],"mappings":"AAAA,OAAO,EAAuC,KAAK,OAAO,EAAE,KAAK,cAAc,EAAE,MAAM,SAAS,CAAA;AAEhG,OAAO,EAAkB,KAAK,mBAAmB,EAAE,KAAK,iBAAiB,EAAE,MAAM,YAAY,CAAA;AAE7F,OAAO,KAAK,EAAmB,cAAc,EAAsB,MAAM,UAAU,CAAA;AACnF,OAAO,EAAyF,KAAK,aAAa,EAAE,MAAM,SAAS,CAAA;AAiDnI,MAAM,WAAW,UAAU;IACzB,mDAAmD;IACnD,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC/B,oGAAoG;IACpG,WAAW,EAAE,MAAM,EAAE,CAAA;IACrB,mCAAmC;IACnC,OAAO,EAAE,MAAM,EAAE,CAAA;IACjB,+EAA+E;IAC/E,KAAK,EAAE,MAAM,EAAE,CAAA;IACf,uFAAuF;IACvF,QAAQ,EAAE,aAAa,EAAE,CAAA;IACzB,sGAAsG;IACtG,OAAO,EAAE,iBAAiB,EAAE,CAAA;CAC7B;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,MAAM,CAAA;CACf;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,OAAO,CAAA;IAChB,GAAG,EAAE,cAAc,CAAA;IACnB,OAAO,EAAE,mBAAmB,CAAA;IAC5B,0EAA0E;IAC1E,WAAW,CAAC,EAAE,cAAc,EAAE,CAAA;CAC/B;AAyJD;;;;;;;;;GASG;AACH,wBAAsB,OAAO,CAAC,EAAE,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,WAAW,EAAE,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC,CAwKtG;AAED;;;GAGG;AACH,wBAAsB,IAAI,CAAC,IAAI,EAAE;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,GAAG,CAAC,EAAE,cAAc,CAAC;IAAC,OAAO,CAAC,EAAE,iBAAiB,CAAA;CAAE,GAAG,OAAO,CAAC,UAAU,CAAC,CAG7H"}
|
package/dist/engine/run.js
CHANGED
|
@@ -1,10 +1,52 @@
|
|
|
1
1
|
import { createLocalReq } from "payload";
|
|
2
|
+
import { notifyAfterSeed } from "../listeners.js";
|
|
2
3
|
import { resolveOptions } from "../options.js";
|
|
3
4
|
import { file, isFileToken, isRef, ref } from "../refs.js";
|
|
4
5
|
import { buildGraph } from "./graph.js";
|
|
5
6
|
import { resolveFilePath, readFileAsUpload, searchedDirs } from "./files.js";
|
|
6
7
|
import { collectTokens, docNodeId, resolveTokens } from "./tokens.js";
|
|
7
|
-
import { SeedValidationError, validateModel } from "./validate.js";
|
|
8
|
+
import { SeedRunError, SeedValidationError, validateModel } from "./validate.js";
|
|
9
|
+
/** Unwrap a thrown error to its deepest `cause` and return one trimmed line. The ORM wraps the
|
|
10
|
+
* driver error ("Failed query: …"), hiding the actionable part (e.g. "NOT NULL constraint failed:
|
|
11
|
+
* projects_gallery.image_id") — this surfaces it. Payload's ValidationError likewise buries the
|
|
12
|
+
* per-field messages in `data.errors` ("The following field is invalid: status" says nothing) —
|
|
13
|
+
* those are appended too. */ function deepestReason(err, fallback) {
|
|
14
|
+
let deepest = err instanceof Error ? err : undefined;
|
|
15
|
+
while(deepest?.cause instanceof Error)deepest = deepest.cause;
|
|
16
|
+
let msg = deepest?.message ?? fallback ?? String(err);
|
|
17
|
+
const data = deepest?.data;
|
|
18
|
+
if (data?.errors?.length) {
|
|
19
|
+
const fields = data.errors.map((e)=>`${e.path ?? e.field ?? '?'}: ${e.message ?? '?'}`).join('; ');
|
|
20
|
+
msg = `${msg} — ${fields}`;
|
|
21
|
+
}
|
|
22
|
+
return msg.replace(/\s+/g, ' ').slice(0, 300);
|
|
23
|
+
}
|
|
24
|
+
/** A human label for a doc that couldn't be cleared. The seed only holds the prior run's opaque id,
|
|
25
|
+
* so it looks the doc up (it still exists — the delete failed) and reports its `admin.useAsTitle`
|
|
26
|
+
* value, else a common identifying field, else the upload filename — falling back to the bare id. */ async function describeFailedDoc(payload, req, slug, useAsTitle, id) {
|
|
27
|
+
try {
|
|
28
|
+
// Through `unknown`: in an app context findByID returns the app's generated union, which
|
|
29
|
+
// needn't overlap with a plain record (e.g. PayloadMigration has no index signature).
|
|
30
|
+
const doc = await payload.findByID({
|
|
31
|
+
collection: slug,
|
|
32
|
+
id,
|
|
33
|
+
req,
|
|
34
|
+
overrideAccess: true,
|
|
35
|
+
depth: 0
|
|
36
|
+
});
|
|
37
|
+
const label = [
|
|
38
|
+
useAsTitle ? doc[useAsTitle] : undefined,
|
|
39
|
+
doc.title,
|
|
40
|
+
doc.name,
|
|
41
|
+
doc.slug,
|
|
42
|
+
doc.filename
|
|
43
|
+
].find((v)=>typeof v === 'string' && v.trim().length > 0);
|
|
44
|
+
if (label) return `"${label}" [${id}]`;
|
|
45
|
+
} catch {
|
|
46
|
+
// The doc's gone or unreadable — the bare id is the best we can do.
|
|
47
|
+
}
|
|
48
|
+
return `[${id}]`;
|
|
49
|
+
}
|
|
8
50
|
const tokens = {
|
|
9
51
|
ref,
|
|
10
52
|
file
|
|
@@ -145,19 +187,18 @@ async function clearCollection(payload, req, collection) {
|
|
|
145
187
|
disableTransaction: true
|
|
146
188
|
});
|
|
147
189
|
} catch (err) {
|
|
148
|
-
//
|
|
149
|
-
//
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
const reason = (deepest?.message ?? e.message ?? String(err)).replace(/\s+/g, ' ').slice(0, 300);
|
|
190
|
+
// A prior-run generated id tells no story, so resolve it to the doc's admin title (the delete
|
|
191
|
+
// failed — the doc still exists to look up), alongside the deepest driver cause.
|
|
192
|
+
const reason = deepestReason(err, e.message);
|
|
193
|
+
const label = await describeFailedDoc(payload, req, collection, config.admin?.useAsTitle, e.id);
|
|
153
194
|
failed.push({
|
|
154
|
-
|
|
195
|
+
label,
|
|
155
196
|
reason
|
|
156
197
|
});
|
|
157
198
|
}
|
|
158
199
|
}
|
|
159
200
|
if (failed.length) {
|
|
160
|
-
const detail = failed.map((f)=>`${f.
|
|
201
|
+
const detail = failed.map((f)=>`${f.label}: ${f.reason}`).join(' | ');
|
|
161
202
|
payload.logger.warn(`[payload-seed] could not clear ${failed.length} doc(s) in '${collection}' — these STALE docs now sit beside the fresh seed; re-run the seed or delete them in the admin. Reasons: ${detail}`);
|
|
162
203
|
}
|
|
163
204
|
} else {
|
|
@@ -319,14 +360,19 @@ async function clearCollection(payload, req, collection) {
|
|
|
319
360
|
}
|
|
320
361
|
}
|
|
321
362
|
payload.logger.info(`[payload-seed] seeding '${nodeId}'`);
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
363
|
+
let doc;
|
|
364
|
+
try {
|
|
365
|
+
doc = await payload.create({
|
|
366
|
+
collection: slug,
|
|
367
|
+
data: data,
|
|
368
|
+
...uploadFile ? {
|
|
369
|
+
file: uploadFile
|
|
370
|
+
} : {},
|
|
371
|
+
...baseArgs
|
|
372
|
+
});
|
|
373
|
+
} catch (err) {
|
|
374
|
+
throw new SeedRunError(`creating '${nodeId}': ${deepestReason(err)}`);
|
|
375
|
+
}
|
|
330
376
|
docIds.set(nodeId, doc.id);
|
|
331
377
|
created[slug] = (created[slug] ?? 0) + 1;
|
|
332
378
|
}
|
|
@@ -341,14 +387,18 @@ async function clearCollection(payload, req, collection) {
|
|
|
341
387
|
docs: docIds,
|
|
342
388
|
where: `${node}.${field}`
|
|
343
389
|
});
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
390
|
+
try {
|
|
391
|
+
await payload.update({
|
|
392
|
+
collection: entry.slug,
|
|
393
|
+
id,
|
|
394
|
+
data: {
|
|
395
|
+
[field]: value
|
|
396
|
+
},
|
|
397
|
+
...baseArgs
|
|
398
|
+
});
|
|
399
|
+
} catch (err) {
|
|
400
|
+
throw new SeedRunError(`setting deferred field '${node}.${field}': ${deepestReason(err)}`);
|
|
401
|
+
}
|
|
352
402
|
}
|
|
353
403
|
}
|
|
354
404
|
// Update globals after all docs exist.
|
|
@@ -358,19 +408,27 @@ async function clearCollection(payload, req, collection) {
|
|
|
358
408
|
docs: docIds,
|
|
359
409
|
where: `global:${g.slug}`
|
|
360
410
|
});
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
411
|
+
try {
|
|
412
|
+
await payload.updateGlobal({
|
|
413
|
+
slug: g.slug,
|
|
414
|
+
data: data,
|
|
415
|
+
...baseArgs
|
|
416
|
+
});
|
|
417
|
+
} catch (err) {
|
|
418
|
+
throw new SeedRunError(`updating global '${g.slug}': ${deepestReason(err)}`);
|
|
419
|
+
}
|
|
366
420
|
}
|
|
367
421
|
payload.logger.info('[payload-seed] seed complete.');
|
|
368
|
-
|
|
422
|
+
const result = {
|
|
369
423
|
created,
|
|
424
|
+
collections: model.collections.map((c)=>c.slug),
|
|
425
|
+
globals: model.globals.map((g)=>g.slug),
|
|
370
426
|
order,
|
|
371
427
|
deferred,
|
|
372
428
|
skipped
|
|
373
429
|
};
|
|
430
|
+
await notifyAfterSeed(payload, req, result);
|
|
431
|
+
return result;
|
|
374
432
|
}
|
|
375
433
|
/**
|
|
376
434
|
* CLI / Local-API convenience: run the seed from a script (`payload run`) or test. Builds
|
package/dist/engine/run.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/engine/run.ts"],"sourcesContent":["import { type CollectionSlug, createLocalReq, type Payload, type PayloadRequest } from 'payload'\nimport { resolveOptions, type ResolvedSeedOptions, type SeedPluginOptions } from '../options'\nimport { file, isFileToken, isRef, ref } from '../refs'\nimport type { SeedAssetMarker, SeedDefinition, SeedDisabledMarker } from '../types'\nimport { type BuiltCollection, type BuiltGlobal, type BuiltModel, type BuiltRecord, buildGraph, type DeferredField } from './graph'\nimport { resolveFilePath, readFileAsUpload, searchedDirs } from './files'\nimport { collectTokens, docNodeId, resolveTokens } from './tokens'\nimport { SeedValidationError, validateModel } from './validate'\n\nexport interface SeedResult {\n /** Created doc counts keyed by collection slug. */\n created: Record<string, number>\n /** The computed topological create order (doc node ids, `collection:_key`). */\n order: string[]\n /** Fields deferred to break a `ref` cycle: created null, then set in a second pass. */\n deferred: DeferredField[]\n /** Definitions skipped this run (their own `disabled`, or the collection's `custom.seedDisabled`). */\n skipped: SkippedDefinition[]\n}\n\nexport interface SkippedDefinition {\n slug: string\n reason: string\n}\n\nexport interface RunSeedArgs {\n payload: Payload\n req: PayloadRequest\n options: ResolvedSeedOptions\n /** Seed definitions. Falls back to `options.definitions` when omitted. */\n definitions?: SeedDefinition[]\n}\n\nconst tokens = { ref, file }\n\n/** A `custom.seedAsset` collection, resolved to its effective source field (subdir defaults are\n * applied at lookup, alongside `assetSubDirs`, so they match native uploads). */\ninterface AssetCollection {\n sourceField: string\n subdir?: string\n}\n\n/** Discover asset collections from the live config: any collection whose `custom.seedAsset` is set.\n * A `_file` on one of these is handed to the collection's ingest hook via `sourceField` instead of\n * uploaded as bytes. Replaces the old `assetProviders` plugin option — declaration now lives on the\n * collection, so the seed plugin needs no knowledge of the owning plugins. */\nfunction discoverAssetCollections(payload: Payload): Map<string, AssetCollection> {\n const map = new Map<string, AssetCollection>()\n for (const slug of Object.keys(payload.collections)) {\n const marker = payload.collections[slug as CollectionSlug]?.config.custom?.seedAsset as SeedAssetMarker | undefined\n if (!marker) continue\n const m = marker === true ? {} : marker\n map.set(slug, { sourceField: m.sourceField ?? 'source', subdir: m.subdir })\n }\n return map\n}\n\n/** Split definitions by kind and build the concrete model (records + their `_file`, and globals). */\nfunction buildModel(definitions: SeedDefinition[]): BuiltModel {\n const collections: BuiltCollection[] = []\n const globals: BuiltGlobal[] = []\n\n for (const def of definitions) {\n if (def.kind === 'collection') {\n const records: BuiltRecord[] = def.build(tokens).map((rec) => {\n const { _key, _file, ...data } = rec as { _key: string; _file?: unknown } & Record<string, unknown>\n return { key: _key, file: isFileToken(_file) ? _file : undefined, data }\n })\n collections.push({ slug: def.slug, records })\n } else if (def.kind === 'global') {\n globals.push({ slug: def.slug, data: def.build(tokens) as Record<string, unknown> })\n }\n }\n\n return { collections, globals }\n}\n\n/** Split definitions into runnable and skipped. A definition is skipped when its own `disabled` is\n * set, or when its target collection declares `custom.seedDisabled` (e.g. a plugin detecting\n * missing credentials at config time). Skipped definitions still shaped the generated seed-ref\n * types — only the run drops them, so types stay stable across environments. */\nfunction partitionDefinitions(payload: Payload, defs: SeedDefinition[]): { active: SeedDefinition[]; skipped: SkippedDefinition[] } {\n const active: SeedDefinition[] = []\n const skipped: SkippedDefinition[] = []\n for (const def of defs) {\n const fromCollection =\n def.kind === 'collection'\n ? (payload.collections[def.slug as CollectionSlug]?.config.custom?.seedDisabled as SeedDisabledMarker | undefined)\n : undefined\n const flag = def.disabled || fromCollection\n if (!flag) {\n active.push(def)\n continue\n }\n const reason = typeof flag === 'string' ? flag : 'disabled'\n skipped.push({ slug: def.slug, reason })\n payload.logger.warn(`[payload-seed] skipping '${def.slug}': ${reason}`)\n }\n return { active, skipped }\n}\n\n/** Drop every optional field whose value contains a `ref()` into a skipped collection (warning per\n * drop — the doc won't exist this run), and hard-error when such a ref sits on a required field.\n * Runs before validation, so the remaining model checks clean. */\nfunction stripRefsToSkipped(payload: Payload, model: BuiltModel, skipped: SkippedDefinition[], requiredFields: Map<string, Set<string>>): void {\n if (!skipped.length) return\n const reasonBySlug = new Map(skipped.map((s) => [s.slug, s.reason]))\n const issues: string[] = []\n\n const strip = (where: string, slug: string | undefined, data: Record<string, unknown>) => {\n for (const [field, value] of Object.entries(data)) {\n const hit = collectTokens(value).find((t) => isRef(t) && reasonBySlug.has(t.collection))\n if (!hit || !isRef(hit)) continue\n if (slug && requiredFields.get(slug)?.has(field)) {\n issues.push(\n `${where}.${field}: required, but ref('${hit.collection}', '${hit.key}') targets a skipped definition (${reasonBySlug.get(hit.collection)}).`,\n )\n continue\n }\n delete data[field]\n payload.logger.warn(\n `[payload-seed] dropping entire field '${field}' on ${where} (contains ref('${hit.collection}', '${hit.key}') to skipped '${hit.collection}': ${reasonBySlug.get(hit.collection)}).`,\n )\n }\n }\n\n for (const coll of model.collections) for (const rec of coll.records) strip(docNodeId(coll.slug, rec.key), coll.slug, rec.data)\n for (const g of model.globals) strip(`global:${g.slug}`, undefined, g.data)\n\n if (issues.length) throw new SeedValidationError(issues)\n}\n\nasync function clearCollection(payload: Payload, req: PayloadRequest, collection: string): Promise<void> {\n const config = payload.collections[collection as CollectionSlug]?.config\n if (!config) return\n payload.logger.info(`[payload-seed] clearing ${collection}`)\n // Delete via the Local API (firing hooks) when clearing must cascade — an upload collection (to\n // remove stored bytes) or any collection with a before/after-delete hook (e.g. `mux-video`'s Mux\n // cleanup, `font`'s cascade to its originals + optimized). Otherwise wipe rows directly.\n const withHooks = Boolean(config.upload || config.hooks?.beforeDelete?.length || config.hooks?.afterDelete?.length)\n if (withHooks) {\n const result = (await payload.delete({\n collection: collection as CollectionSlug,\n where: { id: { exists: true } },\n req,\n overrideAccess: true,\n context: { disableRevalidate: true },\n disableTransaction: true,\n })) as { errors?: Array<{ id?: string | number; message?: string }> }\n // Payload's bulk delete does NOT throw on per-doc failures — it returns them in `errors`.\n // Retry each once, then warn LOUDLY with the underlying reasons — a silent partial wipe\n // would leave stale docs sitting beside the fresh seed.\n const failed: Array<{ id: string | number; reason: string }> = []\n for (const e of result?.errors ?? []) {\n if (e.id == null) continue\n try {\n await payload.delete({\n collection: collection as CollectionSlug,\n id: e.id,\n req,\n overrideAccess: true,\n context: { disableRevalidate: true },\n disableTransaction: true,\n })\n } catch (err) {\n // Surface the DEEPEST cause: the ORM wraps the driver error (\"Failed query: …\"), which\n // hides the actionable part (e.g. \"NOT NULL constraint failed: projects_gallery.image_id\").\n let deepest = err instanceof Error ? err : undefined\n while (deepest?.cause instanceof Error) deepest = deepest.cause\n const reason = (deepest?.message ?? e.message ?? String(err)).replace(/\\s+/g, ' ').slice(0, 300)\n failed.push({ id: e.id, reason })\n }\n }\n if (failed.length) {\n const detail = failed.map((f) => `${f.id}: ${f.reason}`).join(' | ')\n payload.logger.warn(\n `[payload-seed] could not clear ${failed.length} doc(s) in '${collection}' — these STALE docs now sit beside the fresh seed; re-run the seed or delete them in the admin. Reasons: ${detail}`,\n )\n }\n } else {\n await payload.db.deleteMany({ collection: collection as CollectionSlug, req, where: {} })\n }\n if (config.versions) await payload.db.deleteVersions({ collection: collection as CollectionSlug, req, where: {} })\n}\n\n/**\n * The seed engine. Takes the seed definitions, skips the disabled ones (their own `disabled`, or\n * the collection's `custom.seedDisabled` — dropping optional refs that point at them), builds the\n * model, validates references against the live config, topologically sorts the dependency graph,\n * clears the seeded collections, then creates docs in order — resolving `ref` tokens to ids and\n * delivering each doc's `_file` as a native upload (upload collections) or a source-field value\n * (`custom.seedAsset` collections). A `ref` cycle is broken by deferring an optional field, which a\n * second pass sets once every doc exists (a cycle with only required fields is a hard error).\n * Globals are updated last.\n */\nexport async function runSeed({ payload, req, options, definitions }: RunSeedArgs): Promise<SeedResult> {\n const defs = definitions ?? options.definitions ?? []\n if (defs.length === 0) payload.logger.warn('[payload-seed] no seed definitions: pass `definitions` to seedPlugin() or seed().')\n\n // Drop disabled definitions (their own `disabled`, or the collection's `custom.seedDisabled` —\n // e.g. payload-mux without credentials). They still shaped the generated seed-ref types.\n const { active, skipped } = partitionDefinitions(payload, defs)\n\n const model = buildModel(active)\n const collectionSlugs = new Set(Object.keys(payload.collections))\n\n const isUpload = (slug: string): boolean => Boolean(payload.collections[slug as CollectionSlug]?.config.upload)\n const assetBySlug = discoverAssetCollections(payload)\n\n // Collections a `_file` may sit on: every upload collection plus every `custom.seedAsset` collection.\n const fileCollections = new Set<string>([...collectionSlugs].filter(isUpload))\n for (const slug of assetBySlug.keys()) fileCollections.add(slug)\n\n // Valid top-level field names per node (for unknown-field detection) plus the required ones (so a\n // ref cycle can only be broken by deferring an optional field) — read from the live config.\n const fieldNames = new Map<string, Set<string>>()\n const requiredFields = new Map<string, Set<string>>()\n for (const coll of model.collections) {\n const cfg = payload.collections[coll.slug as CollectionSlug]?.config\n if (!cfg) continue\n fieldNames.set(coll.slug, new Set(cfg.flattenedFields.map((f) => f.name)))\n requiredFields.set(coll.slug, new Set(cfg.flattenedFields.filter((f) => (f as { required?: boolean }).required).map((f) => f.name)))\n }\n for (const g of model.globals) {\n const cfg = payload.config.globals.find((gc) => gc.slug === g.slug)\n if (cfg) fieldNames.set(`global:${g.slug}`, new Set(cfg.flattenedFields.map((f) => f.name)))\n }\n\n // Refs into a skipped definition come out of the data before validation: dropped when the field\n // is optional (the run warns; re-seed once the skip is lifted and they fill in), fatal when it's\n // required (the doc can't be created without it).\n stripRefsToSkipped(payload, model, skipped, requiredFields)\n\n const globalSlugs = new Set(payload.config.globals.map((g) => g.slug))\n validateModel({ model, collectionSlugs, globalSlugs, fileCollections, fieldNames })\n const isRequired = (collection: string, field: string): boolean => requiredFields.get(collection)?.has(field) ?? false\n const { order, deferred } = buildGraph(model, { isRequired })\n\n // Fields nulled at create time (their refs point into a cycle) and set in a second pass below.\n const deferredByNode = new Map<string, Set<string>>()\n for (const d of deferred) {\n const set = deferredByNode.get(d.node) ?? new Set<string>()\n set.add(d.field)\n deferredByNode.set(d.node, set)\n }\n\n const baseArgs = { depth: 0, overrideAccess: true, context: { disableRevalidate: true }, req } as const\n\n const docIds = new Map<string, string | number>()\n const recordIndex = new Map<string, { slug: string; record: BuiltRecord }>()\n for (const coll of model.collections)\n for (const rec of coll.records) recordIndex.set(docNodeId(coll.slug, rec.key), { slug: coll.slug, record: rec })\n\n // Clear every seeded collection — dependents BEFORE their dependencies (the REVERSE of creation\n // order). Clearing in creation order deletes referenced docs while the previous run's\n // referencing rows still exist, and on SQL adapters a relationship column can make that delete\n // FAIL outright (e.g. sqlite generates a required in-array upload as NOT NULL with an\n // ON DELETE SET NULL foreign key — nulling it violates the constraint), stranding stale docs\n // beside the fresh seed. `clearCollection` fires delete hooks when the collection needs a\n // cascade (uploads / external-asset cleanup); plain collections are wiped directly.\n const seededCollections = [...new Set(model.collections.map((c) => c.slug))]\n const creationOrder: string[] = []\n const seen = new Set<string>()\n for (const nodeId of order) {\n const slug = recordIndex.get(nodeId)?.slug\n if (slug && !seen.has(slug)) {\n seen.add(slug)\n creationOrder.push(slug)\n }\n }\n // Definitions whose records were all skipped/empty still get cleared (after the ordered ones).\n for (const slug of seededCollections) if (!seen.has(slug)) creationOrder.push(slug)\n payload.logger.info('[payload-seed] clearing collections...')\n for (const slug of [...creationOrder].reverse()) await clearCollection(payload, req, slug)\n\n // Create docs in dependency order, resolving ref tokens to ids and delivering each `_file`.\n const created: Record<string, number> = {}\n\n payload.logger.info('[payload-seed] seeding documents...')\n for (const nodeId of order) {\n const entry = recordIndex.get(nodeId)\n if (!entry) continue\n const { slug, record } = entry\n // Drop any deferred fields before resolving: their refs point into a cycle and aren't created\n // yet. The second pass below sets them once every doc exists.\n const deferFields = deferredByNode.get(nodeId)\n const source = deferFields ? Object.fromEntries(Object.entries(record.data).filter(([k]) => !deferFields.has(k))) : record.data\n let data = resolveTokens(source, { docs: docIds, where: nodeId }) as Record<string, unknown>\n let uploadFile: Awaited<ReturnType<typeof readFileAsUpload>> | undefined\n\n if (record.file) {\n // Both branches resolve the file the same way — under the per-collection subdir (a\n // `custom.seedAsset` `subdir`, else `assetSubDirs`, else the slug), then the assets root.\n const asset = assetBySlug.get(slug)\n const subdir = asset?.subdir ?? options.assetSubDirs[slug] ?? slug\n const subdirs = [subdir, '']\n const path = await resolveFilePath(record.file.name, options.assetsDir, subdirs)\n if (!path) {\n const searched = searchedDirs(record.file.name, options.assetsDir, subdirs).join(', ')\n payload.logger.warn({ msg: `[payload-seed] ${nodeId}: _file '${record.file.name}' not found - skipped. Searched: ${searched}` })\n } else if (asset) {\n // Hand the resolved path + options to the collection's ingest hook via its source field\n // instead of uploading bytes.\n data = { ...data, [asset.sourceField]: { file: path, ...record.file.options } }\n } else if (isUpload(slug)) {\n uploadFile = await readFileAsUpload(path)\n }\n }\n\n payload.logger.info(`[payload-seed] seeding '${nodeId}'`)\n const doc = (await payload.create({\n collection: slug as CollectionSlug,\n data: data as never,\n ...(uploadFile ? { file: uploadFile } : {}),\n ...baseArgs,\n })) as { id: string | number }\n docIds.set(nodeId, doc.id)\n created[slug] = (created[slug] ?? 0) + 1\n }\n\n // Second pass: now every doc exists, resolve and set the fields deferred to break cycles.\n if (deferred.length) {\n payload.logger.info(`[payload-seed] resolving ${deferred.length} deferred reference(s)...`)\n for (const { node, field } of deferred) {\n const entry = recordIndex.get(node)\n const id = docIds.get(node)\n if (!entry || id === undefined) continue\n const value = resolveTokens(entry.record.data[field], { docs: docIds, where: `${node}.${field}` })\n await payload.update({ collection: entry.slug as CollectionSlug, id, data: { [field]: value } as never, ...baseArgs })\n }\n }\n\n // Update globals after all docs exist.\n for (const g of model.globals) {\n payload.logger.info(`[payload-seed] seeding global '${g.slug}'`)\n const data = resolveTokens(g.data, { docs: docIds, where: `global:${g.slug}` }) as Record<string, unknown>\n await payload.updateGlobal({ slug: g.slug as never, data: data as never, ...baseArgs })\n }\n\n payload.logger.info('[payload-seed] seed complete.')\n return { created, order, deferred, skipped }\n}\n\n/**\n * CLI / Local-API convenience: run the seed from a script (`payload run`) or test. Builds\n * a local `req` if one isn't supplied and resolves the public plugin options.\n */\nexport async function seed(args: { payload: Payload; req?: PayloadRequest; options?: SeedPluginOptions }): Promise<SeedResult> {\n const req = args.req ?? (await createLocalReq({}, args.payload))\n return runSeed({ payload: args.payload, req, options: resolveOptions(args.options) })\n}\n"],"names":["createLocalReq","resolveOptions","file","isFileToken","isRef","ref","buildGraph","resolveFilePath","readFileAsUpload","searchedDirs","collectTokens","docNodeId","resolveTokens","SeedValidationError","validateModel","tokens","discoverAssetCollections","payload","map","Map","slug","Object","keys","collections","marker","config","custom","seedAsset","m","set","sourceField","subdir","buildModel","definitions","globals","def","kind","records","build","rec","_key","_file","data","key","undefined","push","partitionDefinitions","defs","active","skipped","fromCollection","seedDisabled","flag","disabled","reason","logger","warn","stripRefsToSkipped","model","requiredFields","length","reasonBySlug","s","issues","strip","where","field","value","entries","hit","find","t","has","collection","get","coll","g","clearCollection","req","info","withHooks","Boolean","upload","hooks","beforeDelete","afterDelete","result","delete","id","exists","overrideAccess","context","disableRevalidate","disableTransaction","failed","e","errors","err","deepest","Error","cause","message","String","replace","slice","detail","f","join","db","deleteMany","versions","deleteVersions","runSeed","options","collectionSlugs","Set","isUpload","assetBySlug","fileCollections","filter","add","fieldNames","cfg","flattenedFields","name","required","gc","globalSlugs","isRequired","order","deferred","deferredByNode","d","node","baseArgs","depth","docIds","recordIndex","record","seededCollections","c","creationOrder","seen","nodeId","reverse","created","entry","deferFields","source","fromEntries","k","docs","uploadFile","asset","assetSubDirs","subdirs","path","assetsDir","searched","msg","doc","create","update","updateGlobal","seed","args"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../src/engine/run.ts"],"sourcesContent":["import { type CollectionSlug, createLocalReq, type Payload, type PayloadRequest } from 'payload'\nimport { notifyAfterSeed } from '../listeners'\nimport { resolveOptions, type ResolvedSeedOptions, type SeedPluginOptions } from '../options'\nimport { file, isFileToken, isRef, ref } from '../refs'\nimport type { SeedAssetMarker, SeedDefinition, SeedDisabledMarker } from '../types'\nimport { type BuiltCollection, type BuiltGlobal, type BuiltModel, type BuiltRecord, buildGraph, type DeferredField } from './graph'\nimport { resolveFilePath, readFileAsUpload, searchedDirs } from './files'\nimport { collectTokens, docNodeId, resolveTokens } from './tokens'\nimport { SeedRunError, SeedValidationError, validateModel } from './validate'\n\n/** Unwrap a thrown error to its deepest `cause` and return one trimmed line. The ORM wraps the\n * driver error (\"Failed query: …\"), hiding the actionable part (e.g. \"NOT NULL constraint failed:\n * projects_gallery.image_id\") — this surfaces it. Payload's ValidationError likewise buries the\n * per-field messages in `data.errors` (\"The following field is invalid: status\" says nothing) —\n * those are appended too. */\nfunction deepestReason(err: unknown, fallback?: string): string {\n let deepest = err instanceof Error ? err : undefined\n while (deepest?.cause instanceof Error) deepest = deepest.cause\n let msg = deepest?.message ?? fallback ?? String(err)\n const data = (deepest as undefined | { data?: { errors?: Array<{ path?: string; field?: string; message?: string }> } })?.data\n if (data?.errors?.length) {\n const fields = data.errors.map((e) => `${e.path ?? e.field ?? '?'}: ${e.message ?? '?'}`).join('; ')\n msg = `${msg} — ${fields}`\n }\n return msg.replace(/\\s+/g, ' ').slice(0, 300)\n}\n\n/** A human label for a doc that couldn't be cleared. The seed only holds the prior run's opaque id,\n * so it looks the doc up (it still exists — the delete failed) and reports its `admin.useAsTitle`\n * value, else a common identifying field, else the upload filename — falling back to the bare id. */\nasync function describeFailedDoc(\n payload: Payload,\n req: PayloadRequest,\n slug: string,\n useAsTitle: string | undefined,\n id: string | number,\n): Promise<string> {\n try {\n // Through `unknown`: in an app context findByID returns the app's generated union, which\n // needn't overlap with a plain record (e.g. PayloadMigration has no index signature).\n const doc = (await payload.findByID({ collection: slug as CollectionSlug, id, req, overrideAccess: true, depth: 0 })) as unknown as Record<\n string,\n unknown\n >\n const label = [useAsTitle ? doc[useAsTitle] : undefined, doc.title, doc.name, doc.slug, doc.filename].find(\n (v): v is string => typeof v === 'string' && v.trim().length > 0,\n )\n if (label) return `\"${label}\" [${id}]`\n } catch {\n // The doc's gone or unreadable — the bare id is the best we can do.\n }\n return `[${id}]`\n}\n\nexport interface SeedResult {\n /** Created doc counts keyed by collection slug. */\n created: Record<string, number>\n /** Collection slugs the run touched — cleared and reseeded, even when zero records were created. */\n collections: string[]\n /** Global slugs the run seeded. */\n globals: string[]\n /** The computed topological create order (doc node ids, `collection:_key`). */\n order: string[]\n /** Fields deferred to break a `ref` cycle: created null, then set in a second pass. */\n deferred: DeferredField[]\n /** Definitions skipped this run (their own `disabled`, or the collection's `custom.seedDisabled`). */\n skipped: SkippedDefinition[]\n}\n\nexport interface SkippedDefinition {\n slug: string\n reason: string\n}\n\nexport interface RunSeedArgs {\n payload: Payload\n req: PayloadRequest\n options: ResolvedSeedOptions\n /** Seed definitions. Falls back to `options.definitions` when omitted. */\n definitions?: SeedDefinition[]\n}\n\nconst tokens = { ref, file }\n\n/** A `custom.seedAsset` collection, resolved to its effective source field (subdir defaults are\n * applied at lookup, alongside `assetSubDirs`, so they match native uploads). */\ninterface AssetCollection {\n sourceField: string\n subdir?: string\n}\n\n/** Discover asset collections from the live config: any collection whose `custom.seedAsset` is set.\n * A `_file` on one of these is handed to the collection's ingest hook via `sourceField` instead of\n * uploaded as bytes. Replaces the old `assetProviders` plugin option — declaration now lives on the\n * collection, so the seed plugin needs no knowledge of the owning plugins. */\nfunction discoverAssetCollections(payload: Payload): Map<string, AssetCollection> {\n const map = new Map<string, AssetCollection>()\n for (const slug of Object.keys(payload.collections)) {\n const marker = payload.collections[slug as CollectionSlug]?.config.custom?.seedAsset as SeedAssetMarker | undefined\n if (!marker) continue\n const m = marker === true ? {} : marker\n map.set(slug, { sourceField: m.sourceField ?? 'source', subdir: m.subdir })\n }\n return map\n}\n\n/** Split definitions by kind and build the concrete model (records + their `_file`, and globals). */\nfunction buildModel(definitions: SeedDefinition[]): BuiltModel {\n const collections: BuiltCollection[] = []\n const globals: BuiltGlobal[] = []\n\n for (const def of definitions) {\n if (def.kind === 'collection') {\n const records: BuiltRecord[] = def.build(tokens).map((rec) => {\n const { _key, _file, ...data } = rec as { _key: string; _file?: unknown } & Record<string, unknown>\n return { key: _key, file: isFileToken(_file) ? _file : undefined, data }\n })\n collections.push({ slug: def.slug, records })\n } else if (def.kind === 'global') {\n globals.push({ slug: def.slug, data: def.build(tokens) as Record<string, unknown> })\n }\n }\n\n return { collections, globals }\n}\n\n/** Split definitions into runnable and skipped. A definition is skipped when its own `disabled` is\n * set, or when its target collection declares `custom.seedDisabled` (e.g. a plugin detecting\n * missing credentials at config time). Skipped definitions still shaped the generated seed-ref\n * types — only the run drops them, so types stay stable across environments. */\nfunction partitionDefinitions(payload: Payload, defs: SeedDefinition[]): { active: SeedDefinition[]; skipped: SkippedDefinition[] } {\n const active: SeedDefinition[] = []\n const skipped: SkippedDefinition[] = []\n for (const def of defs) {\n const fromCollection =\n def.kind === 'collection'\n ? (payload.collections[def.slug as CollectionSlug]?.config.custom?.seedDisabled as SeedDisabledMarker | undefined)\n : undefined\n const flag = def.disabled || fromCollection\n if (!flag) {\n active.push(def)\n continue\n }\n const reason = typeof flag === 'string' ? flag : 'disabled'\n skipped.push({ slug: def.slug, reason })\n payload.logger.warn(`[payload-seed] skipping '${def.slug}': ${reason}`)\n }\n return { active, skipped }\n}\n\n/** Drop every optional field whose value contains a `ref()` into a skipped collection (warning per\n * drop — the doc won't exist this run), and hard-error when such a ref sits on a required field.\n * Runs before validation, so the remaining model checks clean. */\nfunction stripRefsToSkipped(payload: Payload, model: BuiltModel, skipped: SkippedDefinition[], requiredFields: Map<string, Set<string>>): void {\n if (!skipped.length) return\n const reasonBySlug = new Map(skipped.map((s) => [s.slug, s.reason]))\n const issues: string[] = []\n\n const strip = (where: string, slug: string | undefined, data: Record<string, unknown>) => {\n for (const [field, value] of Object.entries(data)) {\n const hit = collectTokens(value).find((t) => isRef(t) && reasonBySlug.has(t.collection))\n if (!hit || !isRef(hit)) continue\n if (slug && requiredFields.get(slug)?.has(field)) {\n issues.push(\n `${where}.${field}: required, but ref('${hit.collection}', '${hit.key}') targets a skipped definition (${reasonBySlug.get(hit.collection)}).`,\n )\n continue\n }\n delete data[field]\n payload.logger.warn(\n `[payload-seed] dropping entire field '${field}' on ${where} (contains ref('${hit.collection}', '${hit.key}') to skipped '${hit.collection}': ${reasonBySlug.get(hit.collection)}).`,\n )\n }\n }\n\n for (const coll of model.collections) for (const rec of coll.records) strip(docNodeId(coll.slug, rec.key), coll.slug, rec.data)\n for (const g of model.globals) strip(`global:${g.slug}`, undefined, g.data)\n\n if (issues.length) throw new SeedValidationError(issues)\n}\n\nasync function clearCollection(payload: Payload, req: PayloadRequest, collection: string): Promise<void> {\n const config = payload.collections[collection as CollectionSlug]?.config\n if (!config) return\n payload.logger.info(`[payload-seed] clearing ${collection}`)\n // Delete via the Local API (firing hooks) when clearing must cascade — an upload collection (to\n // remove stored bytes) or any collection with a before/after-delete hook (e.g. `mux-video`'s Mux\n // cleanup, `font`'s cascade to its originals + optimized). Otherwise wipe rows directly.\n const withHooks = Boolean(config.upload || config.hooks?.beforeDelete?.length || config.hooks?.afterDelete?.length)\n if (withHooks) {\n const result = (await payload.delete({\n collection: collection as CollectionSlug,\n where: { id: { exists: true } },\n req,\n overrideAccess: true,\n context: { disableRevalidate: true },\n disableTransaction: true,\n })) as { errors?: Array<{ id?: string | number; message?: string }> }\n // Payload's bulk delete does NOT throw on per-doc failures — it returns them in `errors`.\n // Retry each once, then warn LOUDLY with the underlying reasons — a silent partial wipe\n // would leave stale docs sitting beside the fresh seed.\n const failed: Array<{ label: string; reason: string }> = []\n for (const e of result?.errors ?? []) {\n if (e.id == null) continue\n try {\n await payload.delete({\n collection: collection as CollectionSlug,\n id: e.id,\n req,\n overrideAccess: true,\n context: { disableRevalidate: true },\n disableTransaction: true,\n })\n } catch (err) {\n // A prior-run generated id tells no story, so resolve it to the doc's admin title (the delete\n // failed — the doc still exists to look up), alongside the deepest driver cause.\n const reason = deepestReason(err, e.message)\n const label = await describeFailedDoc(payload, req, collection, config.admin?.useAsTitle, e.id)\n failed.push({ label, reason })\n }\n }\n if (failed.length) {\n const detail = failed.map((f) => `${f.label}: ${f.reason}`).join(' | ')\n payload.logger.warn(\n `[payload-seed] could not clear ${failed.length} doc(s) in '${collection}' — these STALE docs now sit beside the fresh seed; re-run the seed or delete them in the admin. Reasons: ${detail}`,\n )\n }\n } else {\n await payload.db.deleteMany({ collection: collection as CollectionSlug, req, where: {} })\n }\n if (config.versions) await payload.db.deleteVersions({ collection: collection as CollectionSlug, req, where: {} })\n}\n\n/**\n * The seed engine. Takes the seed definitions, skips the disabled ones (their own `disabled`, or\n * the collection's `custom.seedDisabled` — dropping optional refs that point at them), builds the\n * model, validates references against the live config, topologically sorts the dependency graph,\n * clears the seeded collections, then creates docs in order — resolving `ref` tokens to ids and\n * delivering each doc's `_file` as a native upload (upload collections) or a source-field value\n * (`custom.seedAsset` collections). A `ref` cycle is broken by deferring an optional field, which a\n * second pass sets once every doc exists (a cycle with only required fields is a hard error).\n * Globals are updated last.\n */\nexport async function runSeed({ payload, req, options, definitions }: RunSeedArgs): Promise<SeedResult> {\n const defs = definitions ?? options.definitions ?? []\n if (defs.length === 0) payload.logger.warn('[payload-seed] no seed definitions: pass `definitions` to seedPlugin() or seed().')\n\n // Drop disabled definitions (their own `disabled`, or the collection's `custom.seedDisabled` —\n // e.g. payload-mux without credentials). They still shaped the generated seed-ref types.\n const { active, skipped } = partitionDefinitions(payload, defs)\n\n const model = buildModel(active)\n const collectionSlugs = new Set(Object.keys(payload.collections))\n\n const isUpload = (slug: string): boolean => Boolean(payload.collections[slug as CollectionSlug]?.config.upload)\n const assetBySlug = discoverAssetCollections(payload)\n\n // Collections a `_file` may sit on: every upload collection plus every `custom.seedAsset` collection.\n const fileCollections = new Set<string>([...collectionSlugs].filter(isUpload))\n for (const slug of assetBySlug.keys()) fileCollections.add(slug)\n\n // Valid top-level field names per node (for unknown-field detection) plus the required ones (so a\n // ref cycle can only be broken by deferring an optional field) — read from the live config.\n const fieldNames = new Map<string, Set<string>>()\n const requiredFields = new Map<string, Set<string>>()\n for (const coll of model.collections) {\n const cfg = payload.collections[coll.slug as CollectionSlug]?.config\n if (!cfg) continue\n fieldNames.set(coll.slug, new Set(cfg.flattenedFields.map((f) => f.name)))\n requiredFields.set(coll.slug, new Set(cfg.flattenedFields.filter((f) => (f as { required?: boolean }).required).map((f) => f.name)))\n }\n for (const g of model.globals) {\n const cfg = payload.config.globals.find((gc) => gc.slug === g.slug)\n if (cfg) fieldNames.set(`global:${g.slug}`, new Set(cfg.flattenedFields.map((f) => f.name)))\n }\n\n // Refs into a skipped definition come out of the data before validation: dropped when the field\n // is optional (the run warns; re-seed once the skip is lifted and they fill in), fatal when it's\n // required (the doc can't be created without it).\n stripRefsToSkipped(payload, model, skipped, requiredFields)\n\n const globalSlugs = new Set(payload.config.globals.map((g) => g.slug))\n validateModel({ model, collectionSlugs, globalSlugs, fileCollections, fieldNames })\n const isRequired = (collection: string, field: string): boolean => requiredFields.get(collection)?.has(field) ?? false\n const { order, deferred } = buildGraph(model, { isRequired })\n\n // Fields nulled at create time (their refs point into a cycle) and set in a second pass below.\n const deferredByNode = new Map<string, Set<string>>()\n for (const d of deferred) {\n const set = deferredByNode.get(d.node) ?? new Set<string>()\n set.add(d.field)\n deferredByNode.set(d.node, set)\n }\n\n const baseArgs = { depth: 0, overrideAccess: true, context: { disableRevalidate: true }, req } as const\n\n const docIds = new Map<string, string | number>()\n const recordIndex = new Map<string, { slug: string; record: BuiltRecord }>()\n for (const coll of model.collections)\n for (const rec of coll.records) recordIndex.set(docNodeId(coll.slug, rec.key), { slug: coll.slug, record: rec })\n\n // Clear every seeded collection — dependents BEFORE their dependencies (the REVERSE of creation\n // order). Clearing in creation order deletes referenced docs while the previous run's\n // referencing rows still exist, and on SQL adapters a relationship column can make that delete\n // FAIL outright (e.g. sqlite generates a required in-array upload as NOT NULL with an\n // ON DELETE SET NULL foreign key — nulling it violates the constraint), stranding stale docs\n // beside the fresh seed. `clearCollection` fires delete hooks when the collection needs a\n // cascade (uploads / external-asset cleanup); plain collections are wiped directly.\n const seededCollections = [...new Set(model.collections.map((c) => c.slug))]\n const creationOrder: string[] = []\n const seen = new Set<string>()\n for (const nodeId of order) {\n const slug = recordIndex.get(nodeId)?.slug\n if (slug && !seen.has(slug)) {\n seen.add(slug)\n creationOrder.push(slug)\n }\n }\n // Definitions whose records were all skipped/empty still get cleared (after the ordered ones).\n for (const slug of seededCollections) if (!seen.has(slug)) creationOrder.push(slug)\n payload.logger.info('[payload-seed] clearing collections...')\n for (const slug of [...creationOrder].reverse()) await clearCollection(payload, req, slug)\n\n // Create docs in dependency order, resolving ref tokens to ids and delivering each `_file`.\n const created: Record<string, number> = {}\n\n payload.logger.info('[payload-seed] seeding documents...')\n for (const nodeId of order) {\n const entry = recordIndex.get(nodeId)\n if (!entry) continue\n const { slug, record } = entry\n // Drop any deferred fields before resolving: their refs point into a cycle and aren't created\n // yet. The second pass below sets them once every doc exists.\n const deferFields = deferredByNode.get(nodeId)\n const source = deferFields ? Object.fromEntries(Object.entries(record.data).filter(([k]) => !deferFields.has(k))) : record.data\n let data = resolveTokens(source, { docs: docIds, where: nodeId }) as Record<string, unknown>\n let uploadFile: Awaited<ReturnType<typeof readFileAsUpload>> | undefined\n\n if (record.file) {\n // Both branches resolve the file the same way — under the per-collection subdir (a\n // `custom.seedAsset` `subdir`, else `assetSubDirs`, else the slug), then the assets root.\n const asset = assetBySlug.get(slug)\n const subdir = asset?.subdir ?? options.assetSubDirs[slug] ?? slug\n const subdirs = [subdir, '']\n const path = await resolveFilePath(record.file.name, options.assetsDir, subdirs)\n if (!path) {\n const searched = searchedDirs(record.file.name, options.assetsDir, subdirs).join(', ')\n payload.logger.warn({ msg: `[payload-seed] ${nodeId}: _file '${record.file.name}' not found - skipped. Searched: ${searched}` })\n } else if (asset) {\n // Hand the resolved path + options to the collection's ingest hook via its source field\n // instead of uploading bytes.\n data = { ...data, [asset.sourceField]: { file: path, ...record.file.options } }\n } else if (isUpload(slug)) {\n uploadFile = await readFileAsUpload(path)\n }\n }\n\n payload.logger.info(`[payload-seed] seeding '${nodeId}'`)\n let doc: { id: string | number }\n try {\n doc = (await payload.create({\n collection: slug as CollectionSlug,\n data: data as never,\n ...(uploadFile ? { file: uploadFile } : {}),\n ...baseArgs,\n })) as { id: string | number }\n } catch (err) {\n throw new SeedRunError(`creating '${nodeId}': ${deepestReason(err)}`)\n }\n docIds.set(nodeId, doc.id)\n created[slug] = (created[slug] ?? 0) + 1\n }\n\n // Second pass: now every doc exists, resolve and set the fields deferred to break cycles.\n if (deferred.length) {\n payload.logger.info(`[payload-seed] resolving ${deferred.length} deferred reference(s)...`)\n for (const { node, field } of deferred) {\n const entry = recordIndex.get(node)\n const id = docIds.get(node)\n if (!entry || id === undefined) continue\n const value = resolveTokens(entry.record.data[field], { docs: docIds, where: `${node}.${field}` })\n try {\n await payload.update({ collection: entry.slug as CollectionSlug, id, data: { [field]: value } as never, ...baseArgs })\n } catch (err) {\n throw new SeedRunError(`setting deferred field '${node}.${field}': ${deepestReason(err)}`)\n }\n }\n }\n\n // Update globals after all docs exist.\n for (const g of model.globals) {\n payload.logger.info(`[payload-seed] seeding global '${g.slug}'`)\n const data = resolveTokens(g.data, { docs: docIds, where: `global:${g.slug}` }) as Record<string, unknown>\n try {\n await payload.updateGlobal({ slug: g.slug as never, data: data as never, ...baseArgs })\n } catch (err) {\n throw new SeedRunError(`updating global '${g.slug}': ${deepestReason(err)}`)\n }\n }\n\n payload.logger.info('[payload-seed] seed complete.')\n const result: SeedResult = {\n created,\n collections: model.collections.map((c) => c.slug),\n globals: model.globals.map((g) => g.slug),\n order,\n deferred,\n skipped,\n }\n await notifyAfterSeed(payload, req, result)\n return result\n}\n\n/**\n * CLI / Local-API convenience: run the seed from a script (`payload run`) or test. Builds\n * a local `req` if one isn't supplied and resolves the public plugin options.\n */\nexport async function seed(args: { payload: Payload; req?: PayloadRequest; options?: SeedPluginOptions }): Promise<SeedResult> {\n const req = args.req ?? (await createLocalReq({}, args.payload))\n return runSeed({ payload: args.payload, req, options: resolveOptions(args.options) })\n}\n"],"names":["createLocalReq","notifyAfterSeed","resolveOptions","file","isFileToken","isRef","ref","buildGraph","resolveFilePath","readFileAsUpload","searchedDirs","collectTokens","docNodeId","resolveTokens","SeedRunError","SeedValidationError","validateModel","deepestReason","err","fallback","deepest","Error","undefined","cause","msg","message","String","data","errors","length","fields","map","e","path","field","join","replace","slice","describeFailedDoc","payload","req","slug","useAsTitle","id","doc","findByID","collection","overrideAccess","depth","label","title","name","filename","find","v","trim","tokens","discoverAssetCollections","Map","Object","keys","collections","marker","config","custom","seedAsset","m","set","sourceField","subdir","buildModel","definitions","globals","def","kind","records","build","rec","_key","_file","key","push","partitionDefinitions","defs","active","skipped","fromCollection","seedDisabled","flag","disabled","reason","logger","warn","stripRefsToSkipped","model","requiredFields","reasonBySlug","s","issues","strip","where","value","entries","hit","t","has","get","coll","g","clearCollection","info","withHooks","Boolean","upload","hooks","beforeDelete","afterDelete","result","delete","exists","context","disableRevalidate","disableTransaction","failed","admin","detail","f","db","deleteMany","versions","deleteVersions","runSeed","options","collectionSlugs","Set","isUpload","assetBySlug","fileCollections","filter","add","fieldNames","cfg","flattenedFields","required","gc","globalSlugs","isRequired","order","deferred","deferredByNode","d","node","baseArgs","docIds","recordIndex","record","seededCollections","c","creationOrder","seen","nodeId","reverse","created","entry","deferFields","source","fromEntries","k","docs","uploadFile","asset","assetSubDirs","subdirs","assetsDir","searched","create","update","updateGlobal","seed","args"],"mappings":"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"}
|
|
@@ -3,6 +3,13 @@ export declare class SeedValidationError extends Error {
|
|
|
3
3
|
issues: string[];
|
|
4
4
|
constructor(issues: string[]);
|
|
5
5
|
}
|
|
6
|
+
/** A write failed mid-run (create / deferred set / global update). `detail` names the seed node
|
|
7
|
+
* (`collection:_key`) and the deepest driver cause, so the failure tells a meaningful story rather
|
|
8
|
+
* than surfacing an opaque generated id. */
|
|
9
|
+
export declare class SeedRunError extends Error {
|
|
10
|
+
detail: string;
|
|
11
|
+
constructor(detail: string);
|
|
12
|
+
}
|
|
6
13
|
export interface ValidateArgs {
|
|
7
14
|
model: BuiltModel;
|
|
8
15
|
/** Slugs of collections that actually exist in the Payload config. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../src/engine/validate.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AAGzC,qBAAa,mBAAoB,SAAQ,KAAK;IACzB,MAAM,EAAE,MAAM,EAAE;gBAAhB,MAAM,EAAE,MAAM,EAAE;CAIpC;AAED,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,UAAU,CAAA;IACjB,sEAAsE;IACtE,eAAe,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;IAC5B,kEAAkE;IAClE,WAAW,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;IACxB,8FAA8F;IAC9F,eAAe,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;IAC5B;;uEAEmE;IACnE,UAAU,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,CAAA;CACtC;AAKD;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,EAAE,KAAK,EAAE,eAAe,EAAE,WAAW,EAAE,eAAe,EAAE,UAAU,EAAE,EAAE,YAAY,GAAG,IAAI,CA+DtH"}
|
|
1
|
+
{"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../src/engine/validate.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AAGzC,qBAAa,mBAAoB,SAAQ,KAAK;IACzB,MAAM,EAAE,MAAM,EAAE;gBAAhB,MAAM,EAAE,MAAM,EAAE;CAIpC;AAED;;6CAE6C;AAC7C,qBAAa,YAAa,SAAQ,KAAK;IAClB,MAAM,EAAE,MAAM;gBAAd,MAAM,EAAE,MAAM;CAIlC;AAED,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,UAAU,CAAA;IACjB,sEAAsE;IACtE,eAAe,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;IAC5B,kEAAkE;IAClE,WAAW,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;IACxB,8FAA8F;IAC9F,eAAe,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;IAC5B;;uEAEmE;IACnE,UAAU,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,CAAA;CACtC;AAKD;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,EAAE,KAAK,EAAE,eAAe,EAAE,WAAW,EAAE,eAAe,EAAE,UAAU,EAAE,EAAE,YAAY,GAAG,IAAI,CA+DtH"}
|
package/dist/engine/validate.js
CHANGED
|
@@ -7,6 +7,15 @@ export class SeedValidationError extends Error {
|
|
|
7
7
|
this.name = 'SeedValidationError';
|
|
8
8
|
}
|
|
9
9
|
}
|
|
10
|
+
/** A write failed mid-run (create / deferred set / global update). `detail` names the seed node
|
|
11
|
+
* (`collection:_key`) and the deepest driver cause, so the failure tells a meaningful story rather
|
|
12
|
+
* than surfacing an opaque generated id. */ export class SeedRunError extends Error {
|
|
13
|
+
detail;
|
|
14
|
+
constructor(detail){
|
|
15
|
+
super(`[payload-seed] seed run failed while ${detail}`), this.detail = detail;
|
|
16
|
+
this.name = 'SeedRunError';
|
|
17
|
+
}
|
|
18
|
+
}
|
|
10
19
|
// Keys that are valid on a record but aren't schema fields.
|
|
11
20
|
const ALLOWED_NON_FIELDS = new Set([
|
|
12
21
|
'_status'
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/engine/validate.ts"],"sourcesContent":["import { isRef } from '../refs'\nimport type { BuiltModel } from './graph'\nimport { collectTokens, docNodeId } from './tokens'\n\nexport class SeedValidationError extends Error {\n constructor(public issues: string[]) {\n super(`[payload-seed] seed data failed validation:\\n${issues.map((i) => ` - ${i}`).join('\\n')}`)\n this.name = 'SeedValidationError'\n }\n}\n\nexport interface ValidateArgs {\n model: BuiltModel\n /** Slugs of collections that actually exist in the Payload config. */\n collectionSlugs: Set<string>\n /** Slugs of globals that actually exist in the Payload config. */\n globalSlugs: Set<string>\n /** Slugs that can carry a `_file`: upload collections plus `custom.seedAsset` collections. */\n fileCollections: Set<string>\n /** Valid top-level field names per node (`collection` slug, or `global:<slug>`), from the\n * live config's `flattenedFields`. When provided, unknown record fields are flagged —\n * the runtime counterpart to the compile-time exactness check. */\n fieldNames?: Map<string, Set<string>>\n}\n\n// Keys that are valid on a record but aren't schema fields.\nconst ALLOWED_NON_FIELDS = new Set(['_status'])\n\n/**\n * Validate the built model against the declared docs and the live config: every definition's own\n * slug exists in the config (as a collection or global, matching its kind); every `ref()`\n * resolves to a seeded doc and references a real collection; every `_file` sits on a collection\n * that can take one (an upload or `custom.seedAsset` collection); no duplicate `_key` within a collection\n * (across every definition sharing the slug); and (when `fieldNames` is supplied) every record field\n * exists in the schema. Collects ALL issues and throws once. (Cycle detection happens in the graph topo-sort.)\n */\nexport function validateModel({ model, collectionSlugs, globalSlugs, fileCollections, fieldNames }: ValidateArgs): void {\n const issues: string[] = []\n const docIds = new Set<string>()\n for (const coll of model.collections) for (const rec of coll.records) docIds.add(docNodeId(coll.slug, rec.key))\n\n // A definition's own slug must exist in the config, or the run would wipe collections and then die mid-create.\n for (const coll of model.collections) {\n if (!collectionSlugs.has(coll.slug)) {\n issues.push(`defineSeed('${coll.slug}'): no collection '${coll.slug}' in the Payload config - fix the slug or add the collection.`)\n }\n }\n for (const g of model.globals) {\n if (!globalSlugs.has(g.slug)) {\n issues.push(`defineSeed('${g.slug}'): no global '${g.slug}' in the Payload config - fix the slug or add the global.`)\n }\n }\n\n const check = (where: string, data: unknown) => {\n for (const token of collectTokens(data)) {\n if (!isRef(token)) continue\n if (!collectionSlugs.has(token.collection)) {\n issues.push(`${where}: ref('${token.collection}', '${token.key}') targets unknown collection '${token.collection}'.`)\n } else if (!docIds.has(docNodeId(token.collection, token.key))) {\n issues.push(`${where}: ref('${token.collection}', '${token.key}') - no seeded '${token.collection}' doc has _key '${token.key}'.`)\n }\n }\n }\n\n for (const coll of model.collections) for (const rec of coll.records) check(`${coll.slug}:${rec.key}`, rec.data)\n for (const g of model.globals) check(`global:${g.slug}`, g.data)\n\n // A `_file` only makes sense on an upload collection or a `custom.seedAsset` collection.\n for (const coll of model.collections) {\n for (const rec of coll.records) {\n if (rec.file && !fileCollections.has(coll.slug)) {\n issues.push(`${coll.slug}:${rec.key}: _file set, but '${coll.slug}' is not an upload collection or a custom.seedAsset collection.`)\n }\n }\n }\n\n // Unknown top-level fields (runtime counterpart to the compile-time exactness check).\n const checkFields = (where: string, slug: string, data: Record<string, unknown>) => {\n const valid = fieldNames?.get(slug)\n if (!valid) return\n for (const key of Object.keys(data)) {\n if (!valid.has(key) && !ALLOWED_NON_FIELDS.has(key)) issues.push(`${where}: unknown field '${key}' - not in the '${slug}' schema.`)\n }\n }\n for (const coll of model.collections) for (const rec of coll.records) checkFields(`${coll.slug}:${rec.key}`, coll.slug, rec.data)\n for (const g of model.globals) checkFields(`global:${g.slug}`, `global:${g.slug}`, g.data)\n\n // Duplicate _key within a collection makes refs ambiguous - checked across every definition sharing the slug.\n const seenBySlug = new Map<string, Set<string>>()\n for (const coll of model.collections) {\n const seen = seenBySlug.get(coll.slug) ?? new Set<string>()\n seenBySlug.set(coll.slug, seen)\n for (const rec of coll.records) {\n if (seen.has(rec.key)) issues.push(`${coll.slug}: duplicate _key '${rec.key}'.`)\n seen.add(rec.key)\n }\n }\n\n if (issues.length) throw new SeedValidationError(issues)\n}\n"],"names":["isRef","collectTokens","docNodeId","SeedValidationError","Error","issues","map","i","join","name","ALLOWED_NON_FIELDS","Set","validateModel","model","collectionSlugs","globalSlugs","fileCollections","fieldNames","docIds","coll","collections","rec","records","add","slug","key","has","push","g","globals","check","where","data","token","collection","file","checkFields","valid","get","Object","keys","seenBySlug","Map","seen","set","length"],"mappings":"AAAA,SAASA,KAAK,QAAQ,aAAS;AAE/B,SAASC,aAAa,EAAEC,SAAS,QAAQ,cAAU;AAEnD,OAAO,MAAMC,4BAA4BC;;IACvC,YAAY,AAAOC,MAAgB,CAAE;QACnC,KAAK,CAAC,CAAC,6CAA6C,EAAEA,OAAOC,GAAG,CAAC,CAACC,IAAM,CAAC,IAAI,EAAEA,GAAG,EAAEC,IAAI,CAAC,OAAO,QAD/EH,SAAAA;QAEjB,IAAI,CAACI,IAAI,GAAG;IACd;AACF;AAgBA,4DAA4D;AAC5D,
|
|
1
|
+
{"version":3,"sources":["../../src/engine/validate.ts"],"sourcesContent":["import { isRef } from '../refs'\nimport type { BuiltModel } from './graph'\nimport { collectTokens, docNodeId } from './tokens'\n\nexport class SeedValidationError extends Error {\n constructor(public issues: string[]) {\n super(`[payload-seed] seed data failed validation:\\n${issues.map((i) => ` - ${i}`).join('\\n')}`)\n this.name = 'SeedValidationError'\n }\n}\n\n/** A write failed mid-run (create / deferred set / global update). `detail` names the seed node\n * (`collection:_key`) and the deepest driver cause, so the failure tells a meaningful story rather\n * than surfacing an opaque generated id. */\nexport class SeedRunError extends Error {\n constructor(public detail: string) {\n super(`[payload-seed] seed run failed while ${detail}`)\n this.name = 'SeedRunError'\n }\n}\n\nexport interface ValidateArgs {\n model: BuiltModel\n /** Slugs of collections that actually exist in the Payload config. */\n collectionSlugs: Set<string>\n /** Slugs of globals that actually exist in the Payload config. */\n globalSlugs: Set<string>\n /** Slugs that can carry a `_file`: upload collections plus `custom.seedAsset` collections. */\n fileCollections: Set<string>\n /** Valid top-level field names per node (`collection` slug, or `global:<slug>`), from the\n * live config's `flattenedFields`. When provided, unknown record fields are flagged —\n * the runtime counterpart to the compile-time exactness check. */\n fieldNames?: Map<string, Set<string>>\n}\n\n// Keys that are valid on a record but aren't schema fields.\nconst ALLOWED_NON_FIELDS = new Set(['_status'])\n\n/**\n * Validate the built model against the declared docs and the live config: every definition's own\n * slug exists in the config (as a collection or global, matching its kind); every `ref()`\n * resolves to a seeded doc and references a real collection; every `_file` sits on a collection\n * that can take one (an upload or `custom.seedAsset` collection); no duplicate `_key` within a collection\n * (across every definition sharing the slug); and (when `fieldNames` is supplied) every record field\n * exists in the schema. Collects ALL issues and throws once. (Cycle detection happens in the graph topo-sort.)\n */\nexport function validateModel({ model, collectionSlugs, globalSlugs, fileCollections, fieldNames }: ValidateArgs): void {\n const issues: string[] = []\n const docIds = new Set<string>()\n for (const coll of model.collections) for (const rec of coll.records) docIds.add(docNodeId(coll.slug, rec.key))\n\n // A definition's own slug must exist in the config, or the run would wipe collections and then die mid-create.\n for (const coll of model.collections) {\n if (!collectionSlugs.has(coll.slug)) {\n issues.push(`defineSeed('${coll.slug}'): no collection '${coll.slug}' in the Payload config - fix the slug or add the collection.`)\n }\n }\n for (const g of model.globals) {\n if (!globalSlugs.has(g.slug)) {\n issues.push(`defineSeed('${g.slug}'): no global '${g.slug}' in the Payload config - fix the slug or add the global.`)\n }\n }\n\n const check = (where: string, data: unknown) => {\n for (const token of collectTokens(data)) {\n if (!isRef(token)) continue\n if (!collectionSlugs.has(token.collection)) {\n issues.push(`${where}: ref('${token.collection}', '${token.key}') targets unknown collection '${token.collection}'.`)\n } else if (!docIds.has(docNodeId(token.collection, token.key))) {\n issues.push(`${where}: ref('${token.collection}', '${token.key}') - no seeded '${token.collection}' doc has _key '${token.key}'.`)\n }\n }\n }\n\n for (const coll of model.collections) for (const rec of coll.records) check(`${coll.slug}:${rec.key}`, rec.data)\n for (const g of model.globals) check(`global:${g.slug}`, g.data)\n\n // A `_file` only makes sense on an upload collection or a `custom.seedAsset` collection.\n for (const coll of model.collections) {\n for (const rec of coll.records) {\n if (rec.file && !fileCollections.has(coll.slug)) {\n issues.push(`${coll.slug}:${rec.key}: _file set, but '${coll.slug}' is not an upload collection or a custom.seedAsset collection.`)\n }\n }\n }\n\n // Unknown top-level fields (runtime counterpart to the compile-time exactness check).\n const checkFields = (where: string, slug: string, data: Record<string, unknown>) => {\n const valid = fieldNames?.get(slug)\n if (!valid) return\n for (const key of Object.keys(data)) {\n if (!valid.has(key) && !ALLOWED_NON_FIELDS.has(key)) issues.push(`${where}: unknown field '${key}' - not in the '${slug}' schema.`)\n }\n }\n for (const coll of model.collections) for (const rec of coll.records) checkFields(`${coll.slug}:${rec.key}`, coll.slug, rec.data)\n for (const g of model.globals) checkFields(`global:${g.slug}`, `global:${g.slug}`, g.data)\n\n // Duplicate _key within a collection makes refs ambiguous - checked across every definition sharing the slug.\n const seenBySlug = new Map<string, Set<string>>()\n for (const coll of model.collections) {\n const seen = seenBySlug.get(coll.slug) ?? new Set<string>()\n seenBySlug.set(coll.slug, seen)\n for (const rec of coll.records) {\n if (seen.has(rec.key)) issues.push(`${coll.slug}: duplicate _key '${rec.key}'.`)\n seen.add(rec.key)\n }\n }\n\n if (issues.length) throw new SeedValidationError(issues)\n}\n"],"names":["isRef","collectTokens","docNodeId","SeedValidationError","Error","issues","map","i","join","name","SeedRunError","detail","ALLOWED_NON_FIELDS","Set","validateModel","model","collectionSlugs","globalSlugs","fileCollections","fieldNames","docIds","coll","collections","rec","records","add","slug","key","has","push","g","globals","check","where","data","token","collection","file","checkFields","valid","get","Object","keys","seenBySlug","Map","seen","set","length"],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -6,4 +6,8 @@ export type { CollectionSeedData, GlobalSeedData, SeedTokens, WithRefs } from '.
|
|
|
6
6
|
export type { FileToken, Ref } from './refs';
|
|
7
7
|
export { file, isFileToken, isRef, ref } from './refs';
|
|
8
8
|
export { seed } from './engine/run';
|
|
9
|
+
export type { SeedResult } from './engine/run';
|
|
10
|
+
export { registerAfterSeedListener } from './listeners';
|
|
11
|
+
export type { AfterSeedListener } from './listeners';
|
|
12
|
+
export { SeedRunError, SeedValidationError } from './engine/validate';
|
|
9
13
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AACrC,YAAY,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAA;AAKlD,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAGzC,YAAY,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAM9C,YAAY,EAAE,kBAAkB,EAAE,cAAc,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AACvF,YAAY,EAAE,SAAS,EAAE,GAAG,EAAE,MAAM,QAAQ,CAAA;AAG5C,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,QAAQ,CAAA;AAGtD,OAAO,EAAE,IAAI,EAAE,MAAM,cAAc,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AACrC,YAAY,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAA;AAKlD,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAGzC,YAAY,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAM9C,YAAY,EAAE,kBAAkB,EAAE,cAAc,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AACvF,YAAY,EAAE,SAAS,EAAE,GAAG,EAAE,MAAM,QAAQ,CAAA;AAG5C,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,QAAQ,CAAA;AAGtD,OAAO,EAAE,IAAI,EAAE,MAAM,cAAc,CAAA;AACnC,YAAY,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAI9C,OAAO,EAAE,yBAAyB,EAAE,MAAM,aAAa,CAAA;AACvD,YAAY,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAA;AAIpD,OAAO,EAAE,YAAY,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -9,5 +9,11 @@ export { defineSeed } from "./defineSeed.js";
|
|
|
9
9
|
export { file, isFileToken, isRef, ref } from "./refs.js";
|
|
10
10
|
// Run the seed from a script or test (the `payload seed` command and endpoint use this)
|
|
11
11
|
export { seed } from "./engine/run.js";
|
|
12
|
+
// After-seed listeners: decoupled plugins register (by shared symbol slot or this helper) to run
|
|
13
|
+
// once at the end of every seed — e.g. @pro-laico/payload-revalidate's cache flush.
|
|
14
|
+
export { registerAfterSeedListener } from "./listeners.js";
|
|
15
|
+
// The error classes a failing run throws, so callers can branch on (and test) failure modes:
|
|
16
|
+
// SeedValidationError collects every model issue; SeedRunError names the write that died.
|
|
17
|
+
export { SeedRunError, SeedValidationError } from "./engine/validate.js";
|
|
12
18
|
|
|
13
19
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["// The plugin\nexport { seedPlugin } from './plugin'\nexport type { SeedPluginOptions } from './options'\n\n// Authoring: define seed data (the `ref` / `file` tokens are supplied to each builder callback —\n// `defineSeed('x', ({ ref, file }) => …)` — so they aren't imported). One helper for both:\n// `defineSeed` infers collection (array of records) vs global (single object) from the slug.\nexport { defineSeed } from './defineSeed'\n\n// The augmentable interface that generated types fill in (so `ref` keys are typed)\nexport type { SeedRegistry } from './registry'\n\n// Authoring types for seed helpers COMPOSED across files (e.g. per-block seed fragments that a\n// page definition assembles): `SeedTokens` types a helper's `{ ref, file }` parameter, `WithRefs`\n// widens a generated data type so `Ref` tokens may sit in relationship fields, and `Ref` /\n// `FileToken` are the token value types themselves.\nexport type { CollectionSeedData, GlobalSeedData, SeedTokens, WithRefs } from './types'\nexport type { FileToken, Ref } from './refs'\n// The token constructors themselves, for code that builds seed data OUTSIDE a builder\n// callback — e.g. unit tests exercising a composed seed fragment directly.\nexport { file, isFileToken, isRef, ref } from './refs'\n\n// Run the seed from a script or test (the `payload seed` command and endpoint use this)\nexport { seed } from './engine/run'\n"],"names":["seedPlugin","defineSeed","file","isFileToken","isRef","ref","seed"],"mappings":"AAAA,aAAa;AACb,SAASA,UAAU,QAAQ,cAAU;AAGrC,iGAAiG;AACjG,2FAA2F;AAC3F,6FAA6F;AAC7F,SAASC,UAAU,QAAQ,kBAAc;AAWzC,sFAAsF;AACtF,2EAA2E;AAC3E,SAASC,IAAI,EAAEC,WAAW,EAAEC,KAAK,EAAEC,GAAG,QAAQ,YAAQ;AAEtD,wFAAwF;AACxF,SAASC,IAAI,QAAQ,kBAAc"}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["// The plugin\nexport { seedPlugin } from './plugin'\nexport type { SeedPluginOptions } from './options'\n\n// Authoring: define seed data (the `ref` / `file` tokens are supplied to each builder callback —\n// `defineSeed('x', ({ ref, file }) => …)` — so they aren't imported). One helper for both:\n// `defineSeed` infers collection (array of records) vs global (single object) from the slug.\nexport { defineSeed } from './defineSeed'\n\n// The augmentable interface that generated types fill in (so `ref` keys are typed)\nexport type { SeedRegistry } from './registry'\n\n// Authoring types for seed helpers COMPOSED across files (e.g. per-block seed fragments that a\n// page definition assembles): `SeedTokens` types a helper's `{ ref, file }` parameter, `WithRefs`\n// widens a generated data type so `Ref` tokens may sit in relationship fields, and `Ref` /\n// `FileToken` are the token value types themselves.\nexport type { CollectionSeedData, GlobalSeedData, SeedTokens, WithRefs } from './types'\nexport type { FileToken, Ref } from './refs'\n// The token constructors themselves, for code that builds seed data OUTSIDE a builder\n// callback — e.g. unit tests exercising a composed seed fragment directly.\nexport { file, isFileToken, isRef, ref } from './refs'\n\n// Run the seed from a script or test (the `payload seed` command and endpoint use this)\nexport { seed } from './engine/run'\nexport type { SeedResult } from './engine/run'\n\n// After-seed listeners: decoupled plugins register (by shared symbol slot or this helper) to run\n// once at the end of every seed — e.g. @pro-laico/payload-revalidate's cache flush.\nexport { registerAfterSeedListener } from './listeners'\nexport type { AfterSeedListener } from './listeners'\n\n// The error classes a failing run throws, so callers can branch on (and test) failure modes:\n// SeedValidationError collects every model issue; SeedRunError names the write that died.\nexport { SeedRunError, SeedValidationError } from './engine/validate'\n"],"names":["seedPlugin","defineSeed","file","isFileToken","isRef","ref","seed","registerAfterSeedListener","SeedRunError","SeedValidationError"],"mappings":"AAAA,aAAa;AACb,SAASA,UAAU,QAAQ,cAAU;AAGrC,iGAAiG;AACjG,2FAA2F;AAC3F,6FAA6F;AAC7F,SAASC,UAAU,QAAQ,kBAAc;AAWzC,sFAAsF;AACtF,2EAA2E;AAC3E,SAASC,IAAI,EAAEC,WAAW,EAAEC,KAAK,EAAEC,GAAG,QAAQ,YAAQ;AAEtD,wFAAwF;AACxF,SAASC,IAAI,QAAQ,kBAAc;AAGnC,iGAAiG;AACjG,oFAAoF;AACpF,SAASC,yBAAyB,QAAQ,iBAAa;AAGvD,6FAA6F;AAC7F,0FAA0F;AAC1F,SAASC,YAAY,EAAEC,mBAAmB,QAAQ,uBAAmB"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { Payload, PayloadRequest } from 'payload';
|
|
2
|
+
import type { SeedResult } from './engine/run';
|
|
3
|
+
/**
|
|
4
|
+
* After-seed listeners: a keyed record on a shared `Symbol.for` globalThis slot, invoked
|
|
5
|
+
* once at the end of every `runSeed` (endpoint AND CLI paths). This is the function-flavored
|
|
6
|
+
* sibling of the `custom.seedAsset` / `custom.seedDisabled` markers — decoupled plugins
|
|
7
|
+
* (e.g. `@pro-laico/payload-revalidate` flushing cache tags for the seeded surface)
|
|
8
|
+
* register under their own key by writing to the slot directly, no import of this package
|
|
9
|
+
* required; the keyed record makes re-registration (HMR) idempotent. Listener failures
|
|
10
|
+
* are logged and never fail the seed.
|
|
11
|
+
*/
|
|
12
|
+
export type AfterSeedListener = (result: SeedResult, ctx: {
|
|
13
|
+
payload: Payload;
|
|
14
|
+
req: PayloadRequest;
|
|
15
|
+
}) => void | Promise<void>;
|
|
16
|
+
/** Register (or replace) the listener under `key`. Equivalent to writing the slot directly. */
|
|
17
|
+
export declare const registerAfterSeedListener: (key: string, listener: AfterSeedListener) => void;
|
|
18
|
+
/** The currently registered listeners (empty when none). */
|
|
19
|
+
export declare const afterSeedListeners: () => Record<string, AfterSeedListener>;
|
|
20
|
+
/** Called by the engine after a successful run: invoke every listener, isolating failures. */
|
|
21
|
+
export declare const notifyAfterSeed: (payload: Payload, req: PayloadRequest, result: SeedResult) => Promise<void>;
|
|
22
|
+
//# sourceMappingURL=listeners.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"listeners.d.ts","sourceRoot":"","sources":["../src/listeners.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AACtD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAE9C;;;;;;;;GAQG;AACH,MAAM,MAAM,iBAAiB,GAAG,CAAC,MAAM,EAAE,UAAU,EAAE,GAAG,EAAE;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,GAAG,EAAE,cAAc,CAAA;CAAE,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;AAI5H,+FAA+F;AAC/F,eAAO,MAAM,yBAAyB,GAAI,KAAK,MAAM,EAAE,UAAU,iBAAiB,KAAG,IAKpF,CAAA;AAED,4DAA4D;AAC5D,eAAO,MAAM,kBAAkB,QAAO,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAC6C,CAAA;AAEnH,8FAA8F;AAC9F,eAAO,MAAM,eAAe,GAAU,SAAS,OAAO,EAAE,KAAK,cAAc,EAAE,QAAQ,UAAU,KAAG,OAAO,CAAC,IAAI,CAQ7G,CAAA"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
const AFTER_SEED_SLOT = Symbol.for('pro-laico.payload-seed.afterSeed');
|
|
2
|
+
/** Register (or replace) the listener under `key`. Equivalent to writing the slot directly. */ export const registerAfterSeedListener = (key, listener)=>{
|
|
3
|
+
const slot = globalThis;
|
|
4
|
+
const listeners = slot[AFTER_SEED_SLOT] ?? {};
|
|
5
|
+
listeners[key] = listener;
|
|
6
|
+
slot[AFTER_SEED_SLOT] = listeners;
|
|
7
|
+
};
|
|
8
|
+
/** The currently registered listeners (empty when none). */ export const afterSeedListeners = ()=>globalThis[AFTER_SEED_SLOT] ?? {};
|
|
9
|
+
/** Called by the engine after a successful run: invoke every listener, isolating failures. */ export const notifyAfterSeed = async (payload, req, result)=>{
|
|
10
|
+
for (const [key, listener] of Object.entries(afterSeedListeners())){
|
|
11
|
+
try {
|
|
12
|
+
await listener(result, {
|
|
13
|
+
payload,
|
|
14
|
+
req
|
|
15
|
+
});
|
|
16
|
+
} catch (err) {
|
|
17
|
+
payload.logger.warn(`[payload-seed] afterSeed listener '${key}' failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
//# sourceMappingURL=listeners.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/listeners.ts"],"sourcesContent":["import type { Payload, PayloadRequest } from 'payload'\nimport type { SeedResult } from './engine/run'\n\n/**\n * After-seed listeners: a keyed record on a shared `Symbol.for` globalThis slot, invoked\n * once at the end of every `runSeed` (endpoint AND CLI paths). This is the function-flavored\n * sibling of the `custom.seedAsset` / `custom.seedDisabled` markers — decoupled plugins\n * (e.g. `@pro-laico/payload-revalidate` flushing cache tags for the seeded surface)\n * register under their own key by writing to the slot directly, no import of this package\n * required; the keyed record makes re-registration (HMR) idempotent. Listener failures\n * are logged and never fail the seed.\n */\nexport type AfterSeedListener = (result: SeedResult, ctx: { payload: Payload; req: PayloadRequest }) => void | Promise<void>\n\nconst AFTER_SEED_SLOT = Symbol.for('pro-laico.payload-seed.afterSeed')\n\n/** Register (or replace) the listener under `key`. Equivalent to writing the slot directly. */\nexport const registerAfterSeedListener = (key: string, listener: AfterSeedListener): void => {\n const slot = globalThis as Record<symbol, unknown>\n const listeners = (slot[AFTER_SEED_SLOT] as Record<string, AfterSeedListener> | undefined) ?? {}\n listeners[key] = listener\n slot[AFTER_SEED_SLOT] = listeners\n}\n\n/** The currently registered listeners (empty when none). */\nexport const afterSeedListeners = (): Record<string, AfterSeedListener> =>\n ((globalThis as Record<symbol, unknown>)[AFTER_SEED_SLOT] as Record<string, AfterSeedListener> | undefined) ?? {}\n\n/** Called by the engine after a successful run: invoke every listener, isolating failures. */\nexport const notifyAfterSeed = async (payload: Payload, req: PayloadRequest, result: SeedResult): Promise<void> => {\n for (const [key, listener] of Object.entries(afterSeedListeners())) {\n try {\n await listener(result, { payload, req })\n } catch (err) {\n payload.logger.warn(`[payload-seed] afterSeed listener '${key}' failed: ${err instanceof Error ? err.message : String(err)}`)\n }\n }\n}\n"],"names":["AFTER_SEED_SLOT","Symbol","for","registerAfterSeedListener","key","listener","slot","globalThis","listeners","afterSeedListeners","notifyAfterSeed","payload","req","result","Object","entries","err","logger","warn","Error","message","String"],"mappings":"AAcA,MAAMA,kBAAkBC,OAAOC,GAAG,CAAC;AAEnC,6FAA6F,GAC7F,OAAO,MAAMC,4BAA4B,CAACC,KAAaC;IACrD,MAAMC,OAAOC;IACb,MAAMC,YAAY,AAACF,IAAI,CAACN,gBAAgB,IAAsD,CAAC;IAC/FQ,SAAS,CAACJ,IAAI,GAAGC;IACjBC,IAAI,CAACN,gBAAgB,GAAGQ;AAC1B,EAAC;AAED,0DAA0D,GAC1D,OAAO,MAAMC,qBAAqB,IAChC,AAAC,AAACF,UAAsC,CAACP,gBAAgB,IAAsD,CAAC,EAAC;AAEnH,4FAA4F,GAC5F,OAAO,MAAMU,kBAAkB,OAAOC,SAAkBC,KAAqBC;IAC3E,KAAK,MAAM,CAACT,KAAKC,SAAS,IAAIS,OAAOC,OAAO,CAACN,sBAAuB;QAClE,IAAI;YACF,MAAMJ,SAASQ,QAAQ;gBAAEF;gBAASC;YAAI;QACxC,EAAE,OAAOI,KAAK;YACZL,QAAQM,MAAM,CAACC,IAAI,CAAC,CAAC,mCAAmC,EAAEd,IAAI,UAAU,EAAEY,eAAeG,QAAQH,IAAII,OAAO,GAAGC,OAAOL,MAAM;QAC9H;IACF;AACF,EAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pro-laico/payload-seed",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Schema-aware, type-safe seeding plugin for Payload CMS: typed cross-file references with dependency tracking, robust media uploads, a POST /api/seed endpoint, and a SeedRegistry injected into payload-types.ts.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|