@barbapapazes/content-creation 0.17.0 → 0.17.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -130,21 +130,21 @@ content-creation publish-linkedin-calendar --path /path/to/content
130
130
  This command will:
131
131
  1. Scan your `YYYY/MM/DD` directory structure for `linkedin.md` files only
132
132
  2. Read the LinkedIn frontmatter and generate all-day calendar events
133
- 3. Write a `calendar.ics` file locally
134
- 4. Upload `calendar.ics` to the `content-creation` R2 bucket using Wrangler
133
+ 3. Stream the generated calendar directly to Cloudflare R2 using Wrangler
134
+ 4. Upload `content-creation.ics` to the `content-creation` R2 bucket
135
135
  5. Print the final subscription URL for Google Calendar
136
136
 
137
137
  **Required Configuration**: You must set up the following environment variables in a `.env` file:
138
138
 
139
139
  ```bash
140
- CALENDAR_PUBLIC_URL=https://calendar.soubiran.dev/calendar.ics
140
+ CALENDAR_PUBLIC_URL=https://calendar.soubiran.dev
141
141
  CALENDAR_TOKEN=your-calendar-token-here
142
142
  ```
143
143
 
144
144
  See `.env.example` for a template. The printed URL uses the format:
145
145
 
146
146
  ```text
147
- https://calendar.soubiran.dev/calendar.ics?token=<your-calendar-token>
147
+ https://calendar.soubiran.dev/content-creation.ics?token=<your-calendar-token>
148
148
  ```
149
149
 
150
150
  **Current Scope**: This command publishes LinkedIn entries only for now.
@@ -256,10 +256,8 @@ export default {
256
256
 
257
257
  // Calendar publishing configuration (optional, can also be set via environment variables)
258
258
  calendar: {
259
- publicUrl: 'https://calendar.soubiran.dev/calendar.ics',
259
+ publicUrl: 'https://calendar.soubiran.dev',
260
260
  token: 'your-calendar-token',
261
- wranglerBinary: '/absolute/path/to/wrangler',
262
- outputFile: './.generated/calendar.ics',
263
261
  },
264
262
  }
265
263
  ```
package/dist/cli.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import e from"node:process";import{intro as t,isCancel as n,log as r,multiselect as i,outro as a,select as o,text as s}from"@clack/prompts";import{cac as c}from"cac";import{addDays as l,format as u,parse as d,setHours as ee,setMinutes as te,setSeconds as ne}from"date-fns";import{existsSync as f,mkdirSync as p,mkdtempSync as m,readFileSync as h,readdirSync as g,rmSync as re,statSync as _,writeFileSync as v}from"node:fs";import{dirname as y,isAbsolute as b,join as x,resolve as S}from"node:path";import C from"gray-matter";import{loadConfig as ie}from"c12";import{execFileSync as ae}from"node:child_process";import{createHash as w}from"node:crypto";import{tmpdir as T}from"node:os";import{fileURLToPath as E}from"node:url";var D=`0.17.0`;const O={linkedin:{mainFile:`linkedin.md`,additionalFiles:[`linkedin-script.md`]},x:{mainFile:`x.md`},youtube:{mainFile:`youtube-script.md`,additionalFiles:[`youtube-description.md`]},instagram:{mainFile:`instagram-script.md`,additionalFiles:[`instagram-description.md`]}},oe={thematic:[],templatesDir:void 0,templates:{},scheduling:{},calendar:{}};function k(e,t){if(e)return b(e)?e:S(t,e)}function se(t,n){return t?b(t)?t:S(n?y(n):e.cwd(),t):n?y(n):e.cwd()}function ce(e,t){let n={};if(e?.footerPath){let r=S(t,e.footerPath);f(r)&&(n.footerPath=r)}return n}function A(e,t){let n={};if(e?.templatePath){let r=S(t,e.templatePath);f(r)&&(n.templatePath=r)}return n}async function j(){let{config:t,configFile:n}=await ie({name:`content-creation`,defaults:oe,globalRc:!0,dotenv:!0}),r=se(t.templatesDir,n),i={linkedin:ce(t.templates?.linkedin,r),youtube:A(t.templates?.youtube,r),instagram:A(t.templates?.instagram,r)},a={automationEndpoint:t.scheduling?.automationEndpoint||e.env.AUTOMATION_ENDPOINT,cfAccessClientId:t.scheduling?.cfAccessClientId||e.env.CF_ACCESS_CLIENT_ID,cfAccessClientSecret:t.scheduling?.cfAccessClientSecret||e.env.CF_ACCESS_CLIENT_SECRET},o={publicUrl:t.calendar?.publicUrl||e.env.CALENDAR_PUBLIC_URL,token:t.calendar?.token||e.env.CALENDAR_TOKEN,wranglerBinary:k(t.calendar?.wranglerBinary||e.env.CONTENT_CREATION_WRANGLER_BINARY,r),outputFile:k(t.calendar?.outputFile||e.env.CONTENT_CREATION_CALENDAR_OUTPUT_FILE,r)};return{thematic:t.thematic||[],templatesDir:t.templatesDir||``,templates:i,scheduling:a,calendar:o}}function M(e){return f(e)?h(e,`utf-8`):``}function N(t,n,i,a,o=e.cwd()){let s=S(x(o,u(t,`yyyy`),u(t,`MM`),u(t,`dd`)));f(s)||(p(s,{recursive:!0}),r.success(`Created directory: ${s}`));for(let e of n)P(e,s,i,a);return s}function P(e,t,n,i){let a=O[e],o=x(t,a.mainFile);if(f(o)?r.info(`${a.mainFile} already exists, skipping`):F(e,o,n,i),a.additionalFiles)for(let o of a.additionalFiles){let a=x(t,o);L(o,e,n)&&(f(a)?r.info(`${o} already exists, skipping`):I(e,a,o,i))}}function F(e,t,n,i){let a={title:n.title};e===`linkedin`&&(n.theme&&(a.theme=n.theme),n.hasVideo&&(a.video=!0),n.hasImages&&(a.images=[null]));let o=``;if(e===`linkedin`){let e=i.templates.linkedin?.footerPath;o=e?M(e):``}v(t,C.stringify(o,a),`utf-8`),r.success(`Created ${t}`)}function I(e,t,n,i){let a=``;if(n.endsWith(`-description.md`)){let t=i.templates[e]?.templatePath;t&&(a=M(t))}v(t,a,`utf-8`),r.success(`Created ${t}`)}function L(e,t,n){return t===`linkedin`&&e===`linkedin-script.md`?n.hasVideo||!1:!0}const R=[`linkedin`,`x`,`youtube`,`instagram`],z={linkedin:`LinkedIn`,x:`X`,youtube:`YouTube`,instagram:`Instagram`};function B(e,t){let n=[],i=O[t].mainFile;try{let t=g(e).filter(t=>_(x(e,t)).isDirectory()&&/^\d{4}$/.test(t));for(let a of t){let t=x(e,a),o=g(t).filter(e=>_(x(t,e)).isDirectory()&&/^\d{2}$/.test(e));for(let e of o){let o=x(t,e),s=g(o).filter(e=>_(x(o,e)).isDirectory()&&/^\d{2}$/.test(e));for(let t of s){let s=S(x(o,t),i);if(!f(s))continue;let c=`${a}-${e}-${t}`,l=new Date(Number(a),Number(e)-1,Number(t));try{let{data:r}=C(h(s,`utf-8`)),o=r.title||`Untitled (${c})`;n.push({path:s,date:l,title:o,relativePath:`${a}/${e}/${t}/${i}`})}catch(e){r.warn(`Error reading ${s}: ${e}`)}}}}}catch(e){return r.error(`Error reading directories: ${e}`),[]}return n.sort((e,t)=>t.date.getTime()-e.date.getTime())}function V(e){switch(e){case`linkedin`:return`linkedin-posts.md`;case`x`:return`x-posts.md`;case`youtube`:return`youtube-videos.md`;case`instagram`:return`instagram-posts.md`}}function H(e){switch(e){case`linkedin`:return`LinkedIn Posts`;case`x`:return`X Posts`;case`youtube`:return`YouTube Videos`;case`instagram`:return`Instagram Posts`}}function U(e,t){let n=B(e,t);if(n.length===0){r.info(`No ${t} posts found`);return}let i=H(t),a=V(t),o=`# ${i}\n\n`;o+=`_Generated on ${new Date().toLocaleDateString()}_\n\n`,o+=`Total posts: ${n.length}\n\n`;for(let e of n){let t=e.date.toLocaleDateString(`en-US`,{year:`numeric`,month:`long`,day:`numeric`});o+=`- [${e.title}](${e.relativePath}) - _${t}_\n`}v(x(e,a),o,`utf-8`),r.success(`Created ${a} with ${n.length} posts`)}function W(t=e.cwd(),n){let r=n||[...R];for(let e of r)U(t,e)}function G(e){return e.toLowerCase().trim().normalize(`NFD`).replace(/[\u0300-\u036F]/g,``).replace(/[^a-z0-9\s-]/g,``).replace(/\s+/g,`-`).replace(/-+/g,`-`).replace(/^-+|-+$/g,``)||`untitled`}function le(t,n,i=e.cwd()){let a=G(t),o=x(S(x(i,`resources`)),a),s=`${n}.md`,c=x(o,s);if(f(c))return r.info(`${s} already exists at: ${c}`),o;p(o,{recursive:!0}),r.success(`Created directory: ${o}`);let l={title:t,url:``,date:``};return v(c,C.stringify(``,l),`utf-8`),r.success(`Created ${s} at: ${c}`),o}function ue(t=e.cwd()){let n=[];try{let e=g(t).filter(e=>_(x(t,e)).isDirectory()&&/^\d{4}$/.test(e));for(let r of e){let e=x(t,r),i=g(e).filter(t=>_(x(e,t)).isDirectory()&&/^\d{2}$/.test(t));for(let t of i){let i=x(e,t),a=g(i).filter(e=>_(x(i,e)).isDirectory()&&/^\d{2}$/.test(e));for(let e of a){let a=x(i,e);if(f(x(a,`linkedin.md`))){let i=`${r}-${t}-${e}`;n.push({path:a,date:new Date(i),displayPath:`${r}/${t}/${e}`})}}}}}catch(e){return r.error(`Error reading directories: ${e}`),[]}return n.sort((e,t)=>t.date.getTime()-e.date.getTime()).slice(0,8)}function de(e){let t=[`.jpg`,`.jpeg`,`.png`],n=[];try{let r=g(e);for(let i of r)if(_(x(e,i)).isFile()){let e=i.toLowerCase().substring(i.lastIndexOf(`.`));t.includes(e)&&n.push(i)}}catch(e){r.error(`Error reading directory: ${e}`)}return n.sort()}function K(e,t){try{let{data:n,content:i}=C(h(e,`utf-8`));n.images=t.length>0?t:[null],v(e,C.stringify(i,n),`utf-8`),r.success(`Updated ${e} with ${t.length} image(s)`)}catch(e){r.error(`Error updating frontmatter: ${e}`)}}async function fe(t){if(t.length===0)return r.warn(`No dated folders with linkedin.md found`),null;let i=await o({message:`Select a folder to link images:`,options:t.map(e=>({label:e.displayPath,value:e.path,hint:`Link images in ${e.displayPath}`}))});return n(i)&&(r.error(`Operation cancelled.`),e.exit(0)),t.find(e=>e.path===i)||null}async function pe(t=e.cwd()){let n=ue(t);if(n.length===0){r.warn(`No dated folders with linkedin.md found`);return}let i=await fe(n);if(!i)return;let a=de(i.path);if(a.length===0){r.info(`No images found in ${i.displayPath}`),K(x(i.path,`linkedin.md`),[]);return}r.info(`Found ${a.length} image(s): ${a.join(`, `)}`),K(x(i.path,`linkedin.md`),a)}function me(e){let t=new Date;t.setHours(0,0,0,0);let n=R.flatMap(t=>B(e,t).map(e=>({...e,contentType:t}))).filter(e=>e.date.getTime()>=t.getTime()).sort((e,t)=>{let n=e.date.getTime()-t.date.getTime();return n===0?R.indexOf(e.contentType)-R.indexOf(t.contentType):n});if(n.length===0){r.info(`No upcoming content found from today onward.`);return}r.info(`Found ${n.length} upcoming content item${n.length===1?``:`s`}:`),console.log(``);for(let e of n){let t=u(e.date,`EEEE, MMMM d, yyyy`),n=u(e.date,`yyyy-MM-dd`);console.log(`${t} (${n}) · ${z[e.contentType]} · ${e.title}`),console.log(e.path),console.log(``)}}const q=E(new URL(`../../`,import.meta.url)),J=`calendar.ics`;function Y(e){return e.replace(/\\/g,`\\\\`).replace(/\r\n|\r|\n/g,`\\n`).replace(/;/g,`\\;`).replace(/,/g,`\\,`)}function he(e){if(e.length<=75)return e;let t=[],n=e;for(;n.length>75;)t.push(n.slice(0,75)),n=` ${n.slice(75)}`;return t.push(n),t.join(`\r
3
- `)}function ge(e){return e.toISOString().replace(/[-:]/g,``).replace(/\.\d{3}Z$/,`Z`)}function X(e){return u(e,`yyyyMMdd`)}function _e(e){let t=[`Relative path: ${e.relativePath}`,`Absolute path: ${e.path}`];return e.theme&&t.push(`Theme: ${e.theme}`),typeof e.video==`boolean`&&t.push(`Video planned: ${e.video?`yes`:`no`}`),e.imageCount>0&&t.push(`Images planned: ${e.imageCount}`),t.join(`
4
- `)}function ve(e){return`linkedin-${w(`sha1`).update(e).digest(`hex`)}@barbapapazes`}function ye(e){let t=[`BEGIN:VCALENDAR`,`VERSION:2.0`,`PRODID:-//Barbapapazes//Content Creation LinkedIn Calendar//EN`,`CALSCALE:GREGORIAN`,`METHOD:PUBLISH`,`X-WR-CALNAME:${Y(`LinkedIn Publications`)}`];for(let n of e){let e=ge(new Date(Date.UTC(n.date.getFullYear(),n.date.getMonth(),n.date.getDate()))),r=l(n.date,1);t.push(`BEGIN:VEVENT`,`UID:${ve(n.relativePath)}`,`DTSTAMP:${e}`,`SUMMARY:${Y(`${z.linkedin} · ${n.title}`)}`,`DTSTART;VALUE=DATE:${X(n.date)}`,`DTEND;VALUE=DATE:${X(r)}`,`DESCRIPTION:${Y(_e(n))}`,`END:VEVENT`)}return t.push(`END:VCALENDAR`),`${t.map(he).join(`\r
5
- `)}\r\n`}function be(){let t=x(q,`node_modules`,`.bin`,e.platform===`win32`?`wrangler.cmd`:`wrangler`);return f(t)?t:`wrangler`}function xe(t){(!t.calendar.publicUrl||!t.calendar.token)&&(r.error(`Calendar publishing configuration is missing. Please set CALENDAR_PUBLIC_URL and CALENDAR_TOKEN in your .env file or config.`),e.exit(1));let n=new URL(t.calendar.publicUrl);return n.searchParams.set(`token`,t.calendar.token),n.toString()}function Z(e){return B(e,`linkedin`).map(e=>{let t={title:e.title};try{t=C(h(e.path,`utf-8`)).data}catch(t){r.warn(`Failed to read LinkedIn metadata from ${e.path}: ${t}`)}return{...e,title:t.title||e.title,theme:t.theme,video:t.video,imageCount:t.images?.filter(Boolean).length??0}}).sort((e,t)=>e.date.getTime()-t.date.getTime())}function Se(e){if(e.calendar.outputFile)return p(y(e.calendar.outputFile),{recursive:!0}),{outputFile:e.calendar.outputFile,cleanup:()=>{}};let t=m(x(T(),`content-creation-calendar-`));return{outputFile:x(t,J),cleanup:()=>re(t,{recursive:!0,force:!0})}}function Ce(t){let n=be();try{ae(n,[`r2`,`object`,`put`,`content-creation/${J}`,`--file`,t,`--content-type`,`text/calendar; charset=utf-8`,`--remote`],{cwd:q,stdio:`inherit`,env:e.env})}catch(t){r.error(`Failed to upload LinkedIn calendar: ${t instanceof Error?t.message:String(t)}`),e.exit(1)}}async function we(e,t){let n=Z(e);if(n.length===0)return r.info(`No LinkedIn publications found. Skipping calendar upload.`),null;let i=xe(t),{outputFile:a,cleanup:o}=Se(t);try{return v(a,ye(n),`utf-8`),Ce(a),r.success(`✓ LinkedIn calendar uploaded to R2 as ${J}`),r.info(`Subscription URL: ${i}`),i}finally{o()}}async function Te(t,n){let i=x(t,`x.md`),{data:a,content:o}=C(h(i,`utf-8`));a.scheduled===!0&&(r.warn(`This content is already scheduled. Skipping.`),e.exit(0)),(!n.scheduling.automationEndpoint||!n.scheduling.cfAccessClientId||!n.scheduling.cfAccessClientSecret)&&(r.error(`Scheduling configuration is missing. Please set AUTOMATION_ENDPOINT, CF_ACCESS_CLIENT_ID, and CF_ACCESS_CLIENT_SECRET in your .env file or config.`),e.exit(1));let s=t.split(`/`),c=s[s.length-1],l=s[s.length-2],f=d(`${s[s.length-3]}-${l}-${c}`,`yyyy-MM-dd`,new Date);f=ee(f,11),f=te(f,0),f=ne(f,0);let p=f.getTime(),m={content:o.trim(),scheduleAt:p};try{let t=await fetch(n.scheduling.automationEndpoint,{method:`POST`,headers:{"Content-Type":`application/json`,Accept:`application/json`,"CF-Access-Client-Id":n.scheduling.cfAccessClientId,"CF-Access-Client-Secret":n.scheduling.cfAccessClientSecret},body:JSON.stringify(m)});if(!t.ok){let n=await t.text();r.error(`Failed to schedule reminder: ${t.status} ${t.statusText}\n${n}`),e.exit(1)}let s=await t.json(),c={...a,scheduled:!0};v(i,C.stringify(o,c),`utf-8`);let l=new Date(s.scheduledAt);r.success(`✓ Reminder scheduled successfully!`),r.info(`Scheduled for: ${u(l,`yyyy-MM-dd HH:mm:ss`)} UTC`)}catch(t){r.error(`Failed to schedule reminder: ${t instanceof Error?t.message:String(t)}`),e.exit(1)}}async function Ee(){let t=new Date,i=[];for(let e=0;e<7;e++){let n=l(t,e),r=u(n,`yyyy-MM-dd`),a=u(n,`EEEE`),o=r,s=`Create content for ${a}, ${r}`;e===0?(o=`Today - ${a} (${r})`,s=`Create content for today (${a})`):e===1?(o=`Tomorrow - ${a} (${r})`,s=`Create content for tomorrow (${a})`):(o=`In ${e} days - ${a} (${r})`,s=`Create content for ${a}, ${r}`),i.push({label:o,value:`day-${e}`,hint:s})}i.push({label:`Custom date`,value:`custom`,hint:`Enter a specific date`});let a=await o({message:`When do you want to create content?`,options:i});if(n(a)&&(r.error(`Operation cancelled.`),e.exit(0)),a!==`custom`)return l(t,Number.parseInt(a.split(`-`)[1]));let c=await s({message:`Enter date (YYYY-MM-DD):`,placeholder:u(t,`yyyy-MM-dd`),validate:e=>{if(!e)return`Date is required`;if(!/^\d{4}-\d{2}-\d{2}$/.test(e))return`Invalid date format. Please use YYYY-MM-DD`;try{let t=d(e,`yyyy-MM-dd`,new Date);if(Number.isNaN(t.getTime()))return`Invalid date`}catch{return`Invalid date`}}});return n(c)&&(r.error(`Operation cancelled.`),e.exit(0)),d(c,`yyyy-MM-dd`,new Date)}async function Q(){let t=await s({message:`What is the title of your content?`,placeholder:`Enter content title`,validate:e=>{if(!e||e.trim().length===0)return`Title is required`}});return n(t)&&(r.error(`Operation cancelled.`),e.exit(0)),t.trim()}async function De(){let t=await o({message:`Is a LinkedIn video planned?`,options:[{label:`Yes`,value:!0,hint:`Add video: true to frontmatter`},{label:`No`,value:!1,hint:`No video metadata`}]});return n(t)&&(r.error(`Operation cancelled.`),e.exit(0)),t}async function Oe(){let t=await o({message:`Are there images?`,options:[{label:`Yes`,value:!0,hint:`Add images section to frontmatter`},{label:`No`,value:!1,hint:`No images metadata`}]});return n(t)&&(r.error(`Operation cancelled.`),e.exit(0)),t}async function ke(t){if(t.length===0)return;let i=await o({message:`What is the content theme? (optional)`,options:[{label:`No theme`,value:``,hint:`Leave theme empty`},...t.map(e=>({label:e,value:e,hint:`Use theme: ${e}`}))]});n(i)&&(r.error(`Operation cancelled.`),e.exit(0));let a=i;return a.length>0?a:void 0}async function Ae(){let t=await o({message:`What type of resource is this?`,options:[{label:`Article`,value:`article`,hint:`Create article.md`},{label:`Video`,value:`video`,hint:`Create video.md`},{label:`Audio`,value:`audio`,hint:`Create audio.md`},{label:`Tweet`,value:`tweet`,hint:`Create tweet.md`}]});return n(t)&&(r.error(`Operation cancelled.`),e.exit(0)),t}async function je(){let t=await i({message:`Which content types do you want to create?`,options:[{label:`LinkedIn`,value:`linkedin`,hint:`Create LinkedIn post and optional script`},{label:`X (Twitter)`,value:`x`,hint:`Create X post`},{label:`YouTube`,value:`youtube`,hint:`Create YouTube script and description`},{label:`Instagram`,value:`instagram`,hint:`Create Instagram script and description`}],required:!0});return n(t)&&(r.error(`Operation cancelled.`),e.exit(0)),t}async function Me(t){let i=[];f(t)||(r.error(`Base path does not exist: ${t}`),e.exit(1));let a=g(t).filter(e=>_(x(t,e)).isDirectory()&&/^\d{4}$/.test(e));for(let e of a){let n=x(t,e),r=g(n).filter(e=>_(x(n,e)).isDirectory()&&/^\d{2}$/.test(e));for(let t of r){let r=x(n,t),a=g(r).filter(e=>_(x(r,e)).isDirectory()&&/^\d{2}$/.test(e));for(let n of a){let a=x(r,n);if(f(x(a,`x.md`))){let r=`${e}-${t}-${n}`;i.push({path:a,date:r,title:``})}}}}i.length===0&&(r.error(`No x.md files found in dated folders`),e.exit(1)),i.sort((e,t)=>t.date.localeCompare(e.date));let s=await o({message:`Which content do you want to schedule a reminder for?`,options:i.map(e=>({label:e.date,value:e.path,hint:`Schedule reminder for ${e.date}`}))});return n(s)&&(r.error(`Operation cancelled.`),e.exit(0)),s}const $=c(`content-creation`);$.command(`[path]`,`Create a dated content directory with content files`).option(`--path <path>`,`Base path for content directory (defaults to current directory)`).action(async(n,r)=>{t(`Content Creation - Create dated content directory`);let i=await j(),o=await Q(),s=await je(),c={title:o};s.includes(`linkedin`)&&(c.hasVideo=await De(),c.hasImages=await Oe(),i.thematic.length>0&&(c.theme=await ke(i.thematic)));let l=await Ee();N(l,s,c,i,n||r?.path||e.cwd());let d=s.join(`, `);a(`✓ Content directory created: ${u(l,`yyyy/MM/dd`)} (${d})`)}),$.command(`link-images [path]`,`Link images to LinkedIn frontmatter in recent folders`).option(`--path <path>`,`Base path for content directory (defaults to current directory)`).action(async(n,r)=>{t(`Content Creation - Link Images to LinkedIn`),await pe(n||r?.path||e.cwd()),a(`✓ Images linked successfully`)}),$.command(`resource [path]`,`Create a resource with article/video/audio markdown file`).option(`--path <path>`,`Base path for resource directory (defaults to current directory)`).action(async(n,r)=>{t(`Content Creation - Create Resource`);let i=await Q(),o=await Ae();le(i,o,n||r?.path||e.cwd()),a(`✓ Resource created: resources/${G(i)}/${o}.md`)}),$.command(`create-index [path]`,`Create index files for each content type`).option(`--path <path>`,`Base path for content directory (defaults to current directory)`).action(async(n,r)=>{t(`Content Creation - Create Index Files`),W(n||r?.path||e.cwd()),a(`✓ Index files generated successfully`)}),$.command(`list-upcoming [path]`,`List content scheduled for today or later`).option(`--path <path>`,`Base path for content directory (defaults to current directory)`).action(async(n,r)=>{t(`Content Creation - List Upcoming Content`),me(n||r?.path||e.cwd()),a(`✓ Upcoming content listed successfully`)}),$.command(`publish-linkedin-calendar [path]`,`Create and upload a LinkedIn publication calendar`).option(`--path <path>`,`Base path for content directory (defaults to current directory)`).action(async(n,r)=>{t(`Content Creation - Publish LinkedIn Calendar`);let i=await j();if(await we(n||r?.path||e.cwd(),i)){a(`✓ LinkedIn calendar published successfully`);return}a(`✓ No LinkedIn content found, calendar was not updated`)}),$.command(`schedule-reminder [path]`,`Schedule a reminder for X content`).option(`--path <path>`,`Base path for content directory (defaults to current directory)`).action(async(n,r)=>{t(`Content Creation - Schedule X Reminder`);let i=await j();await Te(await Me(n||r?.path||e.cwd()),i),a(`✓ Reminder scheduled successfully`)}),$.help(),$.version(D),$.parse();export{};
2
+ import e from"node:process";import{intro as t,isCancel as n,log as r,multiselect as i,outro as a,select as o,text as s}from"@clack/prompts";import{cac as c}from"cac";import{addDays as l,format as u,parse as d,setHours as ee,setMinutes as te,setSeconds as ne}from"date-fns";import{existsSync as f,mkdirSync as p,readFileSync as m,readdirSync as h,statSync as g,writeFileSync as _}from"node:fs";import{dirname as v,isAbsolute as re,join as y,resolve as b}from"node:path";import x from"gray-matter";import{loadConfig as S}from"c12";import{execFileSync as C}from"node:child_process";import{createHash as w}from"node:crypto";import{fileURLToPath as T}from"node:url";var E=`0.17.1`;const D={linkedin:{mainFile:`linkedin.md`,additionalFiles:[`linkedin-script.md`]},x:{mainFile:`x.md`},youtube:{mainFile:`youtube-script.md`,additionalFiles:[`youtube-description.md`]},instagram:{mainFile:`instagram-script.md`,additionalFiles:[`instagram-description.md`]}},ie={thematic:[],templatesDir:void 0,templates:{},scheduling:{},calendar:{}};function ae(t,n){return t?re(t)?t:b(n?v(n):e.cwd(),t):n?v(n):e.cwd()}function oe(e,t){let n={};if(e?.footerPath){let r=b(t,e.footerPath);f(r)&&(n.footerPath=r)}return n}function O(e,t){let n={};if(e?.templatePath){let r=b(t,e.templatePath);f(r)&&(n.templatePath=r)}return n}async function k(){let{config:t,configFile:n}=await S({name:`content-creation`,defaults:ie,globalRc:!0,dotenv:!0}),r=ae(t.templatesDir,n),i={linkedin:oe(t.templates?.linkedin,r),youtube:O(t.templates?.youtube,r),instagram:O(t.templates?.instagram,r)},a={automationEndpoint:t.scheduling?.automationEndpoint||e.env.AUTOMATION_ENDPOINT,cfAccessClientId:t.scheduling?.cfAccessClientId||e.env.CF_ACCESS_CLIENT_ID,cfAccessClientSecret:t.scheduling?.cfAccessClientSecret||e.env.CF_ACCESS_CLIENT_SECRET},o={publicUrl:t.calendar?.publicUrl||e.env.CALENDAR_PUBLIC_URL,token:t.calendar?.token||e.env.CALENDAR_TOKEN};return{thematic:t.thematic||[],templatesDir:t.templatesDir||``,templates:i,scheduling:a,calendar:o}}function A(e){return f(e)?m(e,`utf-8`):``}function se(t,n,i,a,o=e.cwd()){let s=b(y(o,u(t,`yyyy`),u(t,`MM`),u(t,`dd`)));f(s)||(p(s,{recursive:!0}),r.success(`Created directory: ${s}`));for(let e of n)j(e,s,i,a);return s}function j(e,t,n,i){let a=D[e],o=y(t,a.mainFile);if(f(o)?r.info(`${a.mainFile} already exists, skipping`):M(e,o,n,i),a.additionalFiles)for(let o of a.additionalFiles){let a=y(t,o);P(o,e,n)&&(f(a)?r.info(`${o} already exists, skipping`):N(e,a,o,i))}}function M(e,t,n,i){let a={title:n.title};e===`linkedin`&&(n.theme&&(a.theme=n.theme),n.hasVideo&&(a.video=!0),n.hasImages&&(a.images=[null]));let o=``;if(e===`linkedin`){let e=i.templates.linkedin?.footerPath;o=e?A(e):``}_(t,x.stringify(o,a),`utf-8`),r.success(`Created ${t}`)}function N(e,t,n,i){let a=``;if(n.endsWith(`-description.md`)){let t=i.templates[e]?.templatePath;t&&(a=A(t))}_(t,a,`utf-8`),r.success(`Created ${t}`)}function P(e,t,n){return t===`linkedin`&&e===`linkedin-script.md`?n.hasVideo||!1:!0}const F=[`linkedin`,`x`,`youtube`,`instagram`],I={linkedin:`LinkedIn`,x:`X`,youtube:`YouTube`,instagram:`Instagram`};function L(e,t){let n=[],i=D[t].mainFile;try{let t=h(e).filter(t=>g(y(e,t)).isDirectory()&&/^\d{4}$/.test(t));for(let a of t){let t=y(e,a),o=h(t).filter(e=>g(y(t,e)).isDirectory()&&/^\d{2}$/.test(e));for(let e of o){let o=y(t,e),s=h(o).filter(e=>g(y(o,e)).isDirectory()&&/^\d{2}$/.test(e));for(let t of s){let s=b(y(o,t),i);if(!f(s))continue;let c=`${a}-${e}-${t}`,l=new Date(Number(a),Number(e)-1,Number(t));try{let{data:r}=x(m(s,`utf-8`)),o=r.title||`Untitled (${c})`;n.push({path:s,date:l,title:o,relativePath:`${a}/${e}/${t}/${i}`})}catch(e){r.warn(`Error reading ${s}: ${e}`)}}}}}catch(e){return r.error(`Error reading directories: ${e}`),[]}return n.sort((e,t)=>t.date.getTime()-e.date.getTime())}function R(e){switch(e){case`linkedin`:return`linkedin-posts.md`;case`x`:return`x-posts.md`;case`youtube`:return`youtube-videos.md`;case`instagram`:return`instagram-posts.md`}}function z(e){switch(e){case`linkedin`:return`LinkedIn Posts`;case`x`:return`X Posts`;case`youtube`:return`YouTube Videos`;case`instagram`:return`Instagram Posts`}}function B(e,t){let n=L(e,t);if(n.length===0){r.info(`No ${t} posts found`);return}let i=z(t),a=R(t),o=`# ${i}\n\n`;o+=`_Generated on ${new Date().toLocaleDateString()}_\n\n`,o+=`Total posts: ${n.length}\n\n`;for(let e of n){let t=e.date.toLocaleDateString(`en-US`,{year:`numeric`,month:`long`,day:`numeric`});o+=`- [${e.title}](${e.relativePath}) - _${t}_\n`}_(y(e,a),o,`utf-8`),r.success(`Created ${a} with ${n.length} posts`)}function V(t=e.cwd(),n){let r=n||[...F];for(let e of r)B(t,e)}function H(e){return e.toLowerCase().trim().normalize(`NFD`).replace(/[\u0300-\u036F]/g,``).replace(/[^a-z0-9\s-]/g,``).replace(/\s+/g,`-`).replace(/-+/g,`-`).replace(/^-+|-+$/g,``)||`untitled`}function U(t,n,i=e.cwd()){let a=H(t),o=y(b(y(i,`resources`)),a),s=`${n}.md`,c=y(o,s);if(f(c))return r.info(`${s} already exists at: ${c}`),o;p(o,{recursive:!0}),r.success(`Created directory: ${o}`);let l={title:t,url:``,date:``};return _(c,x.stringify(``,l),`utf-8`),r.success(`Created ${s} at: ${c}`),o}function W(t=e.cwd()){let n=[];try{let e=h(t).filter(e=>g(y(t,e)).isDirectory()&&/^\d{4}$/.test(e));for(let r of e){let e=y(t,r),i=h(e).filter(t=>g(y(e,t)).isDirectory()&&/^\d{2}$/.test(t));for(let t of i){let i=y(e,t),a=h(i).filter(e=>g(y(i,e)).isDirectory()&&/^\d{2}$/.test(e));for(let e of a){let a=y(i,e);if(f(y(a,`linkedin.md`))){let i=`${r}-${t}-${e}`;n.push({path:a,date:new Date(i),displayPath:`${r}/${t}/${e}`})}}}}}catch(e){return r.error(`Error reading directories: ${e}`),[]}return n.sort((e,t)=>t.date.getTime()-e.date.getTime()).slice(0,8)}function G(e){let t=[`.jpg`,`.jpeg`,`.png`],n=[];try{let r=h(e);for(let i of r)if(g(y(e,i)).isFile()){let e=i.toLowerCase().substring(i.lastIndexOf(`.`));t.includes(e)&&n.push(i)}}catch(e){r.error(`Error reading directory: ${e}`)}return n.sort()}function K(e,t){try{let{data:n,content:i}=x(m(e,`utf-8`));n.images=t.length>0?t:[null],_(e,x.stringify(i,n),`utf-8`),r.success(`Updated ${e} with ${t.length} image(s)`)}catch(e){r.error(`Error updating frontmatter: ${e}`)}}async function ce(t){if(t.length===0)return r.warn(`No dated folders with linkedin.md found`),null;let i=await o({message:`Select a folder to link images:`,options:t.map(e=>({label:e.displayPath,value:e.path,hint:`Link images in ${e.displayPath}`}))});return n(i)&&(r.error(`Operation cancelled.`),e.exit(0)),t.find(e=>e.path===i)||null}async function le(t=e.cwd()){let n=W(t);if(n.length===0){r.warn(`No dated folders with linkedin.md found`);return}let i=await ce(n);if(!i)return;let a=G(i.path);if(a.length===0){r.info(`No images found in ${i.displayPath}`),K(y(i.path,`linkedin.md`),[]);return}r.info(`Found ${a.length} image(s): ${a.join(`, `)}`),K(y(i.path,`linkedin.md`),a)}function ue(e){let t=new Date;t.setHours(0,0,0,0);let n=F.flatMap(t=>L(e,t).map(e=>({...e,contentType:t}))).filter(e=>e.date.getTime()>=t.getTime()).sort((e,t)=>{let n=e.date.getTime()-t.date.getTime();return n===0?F.indexOf(e.contentType)-F.indexOf(t.contentType):n});if(n.length===0){r.info(`No upcoming content found from today onward.`);return}r.info(`Found ${n.length} upcoming content item${n.length===1?``:`s`}:`),console.log(``);for(let e of n){let t=u(e.date,`EEEE, MMMM d, yyyy`),n=u(e.date,`yyyy-MM-dd`);console.log(`${t} (${n}) · ${I[e.contentType]} · ${e.title}`),console.log(e.path),console.log(``)}}const q=T(new URL(`../../`,import.meta.url)),J=`content-creation.ics`;function Y(e){return e.replace(/\\/g,`\\\\`).replace(/\r\n|\r|\n/g,`\\n`).replace(/;/g,`\\;`).replace(/,/g,`\\,`)}function de(e){if(e.length<=75)return e;let t=[],n=e;for(;n.length>75;)t.push(n.slice(0,75)),n=` ${n.slice(75)}`;return t.push(n),t.join(`\r
3
+ `)}function fe(e){return e.toISOString().replace(/[-:]/g,``).replace(/\.\d{3}Z$/,`Z`)}function X(e){return u(e,`yyyyMMdd`)}function pe(e){let t=[`Relative path: ${e.relativePath}`,`Absolute path: ${e.path}`];return e.theme&&t.push(`Theme: ${e.theme}`),typeof e.video==`boolean`&&t.push(`Video planned: ${e.video?`yes`:`no`}`),e.imageCount>0&&t.push(`Images planned: ${e.imageCount}`),t.join(`
4
+ `)}function me(e){return`linkedin-${w(`sha1`).update(e).digest(`hex`)}@barbapapazes`}function he(e){let t=[`BEGIN:VCALENDAR`,`VERSION:2.0`,`PRODID:-//Barbapapazes//Content Creation LinkedIn Calendar//EN`,`CALSCALE:GREGORIAN`,`METHOD:PUBLISH`,`X-WR-CALNAME:${Y(`content-creation.ics`)}`];for(let n of e){let e=fe(new Date(Date.UTC(n.date.getFullYear(),n.date.getMonth(),n.date.getDate()))),r=l(n.date,1);t.push(`BEGIN:VEVENT`,`UID:${me(n.relativePath)}`,`DTSTAMP:${e}`,`SUMMARY:${Y(`${I.linkedin} · ${n.title}`)}`,`DTSTART;VALUE=DATE:${X(n.date)}`,`DTEND;VALUE=DATE:${X(r)}`,`DESCRIPTION:${Y(pe(n))}`,`END:VEVENT`)}return t.push(`END:VCALENDAR`),`${t.map(de).join(`\r
5
+ `)}\r\n`}function ge(){let t=y(q,`node_modules`,`.bin`,e.platform===`win32`?`wrangler.cmd`:`wrangler`);return f(t)?t:`wrangler`}function _e(t){(!t.calendar.publicUrl||!t.calendar.token)&&(r.error(`Calendar publishing configuration is missing. Please set CALENDAR_PUBLIC_URL and CALENDAR_TOKEN in your .env file or config.`),e.exit(1));let n=t.calendar.publicUrl.endsWith(`/`)?t.calendar.publicUrl:`${t.calendar.publicUrl}/`,i=new URL(J,n);return i.searchParams.set(`token`,t.calendar.token),i.toString()}function ve(e){return L(e,`linkedin`).map(e=>{let t={title:e.title};try{t=x(m(e.path,`utf-8`)).data}catch(t){r.warn(`Failed to read LinkedIn metadata from ${e.path}: ${t}`)}return{...e,title:t.title||e.title,theme:t.theme,video:t.video,imageCount:t.images?.filter(Boolean).length??0}}).sort((e,t)=>e.date.getTime()-t.date.getTime())}function ye(t){let n=ge();try{C(n,[`r2`,`object`,`put`,`content-creation/${J}`,`--pipe`,`--content-type`,`text/calendar; charset=utf-8`,`--remote`],{cwd:q,stdio:[`pipe`,`inherit`,`inherit`],env:e.env,input:t})}catch(t){r.error(`Failed to upload LinkedIn calendar: ${t instanceof Error?t.message:String(t)}`),e.exit(1)}}async function be(e,t){let n=ve(e);if(n.length===0)return r.info(`No LinkedIn publications found. Skipping calendar upload.`),null;let i=_e(t);return ye(he(n)),r.success(`✓ LinkedIn calendar uploaded to R2 as ${J}`),r.info(`Subscription URL: ${i}`),i}async function xe(t,n){let i=y(t,`x.md`),{data:a,content:o}=x(m(i,`utf-8`));a.scheduled===!0&&(r.warn(`This content is already scheduled. Skipping.`),e.exit(0)),(!n.scheduling.automationEndpoint||!n.scheduling.cfAccessClientId||!n.scheduling.cfAccessClientSecret)&&(r.error(`Scheduling configuration is missing. Please set AUTOMATION_ENDPOINT, CF_ACCESS_CLIENT_ID, and CF_ACCESS_CLIENT_SECRET in your .env file or config.`),e.exit(1));let s=t.split(`/`),c=s[s.length-1],l=s[s.length-2],f=d(`${s[s.length-3]}-${l}-${c}`,`yyyy-MM-dd`,new Date);f=ee(f,11),f=te(f,0),f=ne(f,0);let p=f.getTime(),h={content:o.trim(),scheduleAt:p};try{let t=await fetch(n.scheduling.automationEndpoint,{method:`POST`,headers:{"Content-Type":`application/json`,Accept:`application/json`,"CF-Access-Client-Id":n.scheduling.cfAccessClientId,"CF-Access-Client-Secret":n.scheduling.cfAccessClientSecret},body:JSON.stringify(h)});if(!t.ok){let n=await t.text();r.error(`Failed to schedule reminder: ${t.status} ${t.statusText}\n${n}`),e.exit(1)}let s=await t.json(),c={...a,scheduled:!0};_(i,x.stringify(o,c),`utf-8`);let l=new Date(s.scheduledAt);r.success(`✓ Reminder scheduled successfully!`),r.info(`Scheduled for: ${u(l,`yyyy-MM-dd HH:mm:ss`)} UTC`)}catch(t){r.error(`Failed to schedule reminder: ${t instanceof Error?t.message:String(t)}`),e.exit(1)}}async function Se(){let t=new Date,i=[];for(let e=0;e<7;e++){let n=l(t,e),r=u(n,`yyyy-MM-dd`),a=u(n,`EEEE`),o=r,s=`Create content for ${a}, ${r}`;e===0?(o=`Today - ${a} (${r})`,s=`Create content for today (${a})`):e===1?(o=`Tomorrow - ${a} (${r})`,s=`Create content for tomorrow (${a})`):(o=`In ${e} days - ${a} (${r})`,s=`Create content for ${a}, ${r}`),i.push({label:o,value:`day-${e}`,hint:s})}i.push({label:`Custom date`,value:`custom`,hint:`Enter a specific date`});let a=await o({message:`When do you want to create content?`,options:i});if(n(a)&&(r.error(`Operation cancelled.`),e.exit(0)),a!==`custom`)return l(t,Number.parseInt(a.split(`-`)[1]));let c=await s({message:`Enter date (YYYY-MM-DD):`,placeholder:u(t,`yyyy-MM-dd`),validate:e=>{if(!e)return`Date is required`;if(!/^\d{4}-\d{2}-\d{2}$/.test(e))return`Invalid date format. Please use YYYY-MM-DD`;try{let t=d(e,`yyyy-MM-dd`,new Date);if(Number.isNaN(t.getTime()))return`Invalid date`}catch{return`Invalid date`}}});return n(c)&&(r.error(`Operation cancelled.`),e.exit(0)),d(c,`yyyy-MM-dd`,new Date)}async function Z(){let t=await s({message:`What is the title of your content?`,placeholder:`Enter content title`,validate:e=>{if(!e||e.trim().length===0)return`Title is required`}});return n(t)&&(r.error(`Operation cancelled.`),e.exit(0)),t.trim()}async function Ce(){let t=await o({message:`Is a LinkedIn video planned?`,options:[{label:`Yes`,value:!0,hint:`Add video: true to frontmatter`},{label:`No`,value:!1,hint:`No video metadata`}]});return n(t)&&(r.error(`Operation cancelled.`),e.exit(0)),t}async function we(){let t=await o({message:`Are there images?`,options:[{label:`Yes`,value:!0,hint:`Add images section to frontmatter`},{label:`No`,value:!1,hint:`No images metadata`}]});return n(t)&&(r.error(`Operation cancelled.`),e.exit(0)),t}async function Q(t){if(t.length===0)return;let i=await o({message:`What is the content theme? (optional)`,options:[{label:`No theme`,value:``,hint:`Leave theme empty`},...t.map(e=>({label:e,value:e,hint:`Use theme: ${e}`}))]});n(i)&&(r.error(`Operation cancelled.`),e.exit(0));let a=i;return a.length>0?a:void 0}async function Te(){let t=await o({message:`What type of resource is this?`,options:[{label:`Article`,value:`article`,hint:`Create article.md`},{label:`Video`,value:`video`,hint:`Create video.md`},{label:`Audio`,value:`audio`,hint:`Create audio.md`},{label:`Tweet`,value:`tweet`,hint:`Create tweet.md`}]});return n(t)&&(r.error(`Operation cancelled.`),e.exit(0)),t}async function Ee(){let t=await i({message:`Which content types do you want to create?`,options:[{label:`LinkedIn`,value:`linkedin`,hint:`Create LinkedIn post and optional script`},{label:`X (Twitter)`,value:`x`,hint:`Create X post`},{label:`YouTube`,value:`youtube`,hint:`Create YouTube script and description`},{label:`Instagram`,value:`instagram`,hint:`Create Instagram script and description`}],required:!0});return n(t)&&(r.error(`Operation cancelled.`),e.exit(0)),t}async function De(t){let i=[];f(t)||(r.error(`Base path does not exist: ${t}`),e.exit(1));let a=h(t).filter(e=>g(y(t,e)).isDirectory()&&/^\d{4}$/.test(e));for(let e of a){let n=y(t,e),r=h(n).filter(e=>g(y(n,e)).isDirectory()&&/^\d{2}$/.test(e));for(let t of r){let r=y(n,t),a=h(r).filter(e=>g(y(r,e)).isDirectory()&&/^\d{2}$/.test(e));for(let n of a){let a=y(r,n);if(f(y(a,`x.md`))){let r=`${e}-${t}-${n}`;i.push({path:a,date:r,title:``})}}}}i.length===0&&(r.error(`No x.md files found in dated folders`),e.exit(1)),i.sort((e,t)=>t.date.localeCompare(e.date));let s=await o({message:`Which content do you want to schedule a reminder for?`,options:i.map(e=>({label:e.date,value:e.path,hint:`Schedule reminder for ${e.date}`}))});return n(s)&&(r.error(`Operation cancelled.`),e.exit(0)),s}const $=c(`content-creation`);$.command(`[path]`,`Create a dated content directory with content files`).option(`--path <path>`,`Base path for content directory (defaults to current directory)`).action(async(n,r)=>{t(`Content Creation - Create dated content directory`);let i=await k(),o=await Z(),s=await Ee(),c={title:o};s.includes(`linkedin`)&&(c.hasVideo=await Ce(),c.hasImages=await we(),i.thematic.length>0&&(c.theme=await Q(i.thematic)));let l=await Se();se(l,s,c,i,n||r?.path||e.cwd());let d=s.join(`, `);a(`✓ Content directory created: ${u(l,`yyyy/MM/dd`)} (${d})`)}),$.command(`link-images [path]`,`Link images to LinkedIn frontmatter in recent folders`).option(`--path <path>`,`Base path for content directory (defaults to current directory)`).action(async(n,r)=>{t(`Content Creation - Link Images to LinkedIn`),await le(n||r?.path||e.cwd()),a(`✓ Images linked successfully`)}),$.command(`resource [path]`,`Create a resource with article/video/audio markdown file`).option(`--path <path>`,`Base path for resource directory (defaults to current directory)`).action(async(n,r)=>{t(`Content Creation - Create Resource`);let i=await Z(),o=await Te();U(i,o,n||r?.path||e.cwd()),a(`✓ Resource created: resources/${H(i)}/${o}.md`)}),$.command(`create-index [path]`,`Create index files for each content type`).option(`--path <path>`,`Base path for content directory (defaults to current directory)`).action(async(n,r)=>{t(`Content Creation - Create Index Files`),V(n||r?.path||e.cwd()),a(`✓ Index files generated successfully`)}),$.command(`list-upcoming [path]`,`List content scheduled for today or later`).option(`--path <path>`,`Base path for content directory (defaults to current directory)`).action(async(n,r)=>{t(`Content Creation - List Upcoming Content`),ue(n||r?.path||e.cwd()),a(`✓ Upcoming content listed successfully`)}),$.command(`publish-linkedin-calendar [path]`,`Create and upload a LinkedIn publication calendar`).option(`--path <path>`,`Base path for content directory (defaults to current directory)`).action(async(n,r)=>{t(`Content Creation - Publish LinkedIn Calendar`);let i=await k();if(await be(n||r?.path||e.cwd(),i)){a(`✓ LinkedIn calendar published successfully`);return}a(`✓ No LinkedIn content found, calendar was not updated`)}),$.command(`schedule-reminder [path]`,`Schedule a reminder for X content`).option(`--path <path>`,`Base path for content directory (defaults to current directory)`).action(async(n,r)=>{t(`Content Creation - Schedule X Reminder`);let i=await k();await xe(await De(n||r?.path||e.cwd()),i),a(`✓ Reminder scheduled successfully`)}),$.help(),$.version(E),$.parse();export{};
package/dist/index.d.mts CHANGED
@@ -40,17 +40,13 @@ interface SchedulingConfig {
40
40
  */
41
41
  interface CalendarPublishingConfig {
42
42
  /**
43
- * Public calendar URL used to build the Google Calendar subscription link
43
+ * Public base URL used to build the Google Calendar subscription link
44
44
  */
45
45
  publicUrl?: string;
46
46
  /**
47
- * Token appended to the public calendar URL
47
+ * Token appended to the public calendar subscription URL
48
48
  */
49
49
  token?: string;
50
- /**
51
- * Optional output file path for the generated ICS file
52
- */
53
- outputFile?: string;
54
50
  }
55
51
  /**
56
52
  * Configuration for content-creation
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@barbapapazes/content-creation",
3
3
  "type": "module",
4
- "version": "0.17.0",
4
+ "version": "0.17.1",
5
5
  "author": "Estéban Soubiran <esteban@soubiran.dev>",
6
6
  "license": "MIT",
7
7
  "funding": "https://github.com/sponsors/Barbapapazes",