create-ortha-app 0.4.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/LICENSE +21 -0
- package/README.md +7 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +277 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +32 -0
- package/dist/lib/conditionals.d.ts +37 -0
- package/dist/lib/conditionals.d.ts.map +1 -0
- package/dist/lib/conditionals.js +115 -0
- package/dist/lib/features.d.ts +185 -0
- package/dist/lib/features.d.ts.map +1 -0
- package/dist/lib/features.js +328 -0
- package/dist/lib/template.d.ts +46 -0
- package/dist/lib/template.d.ts.map +1 -0
- package/dist/lib/template.js +115 -0
- package/dist/lib/ui.d.ts +76 -0
- package/dist/lib/ui.d.ts.map +1 -0
- package/dist/lib/ui.js +310 -0
- package/dist/lib/validate.d.ts +13 -0
- package/dist/lib/validate.d.ts.map +1 -0
- package/dist/lib/validate.js +51 -0
- package/package.json +37 -0
- package/templates/default/README.md.tmpl +199 -0
- package/templates/default/_gitignore +8 -0
- package/templates/default/apps/admin/index.html +38 -0
- package/templates/default/apps/admin/src/main.tsx +5 -0
- package/templates/default/apps/admin/src/plugins.spec.ts +58 -0
- package/templates/default/apps/admin/src/plugins.ts +57 -0
- package/templates/default/apps/admin/src/styles.css +254 -0
- package/templates/default/apps/admin/tsconfig.json +29 -0
- package/templates/default/apps/admin/vite.config.mts +67 -0
- package/templates/default/apps/admin-e2e/playwright.config.ts +51 -0
- package/templates/default/apps/admin-e2e/src/auth.spec.ts +55 -0
- package/templates/default/apps/admin-e2e/src/support/seed.ts +62 -0
- package/templates/default/apps/admin-e2e/tsconfig.json +23 -0
- package/templates/default/apps/server/jest.config.js +38 -0
- package/templates/default/apps/server/jest.setup.js +20 -0
- package/templates/default/apps/server/ortha.config.ts +505 -0
- package/templates/default/apps/server/src/main.ts +26 -0
- package/templates/default/apps/server/src/plugins.spec.ts +71 -0
- package/templates/default/apps/server/src/plugins.ts +229 -0
- package/templates/default/apps/server/tsconfig.json +31 -0
- package/templates/default/apps/server-e2e/jest.config.js +47 -0
- package/templates/default/apps/server-e2e/src/api.spec.ts +107 -0
- package/templates/default/apps/server-e2e/src/global-setup.ts +41 -0
- package/templates/default/apps/server-e2e/src/jest.setup.ts +28 -0
- package/templates/default/apps/server-e2e/src/support/db.ts +143 -0
- package/templates/default/apps/server-e2e/src/support/test-app.ts +64 -0
- package/templates/default/apps/server-e2e/tsconfig.json +26 -0
- package/templates/default/docker-compose.yml +21 -0
- package/templates/default/env.tmpl +140 -0
- package/templates/default/package.json.tmpl +51 -0
- package/templates/default/tsconfig.json +18 -0
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a generated app is made of.
|
|
3
|
+
*
|
|
4
|
+
* **This file is the contract between the release and the scaffolder.** Every
|
|
5
|
+
* published `@orthacms/*` package is accounted for here exactly once — as core,
|
|
6
|
+
* as part of an optional feature, or as deliberately transitive — and
|
|
7
|
+
* `features.spec.ts` fails the build when one is not. That guard is the point:
|
|
8
|
+
* adding a package to the workspace should force a decision about whether a new
|
|
9
|
+
* app gets it, rather than the template quietly falling a release behind.
|
|
10
|
+
*
|
|
11
|
+
* Versions are *not* listed. Every `@orthacms/*` dependency is pinned to the
|
|
12
|
+
* scaffolder's own version at render time (`__ORTHA_VERSION__`), so a release
|
|
13
|
+
* bumps the whole set with no edit here.
|
|
14
|
+
*/
|
|
15
|
+
/** An optional capability a new app can opt into. */
|
|
16
|
+
export interface Feature {
|
|
17
|
+
/** Stable id — used by `ortha:if` blocks in the templates and by flags. */
|
|
18
|
+
id: string;
|
|
19
|
+
/** What the picker shows. */
|
|
20
|
+
label: string;
|
|
21
|
+
/** One line under the label. */
|
|
22
|
+
hint: string;
|
|
23
|
+
/** `@orthacms/*` packages added to the app when this is enabled. */
|
|
24
|
+
packages: readonly string[];
|
|
25
|
+
/** Whether it starts ticked. */
|
|
26
|
+
enabledByDefault: boolean;
|
|
27
|
+
/**
|
|
28
|
+
* `false` for a capability that exists in the codebase but has no published,
|
|
29
|
+
* working adapter yet. Shown greyed out and unselectable rather than hidden,
|
|
30
|
+
* so the picker tells the truth about what is coming.
|
|
31
|
+
*/
|
|
32
|
+
available: boolean;
|
|
33
|
+
/**
|
|
34
|
+
* Always on, and shown ticked but unselectable. For REST, which is not a
|
|
35
|
+
* choice — it is what the other protocols adapt.
|
|
36
|
+
*/
|
|
37
|
+
locked?: boolean;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* The packages every app gets, whatever it opts into.
|
|
41
|
+
*
|
|
42
|
+
* The **copilot** is here — plugin, admin panel and the offline `fake` adapter —
|
|
43
|
+
* even though it is a large feature nobody may want. Two reasons. Its server
|
|
44
|
+
* half arrives anyway: five core plugins (`content`, `activity`, `i18n`,
|
|
45
|
+
* `media`, `users`) depend on `copilot-server` to contribute their tools, so
|
|
46
|
+
* the code is on disk whatever the manifest says, and leaving it undeclared
|
|
47
|
+
* bought nothing but a missing chat panel. And `fake` needs no key and no
|
|
48
|
+
* network, so a default app gets a copilot that genuinely works offline —
|
|
49
|
+
* while `COPILOT_ENABLED` stays `false`, so nothing reaches a model until an
|
|
50
|
+
* operator says so.
|
|
51
|
+
*
|
|
52
|
+
* The **extension points** are here for the same reason — `content-domain`,
|
|
53
|
+
* `copilot-domain`, `tools-server`, `query-builder-admin`. Every one of them
|
|
54
|
+
* already arrives transitively, so an import would resolve on npm's flat
|
|
55
|
+
* `node_modules` today; declaring them is what makes that resolution something
|
|
56
|
+
* the app owns rather than something it borrows. An undeclared import breaks
|
|
57
|
+
* the moment a version conflict nests a copy, and never resolves under pnpm at
|
|
58
|
+
* all.
|
|
59
|
+
*
|
|
60
|
+
* **SSO** contributes two entries on the same reasoning as the copilot's.
|
|
61
|
+
* `identity-domain` is an extension point — the `SsoProvider` port an operator
|
|
62
|
+
* implements to reach an identity provider we do not ship an adapter for — and
|
|
63
|
+
* it already arrives transitively through `identity-server`, so declaring it is
|
|
64
|
+
* what makes that resolution something the app owns rather than borrows.
|
|
65
|
+
* `identity-provider-fake` is the scripted identity provider: it needs no
|
|
66
|
+
* tenant and no network, so it is how a generated app's sign-in page can be
|
|
67
|
+
* exercised offline, exactly as `copilot-provider-fake` is for the chat. Note
|
|
68
|
+
* that shipping it installs nothing: an adapter only does something once the
|
|
69
|
+
* composition root registers it, and the template registers none.
|
|
70
|
+
*
|
|
71
|
+
* `design-system`, `utils-admin` and `utils-server` are here even though the
|
|
72
|
+
* template's own files barely touch them: they are the first things anyone
|
|
73
|
+
* reaches for when writing a page or a plugin of their own, and relying on
|
|
74
|
+
* npm's hoisting to make an undeclared import work is a phantom dependency —
|
|
75
|
+
* it resolves until a version conflict nests a copy, and never resolves under
|
|
76
|
+
* pnpm at all.
|
|
77
|
+
*/
|
|
78
|
+
export declare const CORE_PACKAGES: readonly string[];
|
|
79
|
+
/** Packages the app needs to build and run itself, as devDependencies. */
|
|
80
|
+
export declare const CORE_DEV_PACKAGES: readonly string[];
|
|
81
|
+
/**
|
|
82
|
+
* Packages deliberately left undeclared — published, but with no reason for a
|
|
83
|
+
* generated app to import them.
|
|
84
|
+
*
|
|
85
|
+
* Both entries are tools for **writing a storage provider**, not for running
|
|
86
|
+
* one. `StorageProviderCheck` refuses to boot a database whose rows were
|
|
87
|
+
* written by a provider that is no longer configured, so the in-memory backend
|
|
88
|
+
* is a test and offline-development affordance, never a deployment: offering it
|
|
89
|
+
* in the scaffolder would be offering an app that loses every upload on
|
|
90
|
+
* restart. The testkit is the contract suite those providers run against.
|
|
91
|
+
*
|
|
92
|
+
* Everything else a generated app can reach is in its own manifest, so "it
|
|
93
|
+
* resolves because npm hoisted it" is never the answer to why an import works.
|
|
94
|
+
* Putting a package here is a decision the coverage guard accepts; forgetting
|
|
95
|
+
* it entirely is not.
|
|
96
|
+
*/
|
|
97
|
+
export declare const TRANSITIVE_PACKAGES: readonly string[];
|
|
98
|
+
/**
|
|
99
|
+
* Where uploads are written.
|
|
100
|
+
*
|
|
101
|
+
* A single-choice group: media always runs, the question is only which adapter
|
|
102
|
+
* backs it. S3 is listed and disabled — the package exists but has never been
|
|
103
|
+
* released, and offering it would generate an app that cannot install.
|
|
104
|
+
*/
|
|
105
|
+
export declare const MEDIA_PROVIDERS: readonly Feature[];
|
|
106
|
+
/**
|
|
107
|
+
* Model backends for the AI copilot.
|
|
108
|
+
*
|
|
109
|
+
* A multi-choice group, and picking none is the meaningful default: enabling a
|
|
110
|
+
* hosted provider sends workspace content to a third party, which
|
|
111
|
+
* [ADR-0005](https://github.com/ortha-source/ortha-cms/blob/main/docs/adr/0005-copilot-authority-model.md)
|
|
112
|
+
* §10 says is an operator's decision to make explicitly. Pick nothing and the
|
|
113
|
+
* copilot is not registered at all.
|
|
114
|
+
*
|
|
115
|
+
* `copilot-provider-fake` is not offered here — it is installed automatically
|
|
116
|
+
* whenever the copilot is on. It is a shipped adapter rather than test
|
|
117
|
+
* scaffolding (ADR-0004 §3): it needs no key and no network, so it is what
|
|
118
|
+
* makes the chat work offline, and it is registered last so it is the default
|
|
119
|
+
* only when it is the only one.
|
|
120
|
+
*/
|
|
121
|
+
export declare const COPILOT_PROVIDERS: readonly Feature[];
|
|
122
|
+
/**
|
|
123
|
+
* How people sign in to the admin.
|
|
124
|
+
*
|
|
125
|
+
* A single opt-in, and off by default, because SSO is not something a CMS can
|
|
126
|
+
* usefully guess at: it needs an issuer, a client and a callback URL registered
|
|
127
|
+
* on the other side, none of which a scaffolder can invent. A generated app
|
|
128
|
+
* without it signs in with email and password, which is the invite-only flow
|
|
129
|
+
* Ortha has always had.
|
|
130
|
+
*
|
|
131
|
+
* **One entry covers most of the field.** Okta, Auth0, Keycloak, Google, Entra
|
|
132
|
+
* ID, Authentik, Zitadel, JumpCloud, Ping and GitLab all speak OpenID Connect,
|
|
133
|
+
* and the named vendors are preset factories inside that one package rather
|
|
134
|
+
* than packages of their own — the SSO equivalent of the copilot's
|
|
135
|
+
* OpenAI-compatible adapter.
|
|
136
|
+
*
|
|
137
|
+
* The other two are here because their **wire** genuinely differs, which is the
|
|
138
|
+
* only thing that earns a package: GitHub is OAuth2 with no identity token, and
|
|
139
|
+
* SAML is a POST binding with XML signatures. Each also brings its own
|
|
140
|
+
* dependency — `jose` for OIDC, `@node-saml/node-saml` for SAML — which is a
|
|
141
|
+
* second reason not to install them for an app that will never speak them.
|
|
142
|
+
*
|
|
143
|
+
* `identity-provider-fake` is not offered: it is installed unconditionally,
|
|
144
|
+
* like `copilot-provider-fake`, because it needs no tenant and no network and
|
|
145
|
+
* is how a generated app's sign-in page is exercised offline. Installing it
|
|
146
|
+
* registers nothing — an adapter only does something once the composition root
|
|
147
|
+
* names it, and the template names none.
|
|
148
|
+
*/
|
|
149
|
+
export declare const SSO_PROVIDERS: readonly Feature[];
|
|
150
|
+
/**
|
|
151
|
+
* How the content API is spoken.
|
|
152
|
+
*
|
|
153
|
+
* REST is always there and is shown `locked` rather than hidden, because "which
|
|
154
|
+
* protocols does this app serve" is a more useful question than "do you want
|
|
155
|
+
* these two extras" — the answer should read as a set, with the one you always
|
|
156
|
+
* get visible in it.
|
|
157
|
+
*
|
|
158
|
+
* Neither addition brings a credential or a permission of its own: GraphQL is
|
|
159
|
+
* an adapter over the REST API's own services
|
|
160
|
+
* ([ADR-0008](https://github.com/ortha-source/ortha-cms/blob/main/docs/adr/0008-graphql-as-a-protocol-adapter.md)),
|
|
161
|
+
* and MCP reuses the same API tokens and scopes. They are opt-in because an
|
|
162
|
+
* endpoint nobody asked for is still an endpoint.
|
|
163
|
+
*/
|
|
164
|
+
export declare const PROTOCOLS: readonly Feature[];
|
|
165
|
+
/** Every optional feature, in the order the wizard asks about them. */
|
|
166
|
+
export declare const ALL_FEATURES: readonly Feature[];
|
|
167
|
+
/** The answers a scaffold run resolves to. */
|
|
168
|
+
export interface FeatureSelection {
|
|
169
|
+
/** Ids of every enabled feature — what `ortha:if` blocks are tested against. */
|
|
170
|
+
enabled: ReadonlySet<string>;
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* The `@orthacms/*` dependencies for a selection, sorted.
|
|
174
|
+
*
|
|
175
|
+
* Built here rather than with `ortha:if` blocks inside `package.json.tmpl`:
|
|
176
|
+
* removing lines from JSON is how you get a trailing comma and an app that
|
|
177
|
+
* cannot even be installed, and the failure would name the template rather than
|
|
178
|
+
* the feature that was switched off.
|
|
179
|
+
*/
|
|
180
|
+
export declare function resolvePackages(selection: FeatureSelection): string[];
|
|
181
|
+
/** The dev-time `@orthacms/*` dependencies, sorted. */
|
|
182
|
+
export declare function resolveDevPackages(): string[];
|
|
183
|
+
/** The feature ids in force for a selection — what `ortha:if` tests against. */
|
|
184
|
+
export declare function resolveFlags(selection: FeatureSelection): Set<string>;
|
|
185
|
+
//# sourceMappingURL=features.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"features.d.ts","sourceRoot":"","sources":["../../src/lib/features.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,qDAAqD;AACrD,MAAM,WAAW,OAAO;IACpB,2EAA2E;IAC3E,EAAE,EAAE,MAAM,CAAC;IACX,6BAA6B;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,gCAAgC;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,oEAAoE;IACpE,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;IAC5B,gCAAgC;IAChC,gBAAgB,EAAE,OAAO,CAAC;IAC1B;;;;OAIG;IACH,SAAS,EAAE,OAAO,CAAC;IACnB;;;OAGG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;CACpB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,eAAO,MAAM,aAAa,EAAE,SAAS,MAAM,EAqC1C,CAAC;AAEF,0EAA0E;AAC1E,eAAO,MAAM,iBAAiB,EAAE,SAAS,MAAM,EAAsB,CAAC;AAEtE;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,mBAAmB,EAAE,SAAS,MAAM,EAGhD,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,eAAe,EAAE,SAAS,OAAO,EAyC7C,CAAC;AAEF;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,iBAAiB,EAAE,SAAS,OAAO,EAiB/C,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,eAAO,MAAM,aAAa,EAAE,SAAS,OAAO,EAyB3C,CAAC;AAEF;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,SAAS,EAAE,SAAS,OAAO,EA0BvC,CAAC;AAEF,uEAAuE;AACvE,eAAO,MAAM,YAAY,EAAE,SAAS,OAAO,EAK1C,CAAC;AAEF,8CAA8C;AAC9C,MAAM,WAAW,gBAAgB;IAC7B,gFAAgF;IAChF,OAAO,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;CAChC;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,SAAS,EAAE,gBAAgB,GAAG,MAAM,EAAE,CASrE;AAED,uDAAuD;AACvD,wBAAgB,kBAAkB,IAAI,MAAM,EAAE,CAE7C;AAED,gFAAgF;AAChF,wBAAgB,YAAY,CAAC,SAAS,EAAE,gBAAgB,GAAG,GAAG,CAAC,MAAM,CAAC,CAErE"}
|
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* What a generated app is made of.
|
|
4
|
+
*
|
|
5
|
+
* **This file is the contract between the release and the scaffolder.** Every
|
|
6
|
+
* published `@orthacms/*` package is accounted for here exactly once — as core,
|
|
7
|
+
* as part of an optional feature, or as deliberately transitive — and
|
|
8
|
+
* `features.spec.ts` fails the build when one is not. That guard is the point:
|
|
9
|
+
* adding a package to the workspace should force a decision about whether a new
|
|
10
|
+
* app gets it, rather than the template quietly falling a release behind.
|
|
11
|
+
*
|
|
12
|
+
* Versions are *not* listed. Every `@orthacms/*` dependency is pinned to the
|
|
13
|
+
* scaffolder's own version at render time (`__ORTHA_VERSION__`), so a release
|
|
14
|
+
* bumps the whole set with no edit here.
|
|
15
|
+
*/
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.ALL_FEATURES = exports.PROTOCOLS = exports.SSO_PROVIDERS = exports.COPILOT_PROVIDERS = exports.MEDIA_PROVIDERS = exports.TRANSITIVE_PACKAGES = exports.CORE_DEV_PACKAGES = exports.CORE_PACKAGES = void 0;
|
|
18
|
+
exports.resolvePackages = resolvePackages;
|
|
19
|
+
exports.resolveDevPackages = resolveDevPackages;
|
|
20
|
+
exports.resolveFlags = resolveFlags;
|
|
21
|
+
/**
|
|
22
|
+
* The packages every app gets, whatever it opts into.
|
|
23
|
+
*
|
|
24
|
+
* The **copilot** is here — plugin, admin panel and the offline `fake` adapter —
|
|
25
|
+
* even though it is a large feature nobody may want. Two reasons. Its server
|
|
26
|
+
* half arrives anyway: five core plugins (`content`, `activity`, `i18n`,
|
|
27
|
+
* `media`, `users`) depend on `copilot-server` to contribute their tools, so
|
|
28
|
+
* the code is on disk whatever the manifest says, and leaving it undeclared
|
|
29
|
+
* bought nothing but a missing chat panel. And `fake` needs no key and no
|
|
30
|
+
* network, so a default app gets a copilot that genuinely works offline —
|
|
31
|
+
* while `COPILOT_ENABLED` stays `false`, so nothing reaches a model until an
|
|
32
|
+
* operator says so.
|
|
33
|
+
*
|
|
34
|
+
* The **extension points** are here for the same reason — `content-domain`,
|
|
35
|
+
* `copilot-domain`, `tools-server`, `query-builder-admin`. Every one of them
|
|
36
|
+
* already arrives transitively, so an import would resolve on npm's flat
|
|
37
|
+
* `node_modules` today; declaring them is what makes that resolution something
|
|
38
|
+
* the app owns rather than something it borrows. An undeclared import breaks
|
|
39
|
+
* the moment a version conflict nests a copy, and never resolves under pnpm at
|
|
40
|
+
* all.
|
|
41
|
+
*
|
|
42
|
+
* **SSO** contributes two entries on the same reasoning as the copilot's.
|
|
43
|
+
* `identity-domain` is an extension point — the `SsoProvider` port an operator
|
|
44
|
+
* implements to reach an identity provider we do not ship an adapter for — and
|
|
45
|
+
* it already arrives transitively through `identity-server`, so declaring it is
|
|
46
|
+
* what makes that resolution something the app owns rather than borrows.
|
|
47
|
+
* `identity-provider-fake` is the scripted identity provider: it needs no
|
|
48
|
+
* tenant and no network, so it is how a generated app's sign-in page can be
|
|
49
|
+
* exercised offline, exactly as `copilot-provider-fake` is for the chat. Note
|
|
50
|
+
* that shipping it installs nothing: an adapter only does something once the
|
|
51
|
+
* composition root registers it, and the template registers none.
|
|
52
|
+
*
|
|
53
|
+
* `design-system`, `utils-admin` and `utils-server` are here even though the
|
|
54
|
+
* template's own files barely touch them: they are the first things anyone
|
|
55
|
+
* reaches for when writing a page or a plugin of their own, and relying on
|
|
56
|
+
* npm's hoisting to make an undeclared import work is a phantom dependency —
|
|
57
|
+
* it resolves until a version conflict nests a copy, and never resolves under
|
|
58
|
+
* pnpm at all.
|
|
59
|
+
*/
|
|
60
|
+
exports.CORE_PACKAGES = [
|
|
61
|
+
'@orthacms/activity-admin',
|
|
62
|
+
'@orthacms/activity-server',
|
|
63
|
+
'@orthacms/api-tokens-admin',
|
|
64
|
+
'@orthacms/bootstrap-admin',
|
|
65
|
+
'@orthacms/bootstrap-server',
|
|
66
|
+
'@orthacms/content-admin',
|
|
67
|
+
'@orthacms/content-domain',
|
|
68
|
+
'@orthacms/content-server',
|
|
69
|
+
'@orthacms/copilot-admin',
|
|
70
|
+
'@orthacms/copilot-domain',
|
|
71
|
+
'@orthacms/copilot-provider-fake',
|
|
72
|
+
'@orthacms/copilot-server',
|
|
73
|
+
'@orthacms/database',
|
|
74
|
+
'@orthacms/design-system',
|
|
75
|
+
'@orthacms/i18n-admin',
|
|
76
|
+
'@orthacms/i18n-server',
|
|
77
|
+
'@orthacms/identity-admin',
|
|
78
|
+
'@orthacms/identity-domain',
|
|
79
|
+
'@orthacms/identity-provider-fake',
|
|
80
|
+
'@orthacms/identity-server',
|
|
81
|
+
'@orthacms/insights-admin',
|
|
82
|
+
'@orthacms/media-admin',
|
|
83
|
+
'@orthacms/media-server',
|
|
84
|
+
'@orthacms/query-builder-admin',
|
|
85
|
+
'@orthacms/shell-admin',
|
|
86
|
+
'@orthacms/tools-server',
|
|
87
|
+
'@orthacms/transfer-admin',
|
|
88
|
+
'@orthacms/transfer-domain',
|
|
89
|
+
'@orthacms/transfer-server',
|
|
90
|
+
'@orthacms/users-admin',
|
|
91
|
+
'@orthacms/users-server',
|
|
92
|
+
'@orthacms/utils-admin',
|
|
93
|
+
'@orthacms/utils-server',
|
|
94
|
+
'@orthacms/workspaces-admin',
|
|
95
|
+
'@orthacms/workspaces-server',
|
|
96
|
+
'@orthacms/wysiwyg-admin'
|
|
97
|
+
];
|
|
98
|
+
/** Packages the app needs to build and run itself, as devDependencies. */
|
|
99
|
+
exports.CORE_DEV_PACKAGES = ['@orthacms/cli'];
|
|
100
|
+
/**
|
|
101
|
+
* Packages deliberately left undeclared — published, but with no reason for a
|
|
102
|
+
* generated app to import them.
|
|
103
|
+
*
|
|
104
|
+
* Both entries are tools for **writing a storage provider**, not for running
|
|
105
|
+
* one. `StorageProviderCheck` refuses to boot a database whose rows were
|
|
106
|
+
* written by a provider that is no longer configured, so the in-memory backend
|
|
107
|
+
* is a test and offline-development affordance, never a deployment: offering it
|
|
108
|
+
* in the scaffolder would be offering an app that loses every upload on
|
|
109
|
+
* restart. The testkit is the contract suite those providers run against.
|
|
110
|
+
*
|
|
111
|
+
* Everything else a generated app can reach is in its own manifest, so "it
|
|
112
|
+
* resolves because npm hoisted it" is never the answer to why an import works.
|
|
113
|
+
* Putting a package here is a decision the coverage guard accepts; forgetting
|
|
114
|
+
* it entirely is not.
|
|
115
|
+
*/
|
|
116
|
+
exports.TRANSITIVE_PACKAGES = [
|
|
117
|
+
'@orthacms/media-provider-memory',
|
|
118
|
+
'@orthacms/media-provider-testkit'
|
|
119
|
+
];
|
|
120
|
+
/**
|
|
121
|
+
* Where uploads are written.
|
|
122
|
+
*
|
|
123
|
+
* A single-choice group: media always runs, the question is only which adapter
|
|
124
|
+
* backs it. S3 is listed and disabled — the package exists but has never been
|
|
125
|
+
* released, and offering it would generate an app that cannot install.
|
|
126
|
+
*/
|
|
127
|
+
exports.MEDIA_PROVIDERS = [
|
|
128
|
+
{
|
|
129
|
+
id: 'media-local',
|
|
130
|
+
label: 'Local filesystem',
|
|
131
|
+
hint: 'Writes to a directory on disk. Point MEDIA_LOCAL_ROOT at a volume in production.',
|
|
132
|
+
packages: ['@orthacms/media-provider-local'],
|
|
133
|
+
enabledByDefault: true,
|
|
134
|
+
available: true
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
id: 'media-azure',
|
|
138
|
+
label: 'Azure Blob Storage',
|
|
139
|
+
hint: 'Set MEDIA_AZURE_CONTAINER and a connection string. Managed identity needs a hand-built client — see the package docs.',
|
|
140
|
+
packages: ['@orthacms/media-provider-azure'],
|
|
141
|
+
enabledByDefault: false,
|
|
142
|
+
available: true
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
id: 'media-gcs',
|
|
146
|
+
label: 'Google Cloud Storage',
|
|
147
|
+
hint: 'Native GCS auth. If an HMAC key is acceptable, the S3-compatible adapter reaches GCS too — one package fewer.',
|
|
148
|
+
packages: ['@orthacms/media-provider-gcs'],
|
|
149
|
+
enabledByDefault: false,
|
|
150
|
+
available: true
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
id: 'media-vercel-blob',
|
|
154
|
+
label: 'Vercel Blob',
|
|
155
|
+
hint: 'Smallest setup on Vercel — but every blob gets a permanent public URL, so not for confidential media.',
|
|
156
|
+
packages: ['@orthacms/media-provider-vercel-blob'],
|
|
157
|
+
enabledByDefault: false,
|
|
158
|
+
available: true
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
id: 'media-s3',
|
|
162
|
+
label: 'S3-compatible',
|
|
163
|
+
hint: 'Cloudflare R2, AWS S3, MinIO, Spaces, B2, Wasabi — set MEDIA_S3_BUCKET and, for anything but AWS, MEDIA_S3_ENDPOINT.',
|
|
164
|
+
packages: ['@orthacms/media-provider-s3'],
|
|
165
|
+
enabledByDefault: false,
|
|
166
|
+
available: true
|
|
167
|
+
}
|
|
168
|
+
];
|
|
169
|
+
/**
|
|
170
|
+
* Model backends for the AI copilot.
|
|
171
|
+
*
|
|
172
|
+
* A multi-choice group, and picking none is the meaningful default: enabling a
|
|
173
|
+
* hosted provider sends workspace content to a third party, which
|
|
174
|
+
* [ADR-0005](https://github.com/ortha-source/ortha-cms/blob/main/docs/adr/0005-copilot-authority-model.md)
|
|
175
|
+
* §10 says is an operator's decision to make explicitly. Pick nothing and the
|
|
176
|
+
* copilot is not registered at all.
|
|
177
|
+
*
|
|
178
|
+
* `copilot-provider-fake` is not offered here — it is installed automatically
|
|
179
|
+
* whenever the copilot is on. It is a shipped adapter rather than test
|
|
180
|
+
* scaffolding (ADR-0004 §3): it needs no key and no network, so it is what
|
|
181
|
+
* makes the chat work offline, and it is registered last so it is the default
|
|
182
|
+
* only when it is the only one.
|
|
183
|
+
*/
|
|
184
|
+
exports.COPILOT_PROVIDERS = [
|
|
185
|
+
{
|
|
186
|
+
id: 'copilot-anthropic',
|
|
187
|
+
label: 'Claude (Anthropic)',
|
|
188
|
+
hint: 'Native Claude. Needs ANTHROPIC_API_KEY.',
|
|
189
|
+
packages: ['@orthacms/copilot-provider-anthropic'],
|
|
190
|
+
enabledByDefault: false,
|
|
191
|
+
available: true
|
|
192
|
+
},
|
|
193
|
+
{
|
|
194
|
+
id: 'copilot-openai',
|
|
195
|
+
label: 'OpenAI-compatible endpoint',
|
|
196
|
+
hint: 'Ollama, vLLM, LiteLLM, Azure or OpenAI. Needs COPILOT_OPENAI_BASE_URL.',
|
|
197
|
+
packages: ['@orthacms/copilot-provider-openai'],
|
|
198
|
+
enabledByDefault: false,
|
|
199
|
+
available: true
|
|
200
|
+
}
|
|
201
|
+
];
|
|
202
|
+
/**
|
|
203
|
+
* How people sign in to the admin.
|
|
204
|
+
*
|
|
205
|
+
* A single opt-in, and off by default, because SSO is not something a CMS can
|
|
206
|
+
* usefully guess at: it needs an issuer, a client and a callback URL registered
|
|
207
|
+
* on the other side, none of which a scaffolder can invent. A generated app
|
|
208
|
+
* without it signs in with email and password, which is the invite-only flow
|
|
209
|
+
* Ortha has always had.
|
|
210
|
+
*
|
|
211
|
+
* **One entry covers most of the field.** Okta, Auth0, Keycloak, Google, Entra
|
|
212
|
+
* ID, Authentik, Zitadel, JumpCloud, Ping and GitLab all speak OpenID Connect,
|
|
213
|
+
* and the named vendors are preset factories inside that one package rather
|
|
214
|
+
* than packages of their own — the SSO equivalent of the copilot's
|
|
215
|
+
* OpenAI-compatible adapter.
|
|
216
|
+
*
|
|
217
|
+
* The other two are here because their **wire** genuinely differs, which is the
|
|
218
|
+
* only thing that earns a package: GitHub is OAuth2 with no identity token, and
|
|
219
|
+
* SAML is a POST binding with XML signatures. Each also brings its own
|
|
220
|
+
* dependency — `jose` for OIDC, `@node-saml/node-saml` for SAML — which is a
|
|
221
|
+
* second reason not to install them for an app that will never speak them.
|
|
222
|
+
*
|
|
223
|
+
* `identity-provider-fake` is not offered: it is installed unconditionally,
|
|
224
|
+
* like `copilot-provider-fake`, because it needs no tenant and no network and
|
|
225
|
+
* is how a generated app's sign-in page is exercised offline. Installing it
|
|
226
|
+
* registers nothing — an adapter only does something once the composition root
|
|
227
|
+
* names it, and the template names none.
|
|
228
|
+
*/
|
|
229
|
+
exports.SSO_PROVIDERS = [
|
|
230
|
+
{
|
|
231
|
+
id: 'sso-oidc',
|
|
232
|
+
label: 'OpenID Connect single sign-on',
|
|
233
|
+
hint: 'Okta, Auth0, Keycloak, Google, Entra ID and the rest. Needs SSO_OIDC_ISSUER and SSO_OIDC_CLIENT_ID.',
|
|
234
|
+
packages: ['@orthacms/identity-provider-oidc'],
|
|
235
|
+
enabledByDefault: false,
|
|
236
|
+
available: true
|
|
237
|
+
},
|
|
238
|
+
{
|
|
239
|
+
id: 'sso-github',
|
|
240
|
+
label: 'GitHub sign-in',
|
|
241
|
+
hint: 'GitHub or GitHub Enterprise Server. Needs SSO_GITHUB_CLIENT_ID and SSO_GITHUB_CLIENT_SECRET.',
|
|
242
|
+
packages: ['@orthacms/identity-provider-github'],
|
|
243
|
+
enabledByDefault: false,
|
|
244
|
+
available: true
|
|
245
|
+
},
|
|
246
|
+
{
|
|
247
|
+
id: 'sso-saml',
|
|
248
|
+
label: 'SAML 2.0 single sign-on',
|
|
249
|
+
hint: 'For an identity provider that speaks SAML rather than OIDC. Needs the IdP certificate and entry point.',
|
|
250
|
+
packages: ['@orthacms/identity-provider-saml'],
|
|
251
|
+
enabledByDefault: false,
|
|
252
|
+
available: true
|
|
253
|
+
}
|
|
254
|
+
];
|
|
255
|
+
/**
|
|
256
|
+
* How the content API is spoken.
|
|
257
|
+
*
|
|
258
|
+
* REST is always there and is shown `locked` rather than hidden, because "which
|
|
259
|
+
* protocols does this app serve" is a more useful question than "do you want
|
|
260
|
+
* these two extras" — the answer should read as a set, with the one you always
|
|
261
|
+
* get visible in it.
|
|
262
|
+
*
|
|
263
|
+
* Neither addition brings a credential or a permission of its own: GraphQL is
|
|
264
|
+
* an adapter over the REST API's own services
|
|
265
|
+
* ([ADR-0008](https://github.com/ortha-source/ortha-cms/blob/main/docs/adr/0008-graphql-as-a-protocol-adapter.md)),
|
|
266
|
+
* and MCP reuses the same API tokens and scopes. They are opt-in because an
|
|
267
|
+
* endpoint nobody asked for is still an endpoint.
|
|
268
|
+
*/
|
|
269
|
+
exports.PROTOCOLS = [
|
|
270
|
+
{
|
|
271
|
+
id: 'rest',
|
|
272
|
+
label: 'REST',
|
|
273
|
+
hint: 'Always on — /api/v1/…, the API every other protocol adapts.',
|
|
274
|
+
packages: [],
|
|
275
|
+
enabledByDefault: true,
|
|
276
|
+
available: true,
|
|
277
|
+
locked: true
|
|
278
|
+
},
|
|
279
|
+
{
|
|
280
|
+
id: 'graphql',
|
|
281
|
+
label: 'GraphQL content API',
|
|
282
|
+
hint: 'POST /api/v1/graphql, alongside REST. Same tokens, same scopes.',
|
|
283
|
+
packages: ['@orthacms/content-graphql'],
|
|
284
|
+
enabledByDefault: false,
|
|
285
|
+
available: true
|
|
286
|
+
},
|
|
287
|
+
{
|
|
288
|
+
id: 'mcp',
|
|
289
|
+
label: 'MCP server',
|
|
290
|
+
hint: 'Lets an external agent do content CRUD with an API token. Off unless MCP_ENABLED=true.',
|
|
291
|
+
packages: ['@orthacms/mcp-server'],
|
|
292
|
+
enabledByDefault: false,
|
|
293
|
+
available: true
|
|
294
|
+
}
|
|
295
|
+
];
|
|
296
|
+
/** Every optional feature, in the order the wizard asks about them. */
|
|
297
|
+
exports.ALL_FEATURES = [
|
|
298
|
+
...exports.MEDIA_PROVIDERS,
|
|
299
|
+
...exports.COPILOT_PROVIDERS,
|
|
300
|
+
...exports.SSO_PROVIDERS,
|
|
301
|
+
...exports.PROTOCOLS
|
|
302
|
+
];
|
|
303
|
+
/**
|
|
304
|
+
* The `@orthacms/*` dependencies for a selection, sorted.
|
|
305
|
+
*
|
|
306
|
+
* Built here rather than with `ortha:if` blocks inside `package.json.tmpl`:
|
|
307
|
+
* removing lines from JSON is how you get a trailing comma and an app that
|
|
308
|
+
* cannot even be installed, and the failure would name the template rather than
|
|
309
|
+
* the feature that was switched off.
|
|
310
|
+
*/
|
|
311
|
+
function resolvePackages(selection) {
|
|
312
|
+
const packages = new Set(exports.CORE_PACKAGES);
|
|
313
|
+
for (const feature of exports.ALL_FEATURES) {
|
|
314
|
+
if (!selection.enabled.has(feature.id))
|
|
315
|
+
continue;
|
|
316
|
+
for (const name of feature.packages)
|
|
317
|
+
packages.add(name);
|
|
318
|
+
}
|
|
319
|
+
return [...packages].sort();
|
|
320
|
+
}
|
|
321
|
+
/** The dev-time `@orthacms/*` dependencies, sorted. */
|
|
322
|
+
function resolveDevPackages() {
|
|
323
|
+
return [...exports.CORE_DEV_PACKAGES].sort();
|
|
324
|
+
}
|
|
325
|
+
/** The feature ids in force for a selection — what `ortha:if` tests against. */
|
|
326
|
+
function resolveFlags(selection) {
|
|
327
|
+
return new Set(selection.enabled);
|
|
328
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { type FeatureSelection } from './features';
|
|
2
|
+
/** The values substituted into the template's placeholders. */
|
|
3
|
+
export interface TemplateValues {
|
|
4
|
+
/** npm package name for the generated app. */
|
|
5
|
+
appName: string;
|
|
6
|
+
/** Human-readable name, used in the page title and the README. */
|
|
7
|
+
appTitle: string;
|
|
8
|
+
/** Postgres connection string. */
|
|
9
|
+
databaseUrl: string;
|
|
10
|
+
/** Database name, so `docker-compose.yml` creates the right one. */
|
|
11
|
+
databaseName: string;
|
|
12
|
+
/** Email of the admin provisioned on first boot. */
|
|
13
|
+
adminEmail: string;
|
|
14
|
+
/** Password for that admin. */
|
|
15
|
+
adminPassword: string;
|
|
16
|
+
/** The `@orthacms/*` version every dependency is pinned to. */
|
|
17
|
+
orthaVersion: string;
|
|
18
|
+
/** Which optional features the app was scaffolded with. */
|
|
19
|
+
selection: FeatureSelection;
|
|
20
|
+
}
|
|
21
|
+
/** Substitutes every `__PLACEHOLDER__` in `contents`. */
|
|
22
|
+
export declare function render(contents: string, values: TemplateValues): string;
|
|
23
|
+
/**
|
|
24
|
+
* Renders `package.json` for a selection.
|
|
25
|
+
*
|
|
26
|
+
* The dependency map is **rebuilt**, not patched: the template ships a manifest
|
|
27
|
+
* with the non-Ortha dependencies and an empty `@orthacms` set, and the chosen
|
|
28
|
+
* packages are merged in and re-sorted here. Every `@orthacms/*` range is the
|
|
29
|
+
* scaffolder's own version, exactly — no caret. Releases are lockstep, and a
|
|
30
|
+
* partial upgrade can leave two copies of a shared package in `node_modules`,
|
|
31
|
+
* which means two React context instances and an admin whose sidebar silently
|
|
32
|
+
* stops talking to its provider.
|
|
33
|
+
*/
|
|
34
|
+
export declare function renderManifest(template: string, values: TemplateValues): string;
|
|
35
|
+
/**
|
|
36
|
+
* Copies the template into `target`: conditional blocks applied, placeholders
|
|
37
|
+
* substituted, renames performed.
|
|
38
|
+
*
|
|
39
|
+
* Binary files are copied verbatim — a distinction worth keeping even though
|
|
40
|
+
* today's template is all text, since running a favicon through a string
|
|
41
|
+
* replace corrupts it in a way that only shows up in a browser.
|
|
42
|
+
*/
|
|
43
|
+
export declare function renderTemplate(templateDir: string, target: string, values: TemplateValues): void;
|
|
44
|
+
/** Whether `dir` exists and holds anything. */
|
|
45
|
+
export declare function isNonEmptyDirectory(dir: string): boolean;
|
|
46
|
+
//# sourceMappingURL=template.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"template.d.ts","sourceRoot":"","sources":["../../src/lib/template.ts"],"names":[],"mappings":"AAWA,OAAO,EAIH,KAAK,gBAAgB,EACxB,MAAM,YAAY,CAAC;AAEpB,+DAA+D;AAC/D,MAAM,WAAW,cAAc;IAC3B,8CAA8C;IAC9C,OAAO,EAAE,MAAM,CAAC;IAChB,kEAAkE;IAClE,QAAQ,EAAE,MAAM,CAAC;IACjB,kCAAkC;IAClC,WAAW,EAAE,MAAM,CAAC;IACpB,oEAAoE;IACpE,YAAY,EAAE,MAAM,CAAC;IACrB,oDAAoD;IACpD,UAAU,EAAE,MAAM,CAAC;IACnB,+BAA+B;IAC/B,aAAa,EAAE,MAAM,CAAC;IACtB,+DAA+D;IAC/D,YAAY,EAAE,MAAM,CAAC;IACrB,2DAA2D;IAC3D,SAAS,EAAE,gBAAgB,CAAC;CAC/B;AAoBD,yDAAyD;AACzD,wBAAgB,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,cAAc,GAAG,MAAM,CAevE;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,cAAc,CAC1B,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,cAAc,GACvB,MAAM,CAmBR;AAuBD;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAC1B,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,cAAc,GACvB,IAAI,CAiCN;AAED,+CAA+C;AAC/C,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAMxD"}
|