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.
Files changed (55) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +7 -0
  3. package/dist/cli.d.ts +3 -0
  4. package/dist/cli.d.ts.map +1 -0
  5. package/dist/cli.js +277 -0
  6. package/dist/index.d.ts +11 -0
  7. package/dist/index.d.ts.map +1 -0
  8. package/dist/index.js +32 -0
  9. package/dist/lib/conditionals.d.ts +37 -0
  10. package/dist/lib/conditionals.d.ts.map +1 -0
  11. package/dist/lib/conditionals.js +115 -0
  12. package/dist/lib/features.d.ts +185 -0
  13. package/dist/lib/features.d.ts.map +1 -0
  14. package/dist/lib/features.js +328 -0
  15. package/dist/lib/template.d.ts +46 -0
  16. package/dist/lib/template.d.ts.map +1 -0
  17. package/dist/lib/template.js +115 -0
  18. package/dist/lib/ui.d.ts +76 -0
  19. package/dist/lib/ui.d.ts.map +1 -0
  20. package/dist/lib/ui.js +310 -0
  21. package/dist/lib/validate.d.ts +13 -0
  22. package/dist/lib/validate.d.ts.map +1 -0
  23. package/dist/lib/validate.js +51 -0
  24. package/package.json +37 -0
  25. package/templates/default/README.md.tmpl +199 -0
  26. package/templates/default/_gitignore +8 -0
  27. package/templates/default/apps/admin/index.html +38 -0
  28. package/templates/default/apps/admin/src/main.tsx +5 -0
  29. package/templates/default/apps/admin/src/plugins.spec.ts +58 -0
  30. package/templates/default/apps/admin/src/plugins.ts +57 -0
  31. package/templates/default/apps/admin/src/styles.css +254 -0
  32. package/templates/default/apps/admin/tsconfig.json +29 -0
  33. package/templates/default/apps/admin/vite.config.mts +67 -0
  34. package/templates/default/apps/admin-e2e/playwright.config.ts +51 -0
  35. package/templates/default/apps/admin-e2e/src/auth.spec.ts +55 -0
  36. package/templates/default/apps/admin-e2e/src/support/seed.ts +62 -0
  37. package/templates/default/apps/admin-e2e/tsconfig.json +23 -0
  38. package/templates/default/apps/server/jest.config.js +38 -0
  39. package/templates/default/apps/server/jest.setup.js +20 -0
  40. package/templates/default/apps/server/ortha.config.ts +505 -0
  41. package/templates/default/apps/server/src/main.ts +26 -0
  42. package/templates/default/apps/server/src/plugins.spec.ts +71 -0
  43. package/templates/default/apps/server/src/plugins.ts +229 -0
  44. package/templates/default/apps/server/tsconfig.json +31 -0
  45. package/templates/default/apps/server-e2e/jest.config.js +47 -0
  46. package/templates/default/apps/server-e2e/src/api.spec.ts +107 -0
  47. package/templates/default/apps/server-e2e/src/global-setup.ts +41 -0
  48. package/templates/default/apps/server-e2e/src/jest.setup.ts +28 -0
  49. package/templates/default/apps/server-e2e/src/support/db.ts +143 -0
  50. package/templates/default/apps/server-e2e/src/support/test-app.ts +64 -0
  51. package/templates/default/apps/server-e2e/tsconfig.json +26 -0
  52. package/templates/default/docker-compose.yml +21 -0
  53. package/templates/default/env.tmpl +140 -0
  54. package/templates/default/package.json.tmpl +51 -0
  55. 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"}