@shapio/cms 0.5.1 → 0.5.2
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/cli.js +58 -14
- package/dist/cli.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/{s3Adapter-BipoQiwQ.js → s3Adapter-CQ4iXRWp.js} +2 -2
- package/dist/{s3Adapter-BipoQiwQ.js.map → s3Adapter-CQ4iXRWp.js.map} +1 -1
- package/dist/{worker-BU-V4qbH.js → worker-W5eY5lSj.js} +4 -4
- package/dist/{worker-BU-V4qbH.js.map → worker-W5eY5lSj.js.map} +1 -1
- package/package.json +5 -5
package/dist/cli.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { $ as parseLockFile, A as buildEditorManifest, B as RESERVED_DEFINITION_API_KEYS, C as loopbackUrl, D as BEFORE_EVENTS, E as AFTER_EVENTS, F as lockById, G as THEME_VARIANTS, H as RESERVED_TYPE_NAMES, I as update, J as hashDefinition, K as checkThemeContrast, L as STORAGE_DRIVERS, N as listForAssets, O as EXTENSION_JOB_PREFIX, P as listLiveByDriver, Q as parseDefinition, R as describeError, S as createUrlBuilder, T as loadProjectConfig, U as SCHEMA_KIND_DIRECTORIES, V as RESERVED_FIELD_API_KEYS, W as SITES_DIRECTORY, X as lockEntriesForSite, Y as lockCoversSite, Z as mergePulledLock, _ as listAllSites, a as loadConfig, at as toTypeName, b as generateToken, ct as withSiteCovered, d as migrateToLatest, dt as createStableId, et as schemaFilePath, f as SHAPIO_VERSION, ft as validateRichText, g as createSite, i as assertDatabaseReachable, it as suggestPlural, k as EXTENSION_ROUTE_ROOT, lt as RICHTEXT_FORMAT, mt as sqliteDriverOf, n as startServer, nt as serializeDefinition, o as loadEnvFileIfPresent, ot as upgradeLockFile, p as createMediaStorage, pt as createDb, q as foldApiKey, r as copyObjectVerified, rt as serializeLockFile, st as validateSchema, t as startDedicatedWorker, tt as scopeDirectory, u as getPendingMigrations, ut as SAFE_LINK_PATTERN, v as createAdminDirect, w as SITE_KEY_PATTERN, x as createLogger, y as SYSTEM_CLI_ACTOR } from "./worker-
|
|
2
|
+
import { $ as parseLockFile, A as buildEditorManifest, B as RESERVED_DEFINITION_API_KEYS, C as loopbackUrl, D as BEFORE_EVENTS, E as AFTER_EVENTS, F as lockById, G as THEME_VARIANTS, H as RESERVED_TYPE_NAMES, I as update, J as hashDefinition, K as checkThemeContrast, L as STORAGE_DRIVERS, N as listForAssets, O as EXTENSION_JOB_PREFIX, P as listLiveByDriver, Q as parseDefinition, R as describeError, S as createUrlBuilder, T as loadProjectConfig, U as SCHEMA_KIND_DIRECTORIES, V as RESERVED_FIELD_API_KEYS, W as SITES_DIRECTORY, X as lockEntriesForSite, Y as lockCoversSite, Z as mergePulledLock, _ as listAllSites, a as loadConfig, at as toTypeName, b as generateToken, ct as withSiteCovered, d as migrateToLatest, dt as createStableId, et as schemaFilePath, f as SHAPIO_VERSION, ft as validateRichText, g as createSite, i as assertDatabaseReachable, it as suggestPlural, k as EXTENSION_ROUTE_ROOT, lt as RICHTEXT_FORMAT, mt as sqliteDriverOf, n as startServer, nt as serializeDefinition, o as loadEnvFileIfPresent, ot as upgradeLockFile, p as createMediaStorage, pt as createDb, q as foldApiKey, r as copyObjectVerified, rt as serializeLockFile, st as validateSchema, t as startDedicatedWorker, tt as scopeDirectory, u as getPendingMigrations, ut as SAFE_LINK_PATTERN, v as createAdminDirect, w as SITE_KEY_PATTERN, x as createLogger, y as SYSTEM_CLI_ACTOR } from "./worker-W5eY5lSj.js";
|
|
3
3
|
import { createDecipheriv, createHash, scryptSync } from "node:crypto";
|
|
4
4
|
import { createReadStream, createWriteStream, existsSync, openAsBlob } from "node:fs";
|
|
5
5
|
import { basename, dirname, extname, isAbsolute, join, posix, relative, resolve } from "node:path";
|
|
@@ -2290,12 +2290,18 @@ const planStrapiSchema = (schemas) => {
|
|
|
2290
2290
|
};
|
|
2291
2291
|
const taken = /* @__PURE__ */ new Set();
|
|
2292
2292
|
const plans = /* @__PURE__ */ new Map();
|
|
2293
|
+
/** The API ID for `name`; a reserved or repeated name gets a suffix, which the plan lists. */
|
|
2294
|
+
const claimApiKey = (schema, name) => {
|
|
2295
|
+
const apiKey = definitionApiKey(name, taken);
|
|
2296
|
+
if (apiKey !== toCamelCase(name)) context.notes.push(`${schema.uid} is named ${apiKey} (the name is reserved or already taken).`);
|
|
2297
|
+
return apiKey;
|
|
2298
|
+
};
|
|
2293
2299
|
for (const schema of contentTypes) {
|
|
2294
|
-
const
|
|
2295
|
-
plans.set(schema.uid, planDefinition(schema,
|
|
2300
|
+
const name = schema.info?.singularName ?? schema.uid.split(".").at(-1) ?? "model";
|
|
2301
|
+
plans.set(schema.uid, planDefinition(schema, claimApiKey(schema, name), context));
|
|
2296
2302
|
}
|
|
2297
2303
|
const baseName = componentBaseNames(components);
|
|
2298
|
-
for (const schema of components) plans.set(schema.uid, planDefinition(schema,
|
|
2304
|
+
for (const schema of components) plans.set(schema.uid, planDefinition(schema, claimApiKey(schema, baseName(schema)), context));
|
|
2299
2305
|
if (contentTypes.some((schema) => schema.options?.draftAndPublish === false)) context.notes.push(`Draft and publish is turned on for ${contentTypes.filter((schema) => schema.options?.draftAndPublish === false).map((schema) => plans.get(schema.uid)?.definition.apiKey).join(", ")} (off in Strapi), so imported entries are reviewed before they go live.`);
|
|
2300
2306
|
return {
|
|
2301
2307
|
plans,
|
|
@@ -2337,6 +2343,8 @@ const indexLinks = (links) => {
|
|
|
2337
2343
|
const stringOf = (value) => typeof value === "string" ? value : void 0;
|
|
2338
2344
|
/** Strapi IDs and numeric scalars as text (objects are not scalars and give undefined). */
|
|
2339
2345
|
const textOf = (value) => typeof value === "string" ? value : typeof value === "number" || typeof value === "bigint" ? String(value) : void 0;
|
|
2346
|
+
/** Strapi's `name` may lack the extension (its example project's names do); `ext` always has it. */
|
|
2347
|
+
const withExtension = (name, ext) => name.toLowerCase().endsWith(ext.toLowerCase()) ? name : `${name}${ext}`;
|
|
2340
2348
|
const fileMedia = (data, root) => {
|
|
2341
2349
|
const hash = stringOf(data.hash);
|
|
2342
2350
|
const ext = stringOf(data.ext) ?? "";
|
|
@@ -2345,9 +2353,10 @@ const fileMedia = (data, root) => {
|
|
|
2345
2353
|
const alt = stringOf(data.alternativeText);
|
|
2346
2354
|
const caption = stringOf(data.caption);
|
|
2347
2355
|
const mimeType = stringOf(data.mime);
|
|
2356
|
+
const name = stringOf(data.name);
|
|
2348
2357
|
return {
|
|
2349
2358
|
sourceId: `file:${id}`,
|
|
2350
|
-
filename:
|
|
2359
|
+
filename: name ? withExtension(name, ext) : `${hash}${ext}`,
|
|
2351
2360
|
path: join(root, "assets", "uploads", `${hash}${ext}`),
|
|
2352
2361
|
...mimeType ? { mimeType } : {},
|
|
2353
2362
|
...alt ? { alt } : {},
|
|
@@ -3649,13 +3658,39 @@ const runMap = async (options, io) => {
|
|
|
3649
3658
|
for (const failure of changeSets.failed) io.stderr(` ! change set item ${failure.key}: ${failure.message}\n`);
|
|
3650
3659
|
return printSummary(run, source, changeSets.failed.length);
|
|
3651
3660
|
};
|
|
3661
|
+
/**
|
|
3662
|
+
* The site a `--plan` writes its models for. `--plan` runs offline (no URL or token), so it cannot ask the
|
|
3663
|
+
* instance which site a token works on: it takes `--site`, else `SHAPIO_SITE`, else the primary site, whose key
|
|
3664
|
+
* is always `default`. On a single-site instance that is the only site, so the planned models apply with an
|
|
3665
|
+
* ordinary admin token. `--shared` plans models shared with all sites instead (applying them needs a network
|
|
3666
|
+
* admin token).
|
|
3667
|
+
*/
|
|
3668
|
+
const PRIMARY_SITE_KEY = "default";
|
|
3669
|
+
const resolvePlanSite = (options, env) => {
|
|
3670
|
+
const named = options.site?.trim() || void 0;
|
|
3671
|
+
if (options.shared) {
|
|
3672
|
+
if (named) throw new UsageError$1("Pass either --site <key> or --shared, not both");
|
|
3673
|
+
return {
|
|
3674
|
+
site: void 0,
|
|
3675
|
+
explicit: true
|
|
3676
|
+
};
|
|
3677
|
+
}
|
|
3678
|
+
const site = named ?? (env.SHAPIO_SITE?.trim() || void 0);
|
|
3679
|
+
return site ? {
|
|
3680
|
+
site,
|
|
3681
|
+
explicit: true
|
|
3682
|
+
} : {
|
|
3683
|
+
site: PRIMARY_SITE_KEY,
|
|
3684
|
+
explicit: false
|
|
3685
|
+
};
|
|
3686
|
+
};
|
|
3652
3687
|
/** Relative to the working directory when inside it, else absolute (what to paste into the next command). */
|
|
3653
3688
|
const display = (path) => {
|
|
3654
3689
|
const shown = relative(process.cwd(), path);
|
|
3655
3690
|
return !shown ? "." : shown.startsWith("..") || isAbsolute(shown) ? path : shown;
|
|
3656
3691
|
};
|
|
3657
3692
|
/** What `--plan` wrote and what to do next. */
|
|
3658
|
-
const formatPlanSummary = (source, definitions, dir, command, site) => {
|
|
3693
|
+
const formatPlanSummary = (source, definitions, dir, command, { site, explicit }) => {
|
|
3659
3694
|
const keyOf = new Map(source.definitions.map((definition) => [definition.apiKey, definition.key]));
|
|
3660
3695
|
const lines = [`Planned ${definitions.length} definition(s) in ${display(`${dir}/${PLAN_SCHEMA_DIR}`)}:`];
|
|
3661
3696
|
for (const definition of definitions) {
|
|
@@ -3666,8 +3701,8 @@ const formatPlanSummary = (source, definitions, dir, command, site) => {
|
|
|
3666
3701
|
lines.push(`Media: ${source.media.length} file(s) to import.`);
|
|
3667
3702
|
lines.push("Every model has draft and publish on: nothing goes live until you ship the change set(s) the import opens.");
|
|
3668
3703
|
lines.push(...source.notes);
|
|
3669
|
-
lines.push(site ? `The models and components will belong to site "${site}".` :
|
|
3670
|
-
const siteFlag = site ? ` --site ${site}` : "";
|
|
3704
|
+
lines.push(!site ? "The models and components will be shared with all sites (--shared): applying them needs a network admin token." : explicit ? `The models and components will belong to site "${site}".` : `The models and components will belong to the primary site ("${site}"). Plan with --site <key> for another site, or --shared to share them with all sites.`);
|
|
3705
|
+
const siteFlag = site && explicit ? ` --site ${site}` : "";
|
|
3671
3706
|
lines.push("Next:", " 1. Review the files: rename API IDs or delete fields you do not want (entries follow the stable IDs).", ` 2. shapio schema apply --dir ${display(`${dir}/${PLAN_SCHEMA_DIR}`)} --lock ${display(`${dir}/${PLAN_LOCK_FILE}`)}${siteFlag}`, ` 3. shapio import ${command} --map ${display(dir)}${siteFlag}`);
|
|
3672
3707
|
return `${lines.join("\n")}\n`;
|
|
3673
3708
|
};
|
|
@@ -3681,6 +3716,10 @@ const parse = (args, spec) => {
|
|
|
3681
3716
|
type: "boolean",
|
|
3682
3717
|
default: false
|
|
3683
3718
|
},
|
|
3719
|
+
shared: {
|
|
3720
|
+
type: "boolean",
|
|
3721
|
+
default: false
|
|
3722
|
+
},
|
|
3684
3723
|
url: { type: "string" },
|
|
3685
3724
|
token: { type: "string" },
|
|
3686
3725
|
site: { type: "string" },
|
|
@@ -3692,28 +3731,33 @@ const parse = (args, spec) => {
|
|
|
3692
3731
|
if (values.plan === void 0 === (values.map === void 0)) throw new UsageError$1("Pass either --plan <dir> (with the export file) or --map <dir>");
|
|
3693
3732
|
if (values.plan !== void 0 && !positionals[0]) throw new UsageError$1("--plan needs the export file");
|
|
3694
3733
|
if (values.map !== void 0 && positionals[0]) throw new UsageError$1("--map reads the export recorded in the plan: leave the file out");
|
|
3734
|
+
if (values.map !== void 0 && values.shared) throw new UsageError$1("--shared is a --plan option: --map imports where the plan put the models");
|
|
3735
|
+
if (values.shared && values.site !== void 0) throw new UsageError$1("Pass either --site <key> or --shared, not both");
|
|
3695
3736
|
return {
|
|
3696
3737
|
values,
|
|
3697
3738
|
file: positionals[0]
|
|
3698
3739
|
};
|
|
3699
3740
|
};
|
|
3700
|
-
/** `--site`, else SHAPIO_SITE: the site
|
|
3741
|
+
/** `--site`, else SHAPIO_SITE: the site `--map` writes to (else the site the plan recorded). */
|
|
3701
3742
|
const siteOf = (values, io) => (values.site ?? io.env.SHAPIO_SITE)?.trim() || void 0;
|
|
3702
3743
|
const runPlan = async (spec, file, values, io) => {
|
|
3703
3744
|
const dir = resolve(String(values.plan));
|
|
3704
3745
|
const path = resolve(file);
|
|
3705
3746
|
await checkPlanDirectory(dir, values.force === true);
|
|
3747
|
+
const planSite = resolvePlanSite({
|
|
3748
|
+
site: values.site,
|
|
3749
|
+
shared: values.shared === true
|
|
3750
|
+
}, io.env);
|
|
3706
3751
|
const source = await spec.plan(path, dir, values);
|
|
3707
|
-
const site = siteOf(values, io);
|
|
3708
3752
|
const { definitions } = await writePlan(dir, source, {
|
|
3709
3753
|
kind: source.kind,
|
|
3710
3754
|
path,
|
|
3711
3755
|
sha256: await sha256File(path)
|
|
3712
3756
|
}, {
|
|
3713
3757
|
force: values.force === true,
|
|
3714
|
-
site
|
|
3758
|
+
site: planSite.site
|
|
3715
3759
|
});
|
|
3716
|
-
io.stdout(formatPlanSummary(source, definitions, dir, spec.name,
|
|
3760
|
+
io.stdout(formatPlanSummary(source, definitions, dir, spec.name, planSite));
|
|
3717
3761
|
return 0;
|
|
3718
3762
|
};
|
|
3719
3763
|
const runMapStep = (spec, values, io) => {
|
|
@@ -3759,7 +3803,7 @@ const importStrapiCommand = createImporterCommand({
|
|
|
3759
3803
|
name: "strapi",
|
|
3760
3804
|
label: "Strapi",
|
|
3761
3805
|
summary: "Import a Strapi 5 export: --plan writes schema files, --map creates drafts and change sets",
|
|
3762
|
-
usage: "shapio import strapi <export.tar.gz[.enc]> --plan <dir> [--key <encryption key>] [--site <key>] [--force]\n shapio import strapi --map <dir> [--url <origin>] [--token <admin token>] [--site <key>]\n --plan unpacks the export into <dir>/source (--key for an encrypted export) and writes the planned models\n and components (<dir>/schema) and <dir>/import-map.json; it sends nothing.
|
|
3806
|
+
usage: "shapio import strapi <export.tar[.gz[.enc]]> --plan <dir> [--key <encryption key>] [--site <key> | --shared] [--force]\n shapio import strapi --map <dir> [--url <origin>] [--token <admin token>] [--site <key>]\n --plan unpacks the export into <dir>/source (--key for an encrypted export) and writes the planned models\n and components (<dir>/schema) and <dir>/import-map.json; it sends nothing. They belong to one site\n (<dir>/schema/sites/<key>/): --site (or SHAPIO_SITE), else the primary site (\"default\"); --shared shares\n them with all sites (applying them needs a network admin token). --map without --site imports into the\n planned site. Apply the schema with\n `shapio schema apply --dir <dir>/schema --lock <dir>/schema-lock.json [--site <key>]`, then run --map:\n it uploads the media, creates every document as a draft and opens change sets with the published ones.\n Strapi 5 only.",
|
|
3763
3807
|
options: ["key"],
|
|
3764
3808
|
plan: async (file, dir, values) => {
|
|
3765
3809
|
await extractStrapiExport(file, sourceDir(dir), typeof values.key === "string" ? values.key : void 0);
|
|
@@ -4353,7 +4397,7 @@ const importWordPressCommand = createImporterCommand({
|
|
|
4353
4397
|
name: "wordpress",
|
|
4354
4398
|
label: "WordPress",
|
|
4355
4399
|
summary: "Import a WordPress export (WXR): --plan writes schema files, --map creates drafts and change sets",
|
|
4356
|
-
usage: "shapio import wordpress <export.xml> --plan <dir> [--site <key>] [--force]\n shapio import wordpress --map <dir> [--url <origin>] [--token <admin token>] [--site <key>] [--media-dir <uploads dir>]\n --plan reads the export and writes the planned models (<dir>/schema) and <dir>/import-map.json; it sends nothing.\n
|
|
4400
|
+
usage: "shapio import wordpress <export.xml> --plan <dir> [--site <key> | --shared] [--force]\n shapio import wordpress --map <dir> [--url <origin>] [--token <admin token>] [--site <key>] [--media-dir <uploads dir>]\n --plan reads the export and writes the planned models (<dir>/schema) and <dir>/import-map.json; it sends nothing.\n The models belong to one site (<dir>/schema/sites/<key>/): --site (or SHAPIO_SITE), else the primary site\n (\"default\"); --shared shares them with all sites (applying them needs a network admin token).\n --map without --site imports into the planned site.\n Apply the models with `shapio schema apply --dir <dir>/schema --lock <dir>/schema-lock.json [--site <key>]`, then run --map:\n it uploads the media (downloaded from the site, or read from --media-dir, a copy of wp-content/uploads),\n creates every post and page as a draft, and opens change sets with the published ones. Re-run --map to resume.",
|
|
4357
4401
|
options: ["media-dir"],
|
|
4358
4402
|
plan: async (file) => wordpressSource(await readWxr(file)),
|
|
4359
4403
|
load: async (map, _dir, values) => wordpressSource(await readWxr(map.source.path), mediaDirOf(values))
|