@crombolo/cli 0.14.4 → 0.15.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/bin/crombolo.ts +4 -3166
- package/oclif.manifest.json +6710 -0
- package/package.json +58 -7
- package/src/base.ts +129 -0
- package/src/classify.ts +119 -0
- package/src/clispec.ts +163 -0
- package/src/commands/admin/reset-password.ts +53 -0
- package/src/commands/assets/index.ts +6 -0
- package/src/commands/assets/list.ts +27 -0
- package/src/commands/assets/new.ts +42 -0
- package/src/commands/bundle/purge.ts +71 -0
- package/src/commands/components/check.ts +58 -0
- package/src/commands/components/index.ts +6 -0
- package/src/commands/components/list.ts +40 -0
- package/src/commands/components/new.ts +49 -0
- package/src/commands/content/check.ts +102 -0
- package/src/commands/content/export.ts +116 -0
- package/src/commands/content/import.ts +109 -0
- package/src/commands/content/publish.ts +21 -0
- package/src/commands/content/revert.ts +137 -0
- package/src/commands/content/runs.ts +62 -0
- package/src/commands/content/unpublish.ts +21 -0
- package/src/commands/dictionary/export.ts +49 -0
- package/src/commands/dictionary/import.ts +46 -0
- package/src/commands/domains/apply.ts +23 -0
- package/src/commands/domains/clear.ts +27 -0
- package/src/commands/domains/index.ts +6 -0
- package/src/commands/domains/list.ts +86 -0
- package/src/commands/domains/set.ts +55 -0
- package/src/commands/domains/undo.ts +29 -0
- package/src/commands/export.ts +100 -0
- package/src/commands/generate.ts +33 -0
- package/src/commands/import/index.ts +175 -0
- package/src/commands/import/umbraco/apply.ts +128 -0
- package/src/commands/import/umbraco/report.ts +56 -0
- package/src/commands/init.ts +76 -0
- package/src/commands/login.ts +38 -0
- package/src/commands/logout.ts +36 -0
- package/src/commands/maintenance/pause.ts +29 -0
- package/src/commands/maintenance/resume.ts +20 -0
- package/src/commands/maintenance/status.ts +34 -0
- package/src/commands/media/fetch.ts +62 -0
- package/src/commands/pull.ts +210 -0
- package/src/commands/push.ts +108 -0
- package/src/commands/remote/add.ts +78 -0
- package/src/commands/remote/job.ts +53 -0
- package/src/commands/remote/list.ts +41 -0
- package/src/commands/remote/remove.ts +26 -0
- package/src/commands/schema.ts +32 -0
- package/src/commands/start.ts +64 -0
- package/src/commands/status.ts +103 -0
- package/src/commands/types/add-property.ts +116 -0
- package/src/commands/types/check.ts +88 -0
- package/src/commands/types/export.ts +32 -0
- package/src/commands/types/new.ts +116 -0
- package/src/commands/types/purge.ts +65 -0
- package/src/commands/types/rewrite.ts +30 -0
- package/src/commands/types/sync.ts +66 -0
- package/src/commands/upgrade/check.ts +69 -0
- package/src/commands/upgrade/index.ts +120 -0
- package/src/commands/upgrade/ledger.ts +55 -0
- package/src/commands/whoami.ts +41 -0
- package/src/errors.ts +90 -0
- package/src/main.ts +23 -0
- package/src/manifest.ts +20 -0
- package/src/reference.ts +118 -0
- package/src/remote/client.ts +19 -10
- package/src/remote/remotes-file.ts +3 -2
- package/src/remote/transfer.ts +27 -11
- package/src/support/components.ts +7 -0
- package/src/support/describe.ts +179 -0
- package/src/support/domains.ts +89 -0
- package/src/support/flags.ts +157 -0
- package/src/support/prompt.ts +126 -0
- package/src/support/publish.ts +142 -0
- package/src/support/remote.ts +58 -0
- package/src/support/serve.ts +460 -0
- package/src/support/site.ts +26 -0
- package/src/support/types.ts +112 -0
- package/src/support/umbraco.ts +86 -0
- package/templates/basic/bundle/content/bundle.json +1 -1
- package/templates/basic/bundle/content/nodes/36955f6e-d110-469e-b280-72096eccd148.json +1 -1
- package/templates/basic/bundle/package.json +1 -1
- package/templates/demo/harbourstone/bundle/package.json +1 -1
- package/templates/starter/bundle/package.json +1 -1
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/** What the `types` commands share: loading `schema/`, applying it, and the upgrade check. */
|
|
2
|
+
import { existsSync } from 'node:fs'
|
|
3
|
+
import {
|
|
4
|
+
allProperties,
|
|
5
|
+
loadSchemaDirectory,
|
|
6
|
+
type SyncReport,
|
|
7
|
+
type ValueMigration,
|
|
8
|
+
} from '@crombolo/schema'
|
|
9
|
+
import {
|
|
10
|
+
bootstrapDatabase,
|
|
11
|
+
type CromboloConfig,
|
|
12
|
+
formTypesFor,
|
|
13
|
+
syncSchemaDirectory,
|
|
14
|
+
} from '@crombolo/server'
|
|
15
|
+
import { CliError } from '../errors.ts'
|
|
16
|
+
import * as ops from '../operations/commands.ts'
|
|
17
|
+
import { describeProblem, describeSync, here, printFindings, printProblems } from './describe.ts'
|
|
18
|
+
import type { Io } from './site.ts'
|
|
19
|
+
|
|
20
|
+
export const loadTypes = (config: CromboloConfig) =>
|
|
21
|
+
loadSchemaDirectory(config.schemaDir, { formTypes: formTypesFor(config) })
|
|
22
|
+
|
|
23
|
+
export function needSchemaDir(config: CromboloConfig): void {
|
|
24
|
+
if (!existsSync(config.schemaDir))
|
|
25
|
+
throw new CliError('not_found', `No schema directory at ${here(config.schemaDir)}.`)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function refuseProblems(problems: SyncReport['problems'], message?: string): void {
|
|
29
|
+
if (problems.length > 0)
|
|
30
|
+
throw new CliError('refused', message ?? `${problems.length} problem(s) in schema/.`, {
|
|
31
|
+
details: problems.map(describeProblem),
|
|
32
|
+
})
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** Applies `schema/` as a boot or a deploy step would, and reports what it did. */
|
|
36
|
+
export async function applySchema(io: Io, config: CromboloConfig): Promise<SyncReport> {
|
|
37
|
+
const { db } = await bootstrapDatabase(config)
|
|
38
|
+
try {
|
|
39
|
+
const report = await syncSchemaDirectory(db, config, {
|
|
40
|
+
nodeId: config.nodeId,
|
|
41
|
+
revision: config.schemaRevision,
|
|
42
|
+
})
|
|
43
|
+
if (report.problems.length > 0) printProblems(io, report.problems)
|
|
44
|
+
io.say(` schema ${describeSync(report)}`)
|
|
45
|
+
if (report.action === 'refused')
|
|
46
|
+
throw new CliError('refused', `The schema was not applied: ${describeSync(report)}.`, {
|
|
47
|
+
details: report.problems.map(describeProblem),
|
|
48
|
+
})
|
|
49
|
+
return report
|
|
50
|
+
} finally {
|
|
51
|
+
await db.close()
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** The pull-request rules: keys present, migrations well-formed, nothing the database needs. */
|
|
56
|
+
export function staticProblems(
|
|
57
|
+
loaded: ReturnType<typeof loadSchemaDirectory>,
|
|
58
|
+
migrations: ValueMigration[],
|
|
59
|
+
): string[] {
|
|
60
|
+
const problems: string[] = []
|
|
61
|
+
for (const t of loaded.set.contentTypes) {
|
|
62
|
+
if (!t.key)
|
|
63
|
+
problems.push(`${t.alias}: no key — run \`crombolo types sync\` in development to assign one`)
|
|
64
|
+
for (const p of allProperties(t)) if (!p.key) problems.push(`${t.alias}.${p.alias}: no key`)
|
|
65
|
+
}
|
|
66
|
+
for (const d of loaded.set.dataTypes) if (!d.key) problems.push(`data type ${d.alias}: no key`)
|
|
67
|
+
for (const m of migrations) {
|
|
68
|
+
const type = loaded.set.contentTypes.find((t) => t.alias === m.from.type)
|
|
69
|
+
if (!type) {
|
|
70
|
+
problems.push(`migration ${m.id}: no content type "${m.from.type}"`)
|
|
71
|
+
continue
|
|
72
|
+
}
|
|
73
|
+
for (const target of m.to)
|
|
74
|
+
if (!allProperties(type).some((p) => p.alias === target.property))
|
|
75
|
+
problems.push(
|
|
76
|
+
`migration ${m.id}: "${m.from.type}" has no property "${target.property}" to write to`,
|
|
77
|
+
)
|
|
78
|
+
}
|
|
79
|
+
return problems
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** `--set type.property=value`, repeatable: a value for a new mandatory property. */
|
|
83
|
+
export function parseSet(pairs: readonly string[]): Record<string, unknown> {
|
|
84
|
+
const out: Record<string, unknown> = {}
|
|
85
|
+
for (const pair of pairs) {
|
|
86
|
+
const eq = pair.indexOf('=')
|
|
87
|
+
if (eq < 0) throw new CliError('usage', `--set wants <type>.<property>=<value>, not "${pair}".`)
|
|
88
|
+
const raw = pair.slice(eq + 1)
|
|
89
|
+
let value: unknown = raw
|
|
90
|
+
try {
|
|
91
|
+
value = JSON.parse(raw)
|
|
92
|
+
} catch {}
|
|
93
|
+
out[pair.slice(0, eq)] = value
|
|
94
|
+
}
|
|
95
|
+
return out
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/** What the deployed `schema/` would do to this database's data: the check an upgrade runs. */
|
|
99
|
+
export async function upgradeCheck(io: Io, config: CromboloConfig, set: Record<string, unknown>) {
|
|
100
|
+
const report = await ops.upgradeCheck(config, { set })
|
|
101
|
+
for (const bundle of report.bundles)
|
|
102
|
+
if (bundle.pending.length > 0)
|
|
103
|
+
io.say(
|
|
104
|
+
` bundle ${bundle.id}: ${bundle.pending.map((step) => `${step.name} (${step.kind})`).join(', ')}`,
|
|
105
|
+
)
|
|
106
|
+
printFindings(io, report)
|
|
107
|
+
if (!report.siteChecked)
|
|
108
|
+
io.say(
|
|
109
|
+
'The site schema is checked once the framework migrations have run (`crombolo upgrade`).',
|
|
110
|
+
)
|
|
111
|
+
return report
|
|
112
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `crombolo import umbraco`: a compatibility report for an Umbraco backup, and
|
|
3
|
+
* then a crombolo site written from it. Neither touches a database.
|
|
4
|
+
*
|
|
5
|
+
* The importer is a package of its own, loaded here on demand, so a site that
|
|
6
|
+
* never imports anything does not carry it or the `.bacpac` reader it needs.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { VERSION } from '@crombolo/server'
|
|
10
|
+
import { Args, Flags } from '@oclif/core'
|
|
11
|
+
import { CliError } from '../errors.ts'
|
|
12
|
+
import { contentFlags } from './flags.ts'
|
|
13
|
+
import type { Io } from './site.ts'
|
|
14
|
+
|
|
15
|
+
export const umbracoArgs = {
|
|
16
|
+
backup: Args.string({ required: true, description: "a .bacpac, or Umbraco's SQLite file" }),
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export const umbracoFlags = {
|
|
20
|
+
site: Flags.string({
|
|
21
|
+
summary: "the Umbraco site's files, for its views, media and plugins",
|
|
22
|
+
helpValue: '<dir>',
|
|
23
|
+
}),
|
|
24
|
+
media: Flags.string({
|
|
25
|
+
summary: 'where its media files are, if not under --site',
|
|
26
|
+
helpValue: '<dir>',
|
|
27
|
+
}),
|
|
28
|
+
name: Flags.string({ summary: "the new site's name", helpValue: '<site name>' }),
|
|
29
|
+
drafts: Flags.boolean({ summary: 'bring drafts too' }),
|
|
30
|
+
staging: Flags.string({ summary: 'where to unpack a .bacpac', helpValue: '<dir>' }),
|
|
31
|
+
'with-members': Flags.boolean({ summary: 'bring members, encrypted' }),
|
|
32
|
+
'with-users': Flags.boolean({ summary: 'bring back-office users, encrypted' }),
|
|
33
|
+
...contentFlags,
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function deprecated(io: Io, json: boolean): void {
|
|
37
|
+
// Not into JSON output, which a machine reads.
|
|
38
|
+
if (!json)
|
|
39
|
+
io.warning(
|
|
40
|
+
' deprecated `crombolo import umbraco` is going away: an Umbraco extension will write a crombolo file that `crombolo import <file>` reads (story 074)',
|
|
41
|
+
)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export async function loadImporter(): Promise<typeof import('@crombolo/import-umbraco')> {
|
|
45
|
+
try {
|
|
46
|
+
return await import('@crombolo/import-umbraco')
|
|
47
|
+
} catch {
|
|
48
|
+
throw new CliError('not_found', 'The importer is a separate package.', {
|
|
49
|
+
hint: 'Add it, then run this again: bun add -d @crombolo/import-umbraco',
|
|
50
|
+
})
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export async function planUmbraco(
|
|
55
|
+
importer: typeof import('@crombolo/import-umbraco'),
|
|
56
|
+
backup: string,
|
|
57
|
+
flags: {
|
|
58
|
+
site?: string
|
|
59
|
+
media?: string
|
|
60
|
+
name?: string
|
|
61
|
+
drafts: boolean
|
|
62
|
+
staging?: string
|
|
63
|
+
'with-members': boolean
|
|
64
|
+
'with-users': boolean
|
|
65
|
+
},
|
|
66
|
+
content: 'omitted' | 'sealed' | 'open',
|
|
67
|
+
passphrase: string | undefined,
|
|
68
|
+
) {
|
|
69
|
+
try {
|
|
70
|
+
return await importer.planImport({
|
|
71
|
+
source: backup,
|
|
72
|
+
site: flags.site,
|
|
73
|
+
media: flags.media,
|
|
74
|
+
siteName: flags.name,
|
|
75
|
+
drafts: flags.drafts,
|
|
76
|
+
staging: flags.staging,
|
|
77
|
+
coreVersion: VERSION,
|
|
78
|
+
content,
|
|
79
|
+
withMembers: flags['with-members'],
|
|
80
|
+
withUsers: flags['with-users'],
|
|
81
|
+
passphrase,
|
|
82
|
+
})
|
|
83
|
+
} catch (error) {
|
|
84
|
+
throw new CliError('refused', error instanceof Error ? error.message : String(error))
|
|
85
|
+
}
|
|
86
|
+
}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"id": "2de473a2-d391-4161-801e-e34b202759b8",
|
|
4
4
|
"createdAt": "2026-10-01T00:00:00.000Z",
|
|
5
5
|
"createdBy": "bun run build:template",
|
|
6
|
-
"integrity": "sha256-
|
|
6
|
+
"integrity": "sha256-fa7b7651e0726518570df713624c1ab29af47f7f04a615b514e29d47de301b97",
|
|
7
7
|
"snapshot": "published",
|
|
8
8
|
"provenance": {
|
|
9
9
|
"siteName": "Basic",
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"segment": null,
|
|
24
24
|
"editor": "Umbraco.RichText",
|
|
25
25
|
"value": {
|
|
26
|
-
"markup": "<p>This page is content: it lives in the database and you edit it in the backoffice. Its <em>shape</em> is code — <code>schema/content-types/home-page.toml</code> — and the markup around it is <code>components/homePage.tsx</code>.</p><p>Add a property to the TOML, run <code>crombolo
|
|
26
|
+
"markup": "<p>This page is content: it lives in the database and you edit it in the backoffice. Its <em>shape</em> is code — <code>schema/content-types/home-page.toml</code> — and the markup around it is <code>components/homePage.tsx</code>.</p><p>Add a property to the TOML, run <code>crombolo types check</code> to see what it would do to this page, then <code>crombolo start</code> to apply it.</p>",
|
|
27
27
|
"blocks": null
|
|
28
28
|
}
|
|
29
29
|
},
|