-
Notifications
You must be signed in to change notification settings - Fork 644
Expand file tree
/
Copy pathindex.js
More file actions
237 lines (219 loc) · 11.2 KB
/
Copy pathindex.js
File metadata and controls
237 lines (219 loc) · 11.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
// Public API of create-apostrophe. This is the only module consumers may
// import; everything under core/, ui/ and telemetry/ is private.
//
// Re-exports:
// - createProject (headless install orchestration)
// - runInteractive (structured entry — guided install with no argv
// parsing; the @apostrophecms/cli `create`
// delegate uses this so Commander keeps ownership
// of help)
// - runTelemetryCommand (status | on | off | preview, structured entry;
// the apos `telemetry` delegate uses this)
// - runCli (argv-driven entry for the standalone bin and
// any host that wants full pass-through)
//
// The typedefs below are the shared contract between the UI, core and
// telemetry layers.
/**
* Starter-kit identifier. `astro` kits pair an Astro frontend with an
* Apostrophe backend; the others are full-stack standalone. `demo` kits
* scaffold a working example; `demo-data` additionally seeds a database
* dump and sample images. Multisite/pro kits are not offered here.
* @typedef {(
* 'apostrophe-astro-essentials' |
* 'apostrophe-astro-demo' |
* 'apostrophe-astro-demo-data' |
* 'apostrophe-essentials' |
* 'apostrophe-demo' |
* 'apostrophe-demo-data'
* )} KitId
*/
/**
* Database selection. Used for both the createProject input and the
* telemetry dbChoice value.
* @typedef {('sqlite' | 'mongodb' | 'postgres')} DbChoice
*/
/**
* Package manager, detected from the environment (never self-reported).
* `'unknown'` when it cannot be determined.
* @typedef {('npm' | 'pnpm' | 'yarn' | 'unknown')} PackageManager
*/
/**
* The stage a failed install is attributed to. `null` means the failure
* happened in preflight (Node version / package manager), before any stage
* ran — every step is skipped. Otherwise exactly one stage per failure.
* @typedef {(
* 'clone' | 'dependency_install' | 'db_connect' | 'scaffold' | 'sample_data'
* | 'admin' | 'unknown' | null
* )} FailStage
*/
/**
* Terminal telemetry event. Exactly one is emitted per attempt.
* @typedef {('install_success' | 'install_fail')} TelemetryEvent
*/
/**
* Admin account created during install. `username` is the value the user
* will log in with; it may be email-shaped (a single field, no separate
* email). Never reaches telemetry.
* @typedef {object} AdminAccount
* @property {string} username Username or email.
* @property {string} password Plaintext; passed over stdin, never logged.
*/
/**
* Fully resolved, already-validated inputs. createProject does not prompt.
* @typedef {object} CreateProjectOptions
* @property {string} shortName Project/directory name.
* @property {string} cwd Authoritative working
* directory the install runs
* from. The caller (bin / UI)
* captures it once and threads
* it in; steps must not call
* process.cwd() on their own.
* @property {KitId | 'custom'} kitId Starter kit, or `'custom'` for a
* `--starter` install (escapes the
* kit registry; see `starter`).
* @property {import('./core/starter.js').ResolvedStarter} [starter]
* A custom starter resolved from
* `--starter`. When present it
* overrides `kitId` for cloning:
* the repo is cloned directly and
* the frontend is detected from the
* clone rather than the registry.
* @property {DbChoice} dbChoice Database selection.
* @property {string} [dbUri] Connection string for
* mongodb/postgres. Never sent to
* telemetry. Ignored for sqlite.
* @property {AdminAccount} admin Admin account to create.
* @property {PackageManager} [packageManager] Override; otherwise detected.
* @property {('keep' | 'drop')} [dbReset] Consent to drop a pre-existing
* mongodb/postgres database before
* install. `'drop'` only when the
* user explicitly confirmed it for
* a non-empty, non-seed target;
* defaults to `'keep'`. (Seed kits
* reset the DB in the sample-data
* step, not via this flag.)
* @property {boolean} nonInteractive CI mode: no prompts downstream.
* @property {number} confirmedAt Plan-confirmation timestamp
* (epoch ms), set by the caller.
* Source of durationMs.
*/
/**
* Structured outcome. createProject never throws for an expected install
* failure and never calls process.exit: it resolves with `ok: false` and a
* `failStage`. An unexpected throw is the caller's to convert into a fail
* result.
* @typedef {object} CreateProjectResult
* @property {boolean} ok Whether the project was created.
* @property {KitId | 'custom'} kitId Echo of the input (`'custom'` for
* a `--starter` install).
* @property {DbChoice} dbChoice Echo of the input.
* @property {PackageManager} packageManager Resolved value.
* @property {number} durationMs `Date.now() - options.confirmedAt`.
* @property {import('./core/kits.js').Frontend} [frontend] Resolved layout
* (`'astro'` | `null`). Present only
* on success; never sent to
* telemetry. Lets the UI pick the
* right dev-server port for a custom
* starter whose frontend wasn't known
* until after the clone.
* @property {FailStage} [failStage] Present iff `ok === false`;
* `null` for a preflight failure.
* @property {string} [errorCode] Present only for known,
* allowlisted error codes.
*/
/**
* Partial payload createProject hands to {@link TelemetryHook.event}. The
* telemetry layer adds `event` (discriminator), `cliVersion`, and
* `anonymousId` before sending; the schema validators reject any field
* outside this allowlist. `failStage`/`errorCode` are present iff the result
* was a failure. `errorCode` is typed `string` here — the telemetry-side
* allowlist drops unknown codes, so the contract stays permissive while the
* wire stays strict.
* @typedef {object} TelemetryEventPayload
* @property {KitId} kitId
* @property {DbChoice} dbChoice
* @property {PackageManager} packageManager
* @property {number} durationMs
* @property {FailStage} [failStage]
* @property {string} [errorCode]
*/
/**
* Telemetry hook injected into createProject. Called exactly once, at the
* single terminal point. Fire-and-forget: `event` must not throw or delay
* the caller.
* @typedef {object} TelemetryHook
* @property {(name: TelemetryEvent, payload: TelemetryEventPayload) => void} event
*/
/**
* Hook returned by {@link createTelemetry}. Extends {@link TelemetryHook}
* with `flush()` so the bin can await any in-flight emission at process
* exit. `event()` is fire-and-forget; `flush()` is the bounded wait.
* @typedef {TelemetryHook & { flush(): Promise<void> }} ManagedTelemetryHook
*/
/**
* Handle returned by `Logger.task` to settle a unit of work.
* @typedef {object} TaskHandle
* @property {(msg?: string) => void} succeed
* @property {(msg?: string) => void} fail
* @property {(fraction: number) => void} [progress]
* Optional progress channel for a task opened with `{ progress: true }`
* (e.g. a streamed download). `fraction` is 0–1, forward-only, of *this*
* task. Spinner tasks ignore it; loggers with no visual progress surface
* (headless/test) omit it — callers treat it as optional
* (`task.progress?.(...)`).
*/
/**
* Headless progress sink injected into createProject, keeping core
* UI-agnostic. The UI layer adapts its spinners to this; tests pass a
* recording stub. `task` opens a unit of work and returns a handle.
* @typedef {object} Logger
* @property {(msg: string) => void} info
* @property {(msg: string) => void} warn
* @property {(msg: string) => void} error
* @property {(msg: string) => void} [muted] Optional dimmed-aside
* channel for inline notes the user can ignore (e.g. recovery hints).
* Optional so the contract stays backwards-compatible with existing
* logger stubs.
* @property {(label: string, opts?: { progress?: boolean }) => TaskHandle} task
* Open a unit of work. `{ progress: true }` renders a progress bar (advance
* it via the handle's `progress()`); otherwise a spinner.
*/
/**
* @typedef {object} CreateProjectDeps
* @property {TelemetryHook} telemetry
* @property {Logger} logger
*/
/**
* Headless project orchestration: no prompts, no process.exit, no UI imports.
* @typedef {(
* options: CreateProjectOptions,
* deps: CreateProjectDeps
* ) => Promise<CreateProjectResult>} CreateProject
*/
/**
* Construction-time config for the telemetry client. The caller reads
* consent + anonymousId from the local store and passes them in;
* createTelemetry performs no store I/O. `consent: false` makes the
* returned hook a no-op (no payload built, no transport invoked).
* @typedef {object} TelemetryConfig
* @property {boolean} consent Opt-in result.
* @property {string} cliVersion Semver of the invoking package.
* @property {string} [anonymousId] Stable anonymous identifier (UUID v4).
* Generated by consent.js on first opt-in,
* reused thereafter. Absent until the user
* has ever opted in.
* @property {string} [endpoint] Analytics endpoint override.
* @property {string} [writeKey] Analytics write key (Umami website id)
* override.
* @property {string} [hostname] Wire payload `hostname` override
* (matches Umami's site-domain filter).
* @property {boolean} [verbose] Surface transport errors (otherwise silent).
*/
/**
* @typedef {(config: TelemetryConfig) => ManagedTelemetryHook} CreateTelemetry
*/
export { createProject } from './core/create-project.js';
export {
main as runCli, runInteractive, runTelemetryCommand
} from './cli/main.js';