@nocobase/app-installer 0.0.0-stage → 0.1.0-beta.3

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 (61) hide show
  1. package/README.md +217 -2
  2. package/bin/run.js +43 -0
  3. package/dist/cli.js +171 -0
  4. package/dist/cli.js.map +1 -0
  5. package/dist/commands/install.js +383 -0
  6. package/dist/commands/install.js.map +1 -0
  7. package/dist/commands/rollback.js +287 -0
  8. package/dist/commands/rollback.js.map +1 -0
  9. package/dist/commands/status.js +155 -0
  10. package/dist/commands/status.js.map +1 -0
  11. package/dist/commands/upgrade.js +709 -0
  12. package/dist/commands/upgrade.js.map +1 -0
  13. package/dist/lib/app-cli.js +89 -0
  14. package/dist/lib/app-cli.js.map +1 -0
  15. package/dist/lib/backup.js +167 -0
  16. package/dist/lib/backup.js.map +1 -0
  17. package/dist/lib/confirm.js +36 -0
  18. package/dist/lib/confirm.js.map +1 -0
  19. package/dist/lib/current-link.js +22 -0
  20. package/dist/lib/current-link.js.map +1 -0
  21. package/dist/lib/ecosystem.js +86 -0
  22. package/dist/lib/ecosystem.js.map +1 -0
  23. package/dist/lib/env-file.js +87 -0
  24. package/dist/lib/env-file.js.map +1 -0
  25. package/dist/lib/errors.js +32 -0
  26. package/dist/lib/errors.js.map +1 -0
  27. package/dist/lib/health.js +55 -0
  28. package/dist/lib/health.js.map +1 -0
  29. package/dist/lib/initial-admin.js +39 -0
  30. package/dist/lib/initial-admin.js.map +1 -0
  31. package/dist/lib/interrupt.js +50 -0
  32. package/dist/lib/interrupt.js.map +1 -0
  33. package/dist/lib/invocation.js +33 -0
  34. package/dist/lib/invocation.js.map +1 -0
  35. package/dist/lib/layout.js +61 -0
  36. package/dist/lib/layout.js.map +1 -0
  37. package/dist/lib/lock.js +59 -0
  38. package/dist/lib/lock.js.map +1 -0
  39. package/dist/lib/mount-path.js +34 -0
  40. package/dist/lib/mount-path.js.map +1 -0
  41. package/dist/lib/output.js +61 -0
  42. package/dist/lib/output.js.map +1 -0
  43. package/dist/lib/pm2.js +92 -0
  44. package/dist/lib/pm2.js.map +1 -0
  45. package/dist/lib/prechecks.js +236 -0
  46. package/dist/lib/prechecks.js.map +1 -0
  47. package/dist/lib/registry.js +59 -0
  48. package/dist/lib/registry.js.map +1 -0
  49. package/dist/lib/release.js +328 -0
  50. package/dist/lib/release.js.map +1 -0
  51. package/dist/lib/run-command.js +108 -0
  52. package/dist/lib/run-command.js.map +1 -0
  53. package/dist/lib/service.js +100 -0
  54. package/dist/lib/service.js.map +1 -0
  55. package/dist/lib/source.js +100 -0
  56. package/dist/lib/source.js.map +1 -0
  57. package/dist/lib/state.js +73 -0
  58. package/dist/lib/state.js.map +1 -0
  59. package/dist/lib/version.js +51 -0
  60. package/dist/lib/version.js.map +1 -0
  61. package/package.json +51 -3
@@ -0,0 +1,287 @@
1
+ import { existsSync } from 'node:fs';
2
+ import path from 'node:path';
3
+ import { Flags } from '@oclif/core';
4
+ import { backupHasDatabase, restoreDatabase } from "../lib/backup.js";
5
+ import { confirm } from "../lib/confirm.js";
6
+ import { switchCurrent } from "../lib/current-link.js";
7
+ import { healthUrl, mountPathOf, readAppEnv } from "../lib/env-file.js";
8
+ import { assertFixedMountPath } from "../lib/mount-path.js";
9
+ import { EXIT_FAILED, EXIT_INVALID, EXIT_ROLLBACK_FAILED, InstallerError, } from "../lib/errors.js";
10
+ import { layoutOf, releaseDir, releaseLinkTarget } from "../lib/layout.js";
11
+ import { installerCommandLine } from "../lib/invocation.js";
12
+ import { acquireLock } from "../lib/lock.js";
13
+ import { checkPlatform, checkPm2, currentNodeMajor } from "../lib/prechecks.js";
14
+ import { checkPm2Ownership, errorLogCommandLine, errorLogTail, startApp, stopApp, } from "../lib/service.js";
15
+ import { capitalize, hostsApplications, nodeRebuildAdvice, subjectOf, } from "../lib/source.js";
16
+ import { fixedMountPathOf, readState, resolveReleaseRef, writeState, } from "../lib/state.js";
17
+ export const ROLLBACK_FLAGS = {
18
+ dir: Flags.string({
19
+ description: 'Installation root managed by app-installer. Defaults to the current directory.',
20
+ }),
21
+ to: Flags.string({
22
+ description: 'Release to return to, by id or version (the newest build of it). Defaults to the one the last upgrade came from.',
23
+ }),
24
+ restore: Flags.boolean({
25
+ allowNo: true,
26
+ default: true,
27
+ description: 'Restore the SQLite databases backed up before the upgrade, when that upgrade migrated them.',
28
+ }),
29
+ 'health-timeout': Flags.integer({
30
+ default: 180,
31
+ min: 1,
32
+ description: 'Seconds to wait for the release to answer its health check.',
33
+ }),
34
+ yes: Flags.boolean({
35
+ default: false,
36
+ description: 'Proceed without asking for confirmation.',
37
+ }),
38
+ json: Flags.boolean({
39
+ default: false,
40
+ description: 'Print one JSON result on stdout; progress stays on stderr.',
41
+ }),
42
+ };
43
+ function lastUpgrade(history, matches) {
44
+ for (let index = history.length - 1; index >= 0; index -= 1) {
45
+ const entry = history[index];
46
+ if (entry.action === 'upgrade' &&
47
+ entry.outcome !== 'rolled-back' &&
48
+ matches(entry)) {
49
+ return entry;
50
+ }
51
+ }
52
+ return undefined;
53
+ }
54
+ /**
55
+ * The id of the release to return to: the interrupted operation's origin, else where the last completed upgrade came
56
+ * from.
57
+ */
58
+ export function defaultRollbackTarget(state) {
59
+ // An interrupted upgrade is undone; an interrupted rollback is finished.
60
+ if (state.pending) {
61
+ return state.pending.action === 'upgrade'
62
+ ? state.pending.from
63
+ : state.pending.to;
64
+ }
65
+ return lastUpgrade(state.history, (entry) => entry.to === state.current)
66
+ ?.from;
67
+ }
68
+ /**
69
+ * The backup that holds the databases as they were on `target`, and whether they need restoring: only when the upgrade
70
+ * away from `target` applied migrations.
71
+ *
72
+ * An interrupted upgrade may have migrated once it moved `current`, and how far it got is unknown, so it counts as
73
+ * migrated from then on; before that it had only stopped the application. An interrupted rollback restores what it set
74
+ * out to.
75
+ */
76
+ export function backupFor(state, target) {
77
+ const pending = state.pending;
78
+ if (pending?.action === 'upgrade' && pending.from === target) {
79
+ return { backup: pending.backup, migrated: pending.switched === true };
80
+ }
81
+ if (pending?.action === 'rollback' && pending.to === target) {
82
+ return {
83
+ backup: pending.restoreFrom,
84
+ migrated: pending.restoreFrom !== undefined,
85
+ };
86
+ }
87
+ const upgrade = lastUpgrade(state.history, (entry) => entry.from === target && entry.to === state.current);
88
+ return {
89
+ backup: upgrade?.backup,
90
+ migrated: (upgrade?.migrations ?? 0) > 0,
91
+ };
92
+ }
93
+ export async function rollback(input, deps) {
94
+ const { flags } = input;
95
+ const { reporter, pm2 } = deps;
96
+ const root = path.resolve(deps.cwd ?? process.cwd(), flags.dir ?? '.');
97
+ const layout = layoutOf(root);
98
+ const releaseLock = await acquireLock(layout.lockFile);
99
+ try {
100
+ // Read under the lock: a run that waited on it must see what the previous one wrote.
101
+ const state = await readState(layout);
102
+ checkPlatform();
103
+ const subject = subjectOf(state);
104
+ const from = state.current;
105
+ const interrupted = state.pending;
106
+ const requested = flags.to ?? defaultRollbackTarget(state);
107
+ if (requested === undefined) {
108
+ throw new InstallerError('NOTHING_TO_ROLL_BACK', `No upgrade led to ${from}, so there is no earlier release to return to.`, {
109
+ exitCode: EXIT_INVALID,
110
+ suggestions: [
111
+ { message: 'Name a release on disk with --to; status lists them.' },
112
+ ],
113
+ });
114
+ }
115
+ const record = resolveReleaseRef(state, requested);
116
+ const target = record?.id ?? requested;
117
+ if (target === from && !interrupted) {
118
+ return {
119
+ status: 'success-noop',
120
+ result: { directory: root, current: from, rolledBack: false },
121
+ summary: [`${capitalize(subject)} is already on ${from}.`],
122
+ };
123
+ }
124
+ if (!record || !existsSync(releaseDir(layout, record.id))) {
125
+ throw new InstallerError('RELEASE_MISSING', `${requested} is not on disk; only releases app-installer kept can be returned to.`, {
126
+ exitCode: EXIT_INVALID,
127
+ suggestions: [{ message: 'status lists the releases on disk.' }],
128
+ });
129
+ }
130
+ const machineMajor = currentNodeMajor();
131
+ if (record.buildTarget.nodeMajor !== machineMajor) {
132
+ throw new InstallerError('NODE_MISMATCH', `${target} was built for Node ${record.buildTarget.nodeMajor}, but this machine runs Node ${machineMajor}; its native modules would not load.`, {
133
+ exitCode: EXIT_INVALID,
134
+ suggestions: nodeRebuildAdvice(state, root),
135
+ });
136
+ }
137
+ const env = await readAppEnv(layout);
138
+ // A release built before relocatable builds runs only at the path it was built for, which app.env may have moved
139
+ // away from since.
140
+ const fixedMountPath = fixedMountPathOf(record, state);
141
+ if (fixedMountPath !== undefined) {
142
+ assertFixedMountPath(target, fixedMountPath, mountPathOf(env));
143
+ }
144
+ const service = {
145
+ layout,
146
+ pm2,
147
+ name: state.name,
148
+ healthUrl: healthUrl(env),
149
+ fetchImpl: deps.fetchImpl,
150
+ };
151
+ await checkPm2(pm2);
152
+ await checkPm2Ownership(service);
153
+ // Restore only from a backup that actually holds the database: an external database is never in one, and an
154
+ // upgrade interrupted before its copy finished never records one.
155
+ const { backup, migrated } = backupFor(state, target);
156
+ const restoreFrom = flags.restore &&
157
+ migrated &&
158
+ backup !== undefined &&
159
+ backupHasDatabase(layout, backup)
160
+ ? backup
161
+ : undefined;
162
+ if (flags.restore && migrated && restoreFrom === undefined) {
163
+ reporter.warn(`The databases may have been migrated past ${target}, and app-installer holds no copy of them from before (an external database, or a backup that was cut short). Restore them from your own backup if ${target} misbehaves.`);
164
+ }
165
+ await confirm([
166
+ `Roll ${subject} at ${root} back from ${from} to ${target}.`,
167
+ hostsApplications(state)
168
+ ? `${capitalize(subject)} and every application it hosts stop while the release switches.`
169
+ : `${capitalize(subject)} stops while the release switches.`,
170
+ restoreFrom
171
+ ? `The SQLite databases are restored from ${restoreFrom}: whatever was written since that upgrade is lost.`
172
+ : 'The databases are left as they are; rolling back does not undo migrations.',
173
+ ], { yes: flags.yes, json: flags.json });
174
+ state.pending = {
175
+ action: 'rollback',
176
+ from,
177
+ to: target,
178
+ startedAt: new Date().toISOString(),
179
+ ...(restoreFrom ? { restoreFrom } : {}),
180
+ };
181
+ await writeState(layout, state);
182
+ reporter.progress(`Stopping ${from}`);
183
+ try {
184
+ await stopApp(service);
185
+ }
186
+ catch (error) {
187
+ // Nothing was restored or switched: put back what was pending before this run, if anything.
188
+ if (interrupted)
189
+ state.pending = interrupted;
190
+ else
191
+ delete state.pending;
192
+ await writeState(layout, state);
193
+ throw new InstallerError('ROLLBACK_ABORTED', `Stopping ${from} failed: ${error instanceof Error ? error.message : String(error)}. Nothing was restored or switched.`, { exitCode: EXIT_FAILED, cause: error });
194
+ }
195
+ let restored = false;
196
+ let switched = false;
197
+ let healthy = false;
198
+ let failure;
199
+ try {
200
+ if (restoreFrom) {
201
+ reporter.progress(`Restoring the databases from ${restoreFrom}`);
202
+ await restoreDatabase(layout, restoreFrom);
203
+ restored = true;
204
+ }
205
+ await switchCurrent(layout, releaseLinkTarget(target));
206
+ switched = true;
207
+ // The link and installer.json agree from here, whatever happens next.
208
+ state.current = target;
209
+ await writeState(layout, state);
210
+ reporter.progress(`Starting ${target}`);
211
+ healthy = await startApp({
212
+ ...service,
213
+ timeoutMs: flags['health-timeout'] * 1000,
214
+ });
215
+ }
216
+ catch (error) {
217
+ failure = error;
218
+ }
219
+ if (!healthy) {
220
+ // The rollback stays pending, so running it again retries it — restore included — once the cause is fixed.
221
+ await writeState(layout, state);
222
+ const reason = failure instanceof Error ? ` (${failure.message})` : '';
223
+ throw new InstallerError('ROLLBACK_FAILED', switched
224
+ ? `${target} did not come back${reason}. ${capitalize(subject)} is down.`
225
+ : `${restoreFrom && !restored ? 'Restoring the databases' : 'Switching to ' + target} failed${reason}; ${subject} is stopped and still on ${from}.`, {
226
+ exitCode: EXIT_ROLLBACK_FAILED,
227
+ details: {
228
+ log: await errorLogTail(layout),
229
+ databaseRestored: restored,
230
+ },
231
+ suggestions: [
232
+ {
233
+ message: 'Read the error log:',
234
+ run: errorLogCommandLine(layout),
235
+ },
236
+ {
237
+ message: 'Once the cause is fixed, run the rollback again:',
238
+ run: installerCommandLine(['rollback', '--dir', root], {
239
+ registry: state.registry,
240
+ }),
241
+ },
242
+ ...(switched
243
+ ? [
244
+ {
245
+ message: `Or go back to ${from}:`,
246
+ run: installerCommandLine(['rollback', '--dir', root, '--to', from, '--no-restore'], { registry: state.registry }),
247
+ },
248
+ ]
249
+ : []),
250
+ ],
251
+ });
252
+ }
253
+ delete state.pending;
254
+ state.history.push({
255
+ action: 'rollback',
256
+ from,
257
+ to: target,
258
+ at: new Date().toISOString(),
259
+ databaseRestored: restored,
260
+ });
261
+ await writeState(layout, state);
262
+ return {
263
+ status: 'success',
264
+ result: {
265
+ directory: root,
266
+ from,
267
+ to: target,
268
+ rolledBack: true,
269
+ recovered: interrupted ? interrupted.action : null,
270
+ databaseRestored: restored,
271
+ backup: restoreFrom ?? null,
272
+ },
273
+ summary: [
274
+ interrupted
275
+ ? `Recovered from the interrupted ${interrupted.action} (${interrupted.from} to ${interrupted.to}); ${subject} runs ${target}.`
276
+ : `Rolled ${subject} back from ${from} to ${target}.`,
277
+ restored
278
+ ? ` Databases restored from ${restoreFrom}`
279
+ : ' Databases left as they were',
280
+ ],
281
+ };
282
+ }
283
+ finally {
284
+ await releaseLock();
285
+ }
286
+ }
287
+ //# sourceMappingURL=rollback.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rollback.js","sourceRoot":"","sources":["../../src/commands/rollback.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,155 @@
1
+ import { lstat, readdir } from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import { Flags } from '@oclif/core';
4
+ import { readCurrent } from "../lib/current-link.js";
5
+ import { endpointsOf, healthUrl, mountPathOf, readAppEnv, } from "../lib/env-file.js";
6
+ import { checkHealth } from "../lib/health.js";
7
+ import { formatCommandLine, quoteForShell } from '@nocobase/cli-envelope';
8
+ import { installerCommand } from "../lib/invocation.js";
9
+ import { layoutOf, releaseDir } from "../lib/layout.js";
10
+ import { currentNodeMajor } from "../lib/prechecks.js";
11
+ import { resolveTemplateVersion } from "../lib/registry.js";
12
+ import { capitalize, nodeRebuildAdvice, subjectOf, templateOf, } from "../lib/source.js";
13
+ import { findRelease, readState } from "../lib/state.js";
14
+ export const STATUS_FLAGS = {
15
+ dir: Flags.string({
16
+ description: 'Installation root managed by app-installer. Defaults to the current directory.',
17
+ }),
18
+ offline: Flags.boolean({
19
+ default: false,
20
+ description: 'Skip asking the registry for a newer version of a template installation.',
21
+ }),
22
+ json: Flags.boolean({
23
+ default: false,
24
+ description: 'Print one JSON result on stdout.',
25
+ }),
26
+ };
27
+ /** Bytes used by a directory tree, not following symbolic links. */
28
+ export async function directorySize(dir) {
29
+ let total = 0;
30
+ const entries = await readdir(dir, { withFileTypes: true }).catch(() => []);
31
+ for (const entry of entries) {
32
+ const full = path.join(dir, entry.name);
33
+ if (entry.isDirectory()) {
34
+ total += await directorySize(full);
35
+ }
36
+ else if (entry.isFile()) {
37
+ total += (await lstat(full)).size;
38
+ }
39
+ }
40
+ return total;
41
+ }
42
+ function formatSize(bytes) {
43
+ return bytes >= 1024 ** 3
44
+ ? `${(bytes / 1024 ** 3).toFixed(1)} GB`
45
+ : `${Math.round(bytes / 1024 ** 2)} MB`;
46
+ }
47
+ export async function status(input, deps) {
48
+ const root = path.resolve(deps.cwd ?? process.cwd(), input.flags.dir ?? '.');
49
+ const layout = layoutOf(root);
50
+ const state = await readState(layout);
51
+ const subject = subjectOf(state);
52
+ const template = templateOf(state);
53
+ const env = await readAppEnv(layout);
54
+ const link = await readCurrent(layout);
55
+ const releases = [];
56
+ for (const record of state.releases) {
57
+ const dir = releaseDir(layout, record.id);
58
+ releases.push({
59
+ id: record.id,
60
+ version: record.version,
61
+ current: record.id === state.current,
62
+ builtAt: record.builtAt,
63
+ installedAt: record.installedAt,
64
+ nodeMajor: record.buildTarget.nodeMajor,
65
+ sizeBytes: await directorySize(dir),
66
+ });
67
+ }
68
+ const backups = (await readdir(layout.backupsDir).catch(() => [])).sort();
69
+ const endpoints = endpointsOf(env);
70
+ if (endpoints.port === null) {
71
+ deps.reporter.warn(`APP_SERVER_PORT in app.env is "${env.APP_SERVER_PORT ?? ''}", which is not a port number; ${subject} cannot listen on it.`);
72
+ }
73
+ const url = healthUrl(env);
74
+ const healthy = await checkHealth(url, deps.fetchImpl);
75
+ let processInfo = null;
76
+ try {
77
+ processInfo = (await deps.pm2.describe(state.name)) ?? null;
78
+ }
79
+ catch {
80
+ deps.reporter.warn('pm2 could not be queried; process state is unknown.');
81
+ }
82
+ if (state.pending) {
83
+ deps.reporter.warn(`${state.pending.action === 'upgrade' ? 'An' : 'A'} ${state.pending.action} from ${state.pending.from} to ${state.pending.to}, started ${state.pending.startedAt}, did not finish. Recover with \`${installerCommand(`rollback --dir ${quoteForShell(root)}`, { registry: state.registry })}\`: it undoes an interrupted upgrade and finishes an interrupted rollback.`);
84
+ }
85
+ const currentRelease = findRelease(state, state.current);
86
+ const nodeMajor = currentNodeMajor();
87
+ const nodeMatches = currentRelease?.buildTarget.nodeMajor === nodeMajor;
88
+ if (!nodeMatches) {
89
+ const advice = nodeRebuildAdvice(state, root)
90
+ .map((step) => step.run
91
+ ? `${step.message} \`${formatCommandLine(step.run)}\``
92
+ : step.message)
93
+ .join(' ');
94
+ deps.reporter.warn(`The current release was built for Node ${currentRelease?.buildTarget.nodeMajor ?? '?'}, but this machine runs Node ${nodeMajor}; it will not load its native modules until a release built for this machine replaces it. ${advice}`);
95
+ }
96
+ let latest = null;
97
+ if (template && !input.flags.offline) {
98
+ try {
99
+ latest = await resolveTemplateVersion(state.registry, template.package, 'latest', deps.fetchImpl);
100
+ }
101
+ catch {
102
+ deps.reporter.warn(`Could not ask ${state.registry} for the latest version.`);
103
+ }
104
+ }
105
+ return {
106
+ status: 'success',
107
+ result: {
108
+ directory: root,
109
+ name: state.name,
110
+ appName: state.appName,
111
+ // Where the application is mounted now, as app.env says; installer.json keeps the path it was installed at.
112
+ basePath: mountPathOf(env) || '/',
113
+ source: state.source,
114
+ current: state.current,
115
+ version: currentRelease?.version ?? null,
116
+ builtAt: currentRelease?.builtAt ?? null,
117
+ currentLink: link ?? null,
118
+ dialect: state.dialect,
119
+ registry: state.registry,
120
+ endpoints,
121
+ releases,
122
+ backups,
123
+ health: { url, ok: healthy },
124
+ process: processInfo,
125
+ node: {
126
+ machine: nodeMajor,
127
+ release: currentRelease?.buildTarget.nodeMajor ?? null,
128
+ matches: nodeMatches,
129
+ },
130
+ pending: state.pending ?? null,
131
+ latest,
132
+ updateAvailable: latest === null ? null : latest !== currentRelease?.version,
133
+ },
134
+ summary: [
135
+ `${capitalize(subject)} ${state.appName} ${currentRelease?.version ?? '?'} at ${root}`,
136
+ ` Release ${state.current}${currentRelease ? `, built ${currentRelease.builtAt}` : ''}`,
137
+ ` Source ${template ? `the ${template.name} template` : 'deployment archives'}`,
138
+ ` URL ${endpoints.url} (listening on ${endpoints.host}:${endpoints.port ?? `invalid port "${env.APP_SERVER_PORT ?? ''}"`})`,
139
+ ` Health ${healthy ? 'ok' : 'not answering'} (${url})`,
140
+ ` Process ${processInfo ? `${processInfo.status}, pid ${processInfo.pid}, ${processInfo.restarts} restarts` : 'not registered with pm2'} (${state.name})`,
141
+ ` Node machine ${nodeMajor}, release ${currentRelease?.buildTarget.nodeMajor ?? '?'}${nodeMatches ? '' : ' — mismatch'}`,
142
+ ...(latest === null
143
+ ? []
144
+ : [
145
+ ` Latest ${latest}${latest === currentRelease?.version ? ' (installed)' : ' (update available)'}`,
146
+ ]),
147
+ ' Releases',
148
+ ...releases.map((release) => ` ${release.current ? '*' : ' '} ${release.id} ${formatSize(release.sizeBytes)} installed ${release.installedAt}`),
149
+ ...(backups.length > 0
150
+ ? [' Backups', ...backups.map((backup) => ` ${backup}`)]
151
+ : []),
152
+ ],
153
+ };
154
+ }
155
+ //# sourceMappingURL=status.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"status.js","sourceRoot":"","sources":["../../src/commands/status.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AACpC,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,EACL,WAAW,EACX,SAAS,EACT,WAAW,EACX,UAAU,GACX,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAC1E,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AACxD,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACxD,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAC;AAC5D,OAAO,EACL,UAAU,EACV,iBAAiB,EACjB,SAAS,EACT,UAAU,GACX,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAGzD,MAAM,CAAC,MAAM,YAAY,GAAG;IAC1B,GAAG,EAAE,KAAK,CAAC,MAAM,CAAC;QAChB,WAAW,EACT,gFAAgF;KACnF,CAAC;IACF,OAAO,EAAE,KAAK,CAAC,OAAO,CAAC;QACrB,OAAO,EAAE,KAAK;QACd,WAAW,EACT,0EAA0E;KAC7E,CAAC;IACF,IAAI,EAAE,KAAK,CAAC,OAAO,CAAC;QAClB,OAAO,EAAE,KAAK;QACd,WAAW,EAAE,kCAAkC;KAChD,CAAC;CACH,CAAC;AAMF,oEAAoE;AACpE,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,GAAW;IAC7C,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,GAAG,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC;IAC5E,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;QACxC,IAAI,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC;YACxB,KAAK,IAAI,MAAM,aAAa,CAAC,IAAI,CAAC,CAAC;QACrC,CAAC;aAAM,IAAI,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC;YAC1B,KAAK,IAAI,CAAC,MAAM,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;QACpC,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,UAAU,CAAC,KAAa;IAC/B,OAAO,KAAK,IAAI,IAAI,IAAI,CAAC;QACvB,CAAC,CAAC,GAAG,CAAC,KAAK,GAAG,IAAI,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK;QACxC,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,IAAI,IAAI,CAAC,CAAC,KAAK,CAAC;AAC5C,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,MAAM,CAC1B,KAAkB,EAClB,IAAiB;IAEjB,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,EAAE,EAAE,KAAK,CAAC,KAAK,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC;IAC7E,MAAM,MAAM,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;IAC9B,MAAM,KAAK,GAAG,MAAM,SAAS,CAAC,MAAM,CAAC,CAAC;IACtC,MAAM,OAAO,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC;IACjC,MAAM,QAAQ,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC;IACnC,MAAM,GAAG,GAAG,MAAM,UAAU,CAAC,MAAM,CAAC,CAAC;IACrC,MAAM,IAAI,GAAG,MAAM,WAAW,CAAC,MAAM,CAAC,CAAC;IAEvC,MAAM,QAAQ,GAAG,EAAE,CAAC;IACpB,KAAK,MAAM,MAAM,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAC;QACpC,MAAM,GAAG,GAAG,UAAU,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC;QAC1C,QAAQ,CAAC,IAAI,CAAC;YACZ,EAAE,EAAE,MAAM,CAAC,EAAE;YACb,OAAO,EAAE,MAAM,CAAC,OAAO;YACvB,OAAO,EAAE,MAAM,CAAC,EAAE,KAAK,KAAK,CAAC,OAAO;YACpC,OAAO,EAAE,MAAM,CAAC,OAAO;YACvB,WAAW,EAAE,MAAM,CAAC,WAAW;YAC/B,SAAS,EAAE,MAAM,CAAC,WAAW,CAAC,SAAS;YACvC,SAAS,EAAE,MAAM,aAAa,CAAC,GAAG,CAAC;SACpC,CAAC,CAAC;IACL,CAAC;IACD,MAAM,OAAO,GAAG,CACd,MAAM,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAc,CAAC,CAC7D,CAAC,IAAI,EAAE,CAAC;IAET,MAAM,SAAS,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC;IACnC,IAAI,SAAS,CAAC,IAAI,KAAK,IAAI,EAAE,CAAC;QAC5B,IAAI,CAAC,QAAQ,CAAC,IAAI,CAChB,kCAAkC,GAAG,CAAC,eAAe,IAAI,EAAE,kCAAkC,OAAO,uBAAuB,CAC5H,CAAC;IACJ,CAAC;IACD,MAAM,GAAG,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC;IAC3B,MAAM,OAAO,GAAG,MAAM,WAAW,CAAC,GAAG,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;IAEvD,IAAI,WAAW,GAAyD,IAAI,CAAC;IAC7E,IAAI,CAAC;QACH,WAAW,GAAG,CAAC,MAAM,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,IAAI,CAAC;IAC9D,CAAC;IAAC,MAAM,CAAC;QACP,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,qDAAqD,CAAC,CAAC;IAC5E,CAAC;IAED,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;QAClB,IAAI,CAAC,QAAQ,CAAC,IAAI,CAChB,GAAG,KAAK,CAAC,OAAO,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,SAAS,KAAK,CAAC,OAAO,CAAC,IAAI,OAAO,KAAK,CAAC,OAAO,CAAC,EAAE,aAAa,KAAK,CAAC,OAAO,CAAC,SAAS,oCAAoC,gBAAgB,CAAC,kBAAkB,aAAa,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,QAAQ,EAAE,KAAK,CAAC,QAAQ,EAAE,CAAC,4EAA4E,CACxW,CAAC;IACJ,CAAC;IAED,MAAM,cAAc,GAAG,WAAW,CAAC,KAAK,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;IACzD,MAAM,SAAS,GAAG,gBAAgB,EAAE,CAAC;IACrC,MAAM,WAAW,GAAG,cAAc,EAAE,WAAW,CAAC,SAAS,KAAK,SAAS,CAAC;IACxE,IAAI,CAAC,WAAW,EAAE,CAAC;QACjB,MAAM,MAAM,GAAG,iBAAiB,CAAC,KAAK,EAAE,IAAI,CAAC;aAC1C,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CACZ,IAAI,CAAC,GAAG;YACN,CAAC,CAAC,GAAG,IAAI,CAAC,OAAO,MAAM,iBAAiB,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI;YACtD,CAAC,CAAC,IAAI,CAAC,OAAO,CACjB;aACA,IAAI,CAAC,GAAG,CAAC,CAAC;QACb,IAAI,CAAC,QAAQ,CAAC,IAAI,CAChB,0CAA0C,cAAc,EAAE,WAAW,CAAC,SAAS,IAAI,GAAG,gCAAgC,SAAS,6FAA6F,MAAM,EAAE,CACrO,CAAC;IACJ,CAAC;IAED,IAAI,MAAM,GAAkB,IAAI,CAAC;IACjC,IAAI,QAAQ,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC;QACrC,IAAI,CAAC;YACH,MAAM,GAAG,MAAM,sBAAsB,CACnC,KAAK,CAAC,QAAQ,EACd,QAAQ,CAAC,OAAO,EAChB,QAAQ,EACR,IAAI,CAAC,SAAS,CACf,CAAC;QACJ,CAAC;QAAC,MAAM,CAAC;YACP,IAAI,CAAC,QAAQ,CAAC,IAAI,CAChB,iBAAiB,KAAK,CAAC,QAAQ,0BAA0B,CAC1D,CAAC;QACJ,CAAC;IACH,CAAC;IAED,OAAO;QACL,MAAM,EAAE,SAAS;QACjB,MAAM,EAAE;YACN,SAAS,EAAE,IAAI;YACf,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,OAAO,EAAE,KAAK,CAAC,OAAO;YACtB,4GAA4G;YAC5G,QAAQ,EAAE,WAAW,CAAC,GAAG,CAAC,IAAI,GAAG;YACjC,MAAM,EAAE,KAAK,CAAC,MAAM;YACpB,OAAO,EAAE,KAAK,CAAC,OAAO;YACtB,OAAO,EAAE,cAAc,EAAE,OAAO,IAAI,IAAI;YACxC,OAAO,EAAE,cAAc,EAAE,OAAO,IAAI,IAAI;YACxC,WAAW,EAAE,IAAI,IAAI,IAAI;YACzB,OAAO,EAAE,KAAK,CAAC,OAAO;YACtB,QAAQ,EAAE,KAAK,CAAC,QAAQ;YACxB,SAAS;YACT,QAAQ;YACR,OAAO;YACP,MAAM,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE,OAAO,EAAE;YAC5B,OAAO,EAAE,WAAW;YACpB,IAAI,EAAE;gBACJ,OAAO,EAAE,SAAS;gBAClB,OAAO,EAAE,cAAc,EAAE,WAAW,CAAC,SAAS,IAAI,IAAI;gBACtD,OAAO,EAAE,WAAW;aACrB;YACD,OAAO,EAAE,KAAK,CAAC,OAAO,IAAI,IAAI;YAC9B,MAAM;YACN,eAAe,EACb,MAAM,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,KAAK,cAAc,EAAE,OAAO;SAC9D;QACD,OAAO,EAAE;YACP,GAAG,UAAU,CAAC,OAAO,CAAC,IAAI,KAAK,CAAC,OAAO,IAAI,cAAc,EAAE,OAAO,IAAI,GAAG,OAAO,IAAI,EAAE;YACtF,eAAe,KAAK,CAAC,OAAO,GAAG,cAAc,CAAC,CAAC,CAAC,WAAW,cAAc,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE;YAC1F,eAAe,QAAQ,CAAC,CAAC,CAAC,OAAO,QAAQ,CAAC,IAAI,WAAW,CAAC,CAAC,CAAC,qBAAqB,EAAE;YACnF,eAAe,SAAS,CAAC,GAAG,kBAAkB,SAAS,CAAC,IAAI,IAAI,SAAS,CAAC,IAAI,IAAI,iBAAiB,GAAG,CAAC,eAAe,IAAI,EAAE,GAAG,GAAG;YAClI,eAAe,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,eAAe,KAAK,GAAG,GAAG;YAC1D,eAAe,WAAW,CAAC,CAAC,CAAC,GAAG,WAAW,CAAC,MAAM,SAAS,WAAW,CAAC,GAAG,KAAK,WAAW,CAAC,QAAQ,WAAW,CAAC,CAAC,CAAC,yBAAyB,KAAK,KAAK,CAAC,IAAI,GAAG;YAC5J,uBAAuB,SAAS,aAAa,cAAc,EAAE,WAAW,CAAC,SAAS,IAAI,GAAG,GAAG,WAAW,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,aAAa,EAAE;YAC9H,GAAG,CAAC,MAAM,KAAK,IAAI;gBACjB,CAAC,CAAC,EAAE;gBACJ,CAAC,CAAC;oBACE,eAAe,MAAM,GAAG,MAAM,KAAK,cAAc,EAAE,OAAO,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,qBAAqB,EAAE;iBACtG,CAAC;YACN,YAAY;YACZ,GAAG,QAAQ,CAAC,GAAG,CACb,CAAC,OAAO,EAAE,EAAE,CACV,OAAO,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,IAAI,OAAO,CAAC,EAAE,KAAK,UAAU,CAAC,OAAO,CAAC,SAAS,CAAC,eAAe,OAAO,CAAC,WAAW,EAAE,CACzH;YACD,GAAG,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC;gBACpB,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,OAAO,MAAM,EAAE,CAAC,CAAC;gBAC5D,CAAC,CAAC,EAAE,CAAC;SACR;KACF,CAAC;AACJ,CAAC"}