@spinajs/orm-cli 2.0.487

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 (70) hide show
  1. package/README.md +247 -0
  2. package/lib/cjs/cli/MigrateCreate.d.ts +59 -0
  3. package/lib/cjs/cli/MigrateCreate.d.ts.map +1 -0
  4. package/lib/cjs/cli/MigrateCreate.js +211 -0
  5. package/lib/cjs/cli/MigrateCreate.js.map +1 -0
  6. package/lib/cjs/cli/MigrateDown.d.ts +23 -0
  7. package/lib/cjs/cli/MigrateDown.d.ts.map +1 -0
  8. package/lib/cjs/cli/MigrateDown.js +74 -0
  9. package/lib/cjs/cli/MigrateDown.js.map +1 -0
  10. package/lib/cjs/cli/MigrateResolve.d.ts +19 -0
  11. package/lib/cjs/cli/MigrateResolve.d.ts.map +1 -0
  12. package/lib/cjs/cli/MigrateResolve.js +66 -0
  13. package/lib/cjs/cli/MigrateResolve.js.map +1 -0
  14. package/lib/cjs/cli/MigrateStatus.d.ts +19 -0
  15. package/lib/cjs/cli/MigrateStatus.d.ts.map +1 -0
  16. package/lib/cjs/cli/MigrateStatus.js +109 -0
  17. package/lib/cjs/cli/MigrateStatus.js.map +1 -0
  18. package/lib/cjs/cli/MigrateUp.d.ts +33 -0
  19. package/lib/cjs/cli/MigrateUp.d.ts.map +1 -0
  20. package/lib/cjs/cli/MigrateUp.js +92 -0
  21. package/lib/cjs/cli/MigrateUp.js.map +1 -0
  22. package/lib/cjs/config/orm-cli.d.ts +9 -0
  23. package/lib/cjs/config/orm-cli.d.ts.map +1 -0
  24. package/lib/cjs/config/orm-cli.js +26 -0
  25. package/lib/cjs/config/orm-cli.js.map +1 -0
  26. package/lib/cjs/index.d.ts +18 -0
  27. package/lib/cjs/index.d.ts.map +1 -0
  28. package/lib/cjs/index.js +34 -0
  29. package/lib/cjs/index.js.map +1 -0
  30. package/lib/cjs/orm.d.ts +31 -0
  31. package/lib/cjs/orm.d.ts.map +1 -0
  32. package/lib/cjs/orm.js +38 -0
  33. package/lib/cjs/orm.js.map +1 -0
  34. package/lib/cjs/package.json +1 -0
  35. package/lib/mjs/cli/MigrateCreate.d.ts +59 -0
  36. package/lib/mjs/cli/MigrateCreate.d.ts.map +1 -0
  37. package/lib/mjs/cli/MigrateCreate.js +173 -0
  38. package/lib/mjs/cli/MigrateCreate.js.map +1 -0
  39. package/lib/mjs/cli/MigrateDown.d.ts +23 -0
  40. package/lib/mjs/cli/MigrateDown.d.ts.map +1 -0
  41. package/lib/mjs/cli/MigrateDown.js +71 -0
  42. package/lib/mjs/cli/MigrateDown.js.map +1 -0
  43. package/lib/mjs/cli/MigrateResolve.d.ts +19 -0
  44. package/lib/mjs/cli/MigrateResolve.d.ts.map +1 -0
  45. package/lib/mjs/cli/MigrateResolve.js +63 -0
  46. package/lib/mjs/cli/MigrateResolve.js.map +1 -0
  47. package/lib/mjs/cli/MigrateStatus.d.ts +19 -0
  48. package/lib/mjs/cli/MigrateStatus.d.ts.map +1 -0
  49. package/lib/mjs/cli/MigrateStatus.js +106 -0
  50. package/lib/mjs/cli/MigrateStatus.js.map +1 -0
  51. package/lib/mjs/cli/MigrateUp.d.ts +33 -0
  52. package/lib/mjs/cli/MigrateUp.d.ts.map +1 -0
  53. package/lib/mjs/cli/MigrateUp.js +89 -0
  54. package/lib/mjs/cli/MigrateUp.js.map +1 -0
  55. package/lib/mjs/config/orm-cli.d.ts +9 -0
  56. package/lib/mjs/config/orm-cli.d.ts.map +1 -0
  57. package/lib/mjs/config/orm-cli.js +24 -0
  58. package/lib/mjs/config/orm-cli.js.map +1 -0
  59. package/lib/mjs/index.d.ts +18 -0
  60. package/lib/mjs/index.d.ts.map +1 -0
  61. package/lib/mjs/index.js +18 -0
  62. package/lib/mjs/index.js.map +1 -0
  63. package/lib/mjs/orm.d.ts +31 -0
  64. package/lib/mjs/orm.d.ts.map +1 -0
  65. package/lib/mjs/orm.js +34 -0
  66. package/lib/mjs/orm.js.map +1 -0
  67. package/lib/mjs/package.json +1 -0
  68. package/lib/tsconfig.cjs.tsbuildinfo +1 -0
  69. package/lib/tsconfig.mjs.tsbuildinfo +1 -0
  70. package/package.json +68 -0
package/README.md ADDED
@@ -0,0 +1,247 @@
1
+ # @spinajs/orm-cli
2
+
3
+ Command line front end for spinajs ORM migrations. Five commands — apply, roll back, report,
4
+ force a state, scaffold — over the `orm.Migration` facade in `@spinajs/orm`.
5
+
6
+ The package is a thin wrapper on purpose. Everything that decides what a migration run means
7
+ lives in `@spinajs/orm`; what lives here is the argument handling, the operator-facing wording
8
+ and the exit codes. The dependency runs one way only — `orm-cli` → `orm` — so the ORM stays
9
+ usable, and testable, with no CLI in its dependency tree.
10
+
11
+ ## Install
12
+
13
+ ```bash
14
+ npm i @spinajs/orm-cli
15
+ ```
16
+
17
+ The package ships a config fragment that appends its own command directory to
18
+ `system.dirs.cli`, which is where `@spinajs/cli` looks for commands. Installing it is therefore
19
+ enough — `spinajs migrate-status` works with no import and no wiring on your side. If your
20
+ application builds its own command list instead, `import '@spinajs/orm-cli'` is all that is
21
+ needed: `@Command` registers each class in DI the moment the module is evaluated.
22
+
23
+ The commands are also plain DI classes, so a script can drive them without commander:
24
+
25
+ ```ts
26
+ import { DI } from '@spinajs/di';
27
+ import { MigrateStatusCommand } from '@spinajs/orm-cli';
28
+
29
+ await (await DI.resolve(MigrateStatusCommand)).execute();
30
+ ```
31
+
32
+ ## Running a command never migrates anything
33
+
34
+ Every command starts by resolving an `Orm`, and an ordinary `DI.resolve(Orm)` ends with the boot
35
+ migration pass — every pending migration on every connection whose `Migration.OnStartup` is on.
36
+ For an application that is the point. For a migration tool it is a trap, twice over:
37
+
38
+ - a connection holding a **failed** migration refuses every migration run, so the resolve throws
39
+ before the command body starts. That took down every command on the row it was invoked about,
40
+ including `migrate-resolve` — the one command that clears it, and the one the refusal names as
41
+ the remedy.
42
+ - `migrate-status` would apply everything pending and only then report, so the deploy gate asking
43
+ "is this database current?" made it current, answered "yes" and exited `0`, with the DDL it was
44
+ meant to hold back already run.
45
+
46
+ So the commands resolve their Orm through `resolveCliOrm()`, which passes `MigrateOnStartup:
47
+ false` (an `IOrmOptions` field of `@spinajs/orm`). Everything else about resolving happens —
48
+ connections, models, value converters, `orm.Migration` — only the boot pass is skipped. It is
49
+ opt-**in**: nothing changes for an application that resolves an Orm the ordinary way, and this
50
+ package ships no configuration that would switch startup migrations off for anybody.
51
+
52
+ Two consequences worth knowing:
53
+
54
+ - `migrate-up --fake` means what it says on a `Migration.OnStartup` connection. A boot pass would
55
+ have really applied the migrations the flag promises only to record.
56
+ - **A migration applied by the CLI never gets its `data()` hook.** Seeding belongs to the boot
57
+ pass: `Orm.resolve()` seeds what its own startup run applied, and a later boot finds the
58
+ migration already applied and seeds nothing. That was already true of every connection with
59
+ `Migration.OnStartup` off; it is now true of all of them. Migrations that must be seeded have to
60
+ be applied by an application boot, not by `migrate-up`.
61
+
62
+ ## Commands
63
+
64
+ | Command | Options | Does |
65
+ | --- | --- | --- |
66
+ | `migrate-up` | `-n, --name [name]`, `-c, --connection [connection]`, `-f, --fake` | Applies pending migrations on every configured connection |
67
+ | `migrate-down` | `-n, --name [name]`, `-c, --connection [connection]`, `-a, --all`, `-f, --fake` | Rolls back — **the last applied batch only** unless `--all` |
68
+ | `migrate-status` | — | Prints one line per migration per connection; the deploy gate |
69
+ | `migrate-resolve` | `-n, --name [name]` (required), `--applied`, `--rolled-back` | Records the outcome of a FAILED migration |
70
+ | `migrate-create` | `-n, --name [name]` (required), `-d, --dir [dir]`, `-c, --connection [connection]` | Scaffolds a migration file |
71
+
72
+ ### `migrate-up`
73
+
74
+ ```bash
75
+ spinajs migrate-up
76
+ spinajs migrate-up --name AddUserTable_2026_07_29_10_00_00
77
+ spinajs migrate-up --connection reporting # this connection only
78
+ spinajs migrate-up --fake # record as applied without running anything
79
+ ```
80
+
81
+ Without `--name` it applies everything pending, in `(timestamp, name)` order, across every
82
+ configured connection. With `--name` it applies exactly that one.
83
+
84
+ `--connection` limits the run to one connection. Every other configured connection is left
85
+ completely untouched — its migration service is never reached, so its tracking table is not even
86
+ created. The name is matched against the configured connections (aliases included, since they
87
+ resolve to the same connection), and one nothing answers to **throws** rather than running
88
+ nothing: a filter that silently matched nothing would exit `0` reporting "0 migrations applied".
89
+
90
+ Two named-run outcomes are deliberately **not** reported as success:
91
+
92
+ - the name matches nothing in the registry — the facade throws rather than returning an empty
93
+ list, because "0 migrations applied" from a typo is indistinguishable from "already current";
94
+ - the name is registered but the connection it declares is not configured in this deployment.
95
+ The facade only warns and returns `[]` there, so this command checks `status()` afterwards and
96
+ exits non-zero with an explanation.
97
+
98
+ ### `migrate-down`
99
+
100
+ ```bash
101
+ spinajs migrate-down # the LAST APPLIED BATCH, not everything
102
+ spinajs migrate-down --all # every applied migration, on every connection
103
+ spinajs migrate-down --name AddUserTable_2026_07_29_10_00_00
104
+ spinajs migrate-down --connection reporting --all # everything, on one connection
105
+ ```
106
+
107
+ The default scope is the last applied batch — one `migrate-up` run undone, not the whole
108
+ history. `--all` reverses everything. `--connection` narrows whichever of those two applies, and
109
+ is announced first for that reason: `--all --connection reporting` is "every applied migration on
110
+ *one* connection". The command says which scope it is about to reverse *before* it does it,
111
+ because by the time the result line prints, the schema has already changed.
112
+
113
+ A rollback drops the tracking row rather than stamping it "rolled back": the table is meant to
114
+ hold only migrations that are actually present in the database, and both a missing row and a
115
+ rolled-back one read as pending to the next `migrate-up`.
116
+
117
+ `--name` has a known sharp edge in the migration service: it is handed a one-element unit list,
118
+ so every *other* applied row in the target batch looks unmatched and gets warned about as
119
+ "no registered migration matches them (file deleted or renamed)". Those rows are healthy, and
120
+ the remedy that warning suggests — removing the row by hand — is destructive here. This command
121
+ prints a line saying exactly that before the run, so the warnings can be ignored.
122
+
123
+ ### `migrate-status`
124
+
125
+ ```bash
126
+ spinajs migrate-status
127
+ ```
128
+
129
+ ```
130
+ STATE BATCH CONNECTION MIGRATION
131
+ applied 1 default AddUserTable_2026_07_29_10_00_00
132
+ !! FAILED 0 default AddOrderIndex_2026_07_29_11_00_00
133
+ ?? INTERRUPTED 0 default BackfillTotals_2026_07_29_12_00_00
134
+ pending - default AddInvoices_2026_07_30_09_00_00
135
+ ```
136
+
137
+ Output goes to stdout via `console.log`, not through the framework logger: it is this command's
138
+ *product*, something an operator greps and a script pipes, and routing it through the log would
139
+ let a configured level or target swallow it.
140
+
141
+ A failed row carries `!!` in the leftmost column, not just the word `FAILED`. That row is the
142
+ one line in the report that stops every later `migrate-up` on its connection, and it has to
143
+ survive being skimmed in a wall of `applied`. Below the table the command prints the two exact
144
+ `migrate-resolve` invocations for each failed migration.
145
+
146
+ `??` marks an **interrupted** migration — one that was started and never finished, because the
147
+ process running it was killed before it could record either outcome. It carries the opposite
148
+ warning to `FAILED`: it blocks nothing, and the next `migrate-up` re-runs it from the top, whether
149
+ or not anybody looked. Under the default `Transaction.Mode: None` that means non-idempotent data
150
+ changes get applied twice, silently. The same two `migrate-resolve` invocations are printed for
151
+ it. See "Interrupted runs" in
152
+ [the ORM migration docs](../orm/docs/10-schema-and-migrations.md#interrupted-runs).
153
+
154
+ `[checksum mismatch]` marks a migration whose source changed after it was applied. It is
155
+ reported but does **not** on its own make the command exit non-zero — only pending and failed
156
+ work do.
157
+
158
+ ### `migrate-resolve`
159
+
160
+ The escape hatch for a run that died halfway. Valid on the two row shapes whose real outcome
161
+ nobody recorded — **failed** (`FinishedAt` NULL and `Logs` set) and **interrupted** (`StartedAt`
162
+ set, `FinishedAt` and `Logs` both NULL). Anything healthy, rolled back or absent is refused rather
163
+ than silently rewritten.
164
+
165
+ ```bash
166
+ spinajs migrate-resolve --name AddOrderIndex_2026_07_29_11_00_00 --applied # the change IS in the database
167
+ spinajs migrate-resolve --name AddOrderIndex_2026_07_29_11_00_00 --rolled-back # the change is NOT
168
+ ```
169
+
170
+ Exactly one of the two flags, never both and never neither: the point of the command is to state
171
+ which of the two things actually happened, and neither the CLI nor the ORM can find that out on
172
+ its own. The refusal happens before any Orm is resolved, so a malformed command line never opens
173
+ a database connection.
174
+
175
+ `--rolled-back` makes the migration pending again — it *will* run on the next `migrate-up`.
176
+
177
+ ### `migrate-create`
178
+
179
+ ```bash
180
+ spinajs migrate-create --name AddInvoices
181
+ spinajs migrate-create --name AddInvoices --dir ./src/migrations --connection reporting
182
+ ```
183
+
184
+ Prints the path it wrote, on its own line, so `$(spinajs migrate-create -n AddInvoices)` is
185
+ usable. Defaults: `./src/migrations` and the `default` connection.
186
+
187
+ `--name` takes the *prefix* only, letters and digits, starting with a letter. The
188
+ `_yyyy_MM_dd_HH_mm_ss` suffix is appended here, and it is not decoration: that timestamp is the
189
+ only ordering the migration runner has, and it is read back out of the class name. A name the
190
+ runner cannot parse is refused up front, and an existing file is never overwritten.
191
+
192
+ The generated class only takes effect once it is *imported* — the `@Migration` decorator has to
193
+ run to register it. Re-export it from your package or application index, the way `src/migrations/*.ts`
194
+ files are re-exported elsewhere in spinajs.
195
+
196
+ ## Exit codes
197
+
198
+ | Command | `0` | non-zero |
199
+ | --- | --- | --- |
200
+ | `migrate-up` | migrations applied, or nothing was pending | a named run applied nothing because its connection is not configured, or it is still pending/failed; a `--connection` nothing answers to; any error from the run |
201
+ | `migrate-down` | rollback completed, or nothing to roll back | a `--connection` nothing answers to; any error from the run |
202
+ | `migrate-status` | every migration is applied | anything is pending or failed |
203
+ | `migrate-resolve` | the state was recorded | both/neither flag given; the row is neither failed nor interrupted |
204
+ | `migrate-create` | file written | invalid name or connection; the file already exists |
205
+
206
+ `migrate-status` is meant to be a deploy gate — "is this database current?" — so an un-run
207
+ migration is a "no", not just a failed one.
208
+
209
+ Two things the table does not say:
210
+
211
+ - **A `0` from `migrate-status` means "nothing is pending", not "the database is reachable and
212
+ configured".** With no connections configured, nothing is registered, so nothing is pending and
213
+ the command exits `0`. A gate that must also catch a failed config should check that the command
214
+ reported migrations at all.
215
+ - **Requires a `@spinajs/cli` that propagates `process.exitCode`.** Earlier versions ended the
216
+ bin's success path with a bare `process.exit(0)`, which discards whatever a command set — driven
217
+ through such a bin, `migrate-status` exits `0` even with pending work. If you are pinned to one,
218
+ call the command class directly (see the snippet at the top) rather than going through the bin.
219
+
220
+ ## The blocking guarantee is best-effort
221
+
222
+ A failed migration blocks every later `migrate-up` on its connection. That is what makes
223
+ `migrate-status` + `migrate-resolve` a safe recovery loop instead of a suggestion: a half-applied
224
+ schema change cannot be built on top of.
225
+
226
+ The guarantee holds only as far as the bookkeeping does. When a migration fails, the ORM writes
227
+ the failure into the tracking table — and if *that* write fails too (the connection dropped, the
228
+ table is locked), the error is caught and logged rather than raised. The run still fails, but the
229
+ row that would have blocked the next `migrate-up` was never written, and the next run proceeds as
230
+ if nothing had happened.
231
+
232
+ In practice this needs the database to fail twice, in a specific order. It matters when you are
233
+ reading logs after an incident: a `migrate-up` that succeeded shortly after a failed one is not
234
+ by itself proof that the failure was resolved. Check `migrate-status`.
235
+
236
+ ## Notes
237
+
238
+ - Migrations run against a schema no model is wired to yet. Use the `OrmDriver` passed to `up()`,
239
+ never a model class. The `data()` hook runs later, once models are available.
240
+ - `--fake` records the outcome without executing anything, on both `migrate-up` and
241
+ `migrate-down`. It is for a database that was changed out of band and needs the tracking table
242
+ brought in line.
243
+ - `migrate-status` reports every configured connection, including ones whose
244
+ `Migration.OnStartup` is off — hiding those would answer "nothing to see" for exactly the
245
+ connections somebody is most likely asking about. It has no `--connection` of its own, for the
246
+ same reason: the report is the deploy gate, and a gate that can be narrowed is a gate that can
247
+ be talked past.
@@ -0,0 +1,59 @@
1
+ import { CliCommand } from '@spinajs/cli';
2
+ import { Log } from '@spinajs/log-common';
3
+ export interface IMigrateCreateCommandOptions {
4
+ name: string;
5
+ dir?: string;
6
+ connection?: string;
7
+ env?: string;
8
+ }
9
+ /**
10
+ * The prefix half of `Prefix_yyyy_MM_dd_HH_mm_ss`. Deliberately narrower than what a TS class
11
+ * name allows: underscores and digits are how the runner finds the timestamp - `MIGRATION_FILE_REGEXP`
12
+ * splits on the LAST `_yyyy_MM_dd_HH_mm_ss`-shaped run - so a prefix that carries its own
13
+ * underscore-digit groups is a name nobody can read back with confidence.
14
+ */
15
+ export declare const MIGRATION_NAME_REGEXP: RegExp;
16
+ /**
17
+ * The connection name is interpolated into `@Migration('...')`, so anything that could close that
18
+ * string literal has to be refused here rather than emitted into a file that will not parse.
19
+ */
20
+ export declare const CONNECTION_NAME_REGEXP: RegExp;
21
+ /**
22
+ * The env tag becomes a dot-segment in the file name AND a string literal inside `@Migration()`,
23
+ * so it may carry neither a dot ( which would read as a second tag ) nor anything that could close
24
+ * that literal.
25
+ */
26
+ export declare const ENV_NAME_REGEXP: RegExp;
27
+ /**
28
+ * Reads `--env` directly off `process.argv`, deliberately more flexible than `Configuration`'s own
29
+ * `parseArgv` ( `packages/configuration/src/util.ts` - not exported from that package's public surface,
30
+ * so this logic is duplicated rather than imported ).
31
+ *
32
+ * `parseArgv` handles only the space-separated form ( `--env local` ), but this helper also accepts
33
+ * the equals form ( `--env=local` ). This is correct here: `packages/cli/src/args.ts` strips both
34
+ * forms from commander's argv, because `Configuration` consumes the framework-level `--env` directly.
35
+ * With `--env=local`, the value is stripped but `Configuration`'s `parseArgv` cannot recognize the
36
+ * equals form, so the CLI process boots under the default environment while the scaffolded file gets
37
+ * its `.local` suffix — harmless for this command, which uses the value only as a filename tag and
38
+ * a decorator string. `-e` is untouched by the strip and reaches commander normally, so it needs no
39
+ * duplicate handling here.
40
+ */
41
+ export declare function parseEnvArgv(argv?: string[]): string | undefined;
42
+ export declare const DEFAULT_MIGRATION_DIR = "./src/migrations";
43
+ export declare const DEFAULT_MIGRATION_CONNECTION = "default";
44
+ /**
45
+ * The scaffold. `connection` is named ( rather than `_connection` ) in both hooks because both
46
+ * bodies are meant to be filled in immediately - and the eslint pragma on the first line is the
47
+ * repo's own convention for a migration whose `down()` legitimately ignores it.
48
+ */
49
+ export declare function migrationTemplate(cls: string, connection: string, env?: string): string;
50
+ /**
51
+ * The one command here that needs no database and no Orm: it writes a file. Keeping it free of
52
+ * `DI.resolve(Orm)` means a developer can scaffold a migration in a checkout whose connections
53
+ * are not configured, or not reachable, which is exactly when new migrations get written.
54
+ */
55
+ export declare class MigrateCreateCommand extends CliCommand {
56
+ protected Log: Log;
57
+ execute(options: IMigrateCreateCommandOptions): Promise<void>;
58
+ }
59
+ //# sourceMappingURL=MigrateCreate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MigrateCreate.d.ts","sourceRoot":"","sources":["../../../src/cli/MigrateCreate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAmB,MAAM,cAAc,CAAC;AAE3D,OAAO,EAAE,GAAG,EAAU,MAAM,qBAAqB,CAAC;AAKlD,MAAM,WAAW,4BAA4B;IAC3C,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,QAA2B,CAAC;AAE9D;;;GAGG;AACH,eAAO,MAAM,sBAAsB,QAAiC,CAAC;AAErE;;;;GAIG;AACH,eAAO,MAAM,eAAe,QAA4B,CAAC;AAYzD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,YAAY,CAAC,IAAI,GAAE,MAAM,EAAiB,GAAG,MAAM,GAAG,SAAS,CAc9E;AAED,eAAO,MAAM,qBAAqB,qBAAqB,CAAC;AAExD,eAAO,MAAM,4BAA4B,YAAY,CAAC;AAEtD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,CA0BvF;AAED;;;;GAIG;AACH,qBAKa,oBAAqB,SAAQ,UAAU;IAElD,SAAS,CAAC,GAAG,EAAE,GAAG,CAAC;IAEN,OAAO,CAAC,OAAO,EAAE,4BAA4B,GAAG,OAAO,CAAC,IAAI,CAAC;CA6D3E"}
@@ -0,0 +1,211 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
19
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
20
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
21
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
22
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
23
+ };
24
+ var __importStar = (this && this.__importStar) || (function () {
25
+ var ownKeys = function(o) {
26
+ ownKeys = Object.getOwnPropertyNames || function (o) {
27
+ var ar = [];
28
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
29
+ return ar;
30
+ };
31
+ return ownKeys(o);
32
+ };
33
+ return function (mod) {
34
+ if (mod && mod.__esModule) return mod;
35
+ var result = {};
36
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
37
+ __setModuleDefault(result, mod);
38
+ return result;
39
+ };
40
+ })();
41
+ var __metadata = (this && this.__metadata) || function (k, v) {
42
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
43
+ };
44
+ Object.defineProperty(exports, "__esModule", { value: true });
45
+ exports.MigrateCreateCommand = exports.DEFAULT_MIGRATION_CONNECTION = exports.DEFAULT_MIGRATION_DIR = exports.ENV_NAME_REGEXP = exports.CONNECTION_NAME_REGEXP = exports.MIGRATION_NAME_REGEXP = void 0;
46
+ exports.parseEnvArgv = parseEnvArgv;
47
+ exports.migrationTemplate = migrationTemplate;
48
+ const cli_1 = require("@spinajs/cli");
49
+ const exceptions_1 = require("@spinajs/exceptions");
50
+ const log_common_1 = require("@spinajs/log-common");
51
+ const luxon_1 = require("luxon");
52
+ const fs = __importStar(require("node:fs"));
53
+ const path = __importStar(require("node:path"));
54
+ /**
55
+ * The prefix half of `Prefix_yyyy_MM_dd_HH_mm_ss`. Deliberately narrower than what a TS class
56
+ * name allows: underscores and digits are how the runner finds the timestamp - `MIGRATION_FILE_REGEXP`
57
+ * splits on the LAST `_yyyy_MM_dd_HH_mm_ss`-shaped run - so a prefix that carries its own
58
+ * underscore-digit groups is a name nobody can read back with confidence.
59
+ */
60
+ exports.MIGRATION_NAME_REGEXP = /^[A-Za-z][A-Za-z0-9]*$/;
61
+ /**
62
+ * The connection name is interpolated into `@Migration('...')`, so anything that could close that
63
+ * string literal has to be refused here rather than emitted into a file that will not parse.
64
+ */
65
+ exports.CONNECTION_NAME_REGEXP = /^[A-Za-z0-9][A-Za-z0-9_.-]*$/;
66
+ /**
67
+ * The env tag becomes a dot-segment in the file name AND a string literal inside `@Migration()`,
68
+ * so it may carry neither a dot ( which would read as a second tag ) nor anything that could close
69
+ * that literal.
70
+ */
71
+ exports.ENV_NAME_REGEXP = /^[A-Za-z][A-Za-z0-9-]*$/;
72
+ /**
73
+ * The three middle segments `parseMigrationFileEnv` provably cannot read back as an environment
74
+ * tag - they are carved out by name there ( a test suite named after its migration, a TypeScript
75
+ * declaration file ), not because they collide with a real tag but because a `<Name>.<tag>.ts` file
76
+ * whose tag is one of these is never read as tagged at all. `--env test` would therefore write a
77
+ * file whose suffix channel is silently dead - and one many projects' `**\/*.test.ts` globs would
78
+ * try to execute as a test suite besides. Refused here rather than left to surprise someone later.
79
+ */
80
+ const RESERVED_ENV_NAMES = ['test', 'spec', 'd'];
81
+ /**
82
+ * Reads `--env` directly off `process.argv`, deliberately more flexible than `Configuration`'s own
83
+ * `parseArgv` ( `packages/configuration/src/util.ts` - not exported from that package's public surface,
84
+ * so this logic is duplicated rather than imported ).
85
+ *
86
+ * `parseArgv` handles only the space-separated form ( `--env local` ), but this helper also accepts
87
+ * the equals form ( `--env=local` ). This is correct here: `packages/cli/src/args.ts` strips both
88
+ * forms from commander's argv, because `Configuration` consumes the framework-level `--env` directly.
89
+ * With `--env=local`, the value is stripped but `Configuration`'s `parseArgv` cannot recognize the
90
+ * equals form, so the CLI process boots under the default environment while the scaffolded file gets
91
+ * its `.local` suffix — harmless for this command, which uses the value only as a filename tag and
92
+ * a decorator string. `-e` is untouched by the strip and reaches commander normally, so it needs no
93
+ * duplicate handling here.
94
+ */
95
+ function parseEnvArgv(argv = process.argv) {
96
+ for (let i = 0; i < argv.length; i++) {
97
+ const arg = argv[i];
98
+ if (arg === '--env') {
99
+ return argv[i + 1];
100
+ }
101
+ if (arg.startsWith('--env=')) {
102
+ return arg.slice('--env='.length);
103
+ }
104
+ }
105
+ return undefined;
106
+ }
107
+ exports.DEFAULT_MIGRATION_DIR = './src/migrations';
108
+ exports.DEFAULT_MIGRATION_CONNECTION = 'default';
109
+ /**
110
+ * The scaffold. `connection` is named ( rather than `_connection` ) in both hooks because both
111
+ * bodies are meant to be filled in immediately - and the eslint pragma on the first line is the
112
+ * repo's own convention for a migration whose `down()` legitimately ignores it.
113
+ */
114
+ function migrationTemplate(cls, connection, env) {
115
+ return `/* eslint-disable @typescript-eslint/no-unused-vars */
116
+ import { Migration, OrmDriver, OrmMigration } from '@spinajs/orm';
117
+
118
+ /**
119
+ * TODO: describe the schema change this migration makes.
120
+ */
121
+ @Migration('${connection}'${env ? `, { Env: '${env}' }` : ''})
122
+ export class ${cls} extends OrmMigration {
123
+ /**
124
+ * Schema changes. Models are NOT wired up yet at this point - reach the database through
125
+ * \`connection\`, never through a model class.
126
+ */
127
+ public async up(connection: OrmDriver): Promise<void> {
128
+ // TODO: await connection.schema().createTable('table_name', (table) => { ... });
129
+ }
130
+
131
+ /**
132
+ * Undoes \`up()\`. Leave it empty only when the change genuinely cannot be reversed - an empty
133
+ * \`down()\` makes migrate-down report success while changing nothing.
134
+ */
135
+ public async down(connection: OrmDriver): Promise<void> {
136
+ // TODO: await connection.schema().dropTable('table_name');
137
+ }
138
+ }
139
+ `;
140
+ }
141
+ /**
142
+ * The one command here that needs no database and no Orm: it writes a file. Keeping it free of
143
+ * `DI.resolve(Orm)` means a developer can scaffold a migration in a checkout whose connections
144
+ * are not configured, or not reachable, which is exactly when new migrations get written.
145
+ */
146
+ let MigrateCreateCommand = class MigrateCreateCommand extends cli_1.CliCommand {
147
+ async execute(options) {
148
+ const name = options.name ?? '';
149
+ const connection = options.connection ?? exports.DEFAULT_MIGRATION_CONNECTION;
150
+ // An explicit `options.env` wins - that is how the tests in this suite call `execute()`
151
+ // directly - and falls back to a direct argv read for the real CLI path, where
152
+ // `packages/cli/src/args.ts` has already stripped `--env <value>` out of what commander sees.
153
+ // See `parseEnvArgv()`.
154
+ const env = options.env ?? parseEnvArgv();
155
+ if (!exports.MIGRATION_NAME_REGEXP.test(name)) {
156
+ throw new exceptions_1.InvalidArgument(`Invalid migration name "${name}" - it must be a plain class-name prefix: a letter followed by letters or digits, no spaces, dashes or underscores. The _yyyy_MM_dd_HH_mm_ss suffix is appended here.`);
157
+ }
158
+ if (!exports.CONNECTION_NAME_REGEXP.test(connection)) {
159
+ throw new exceptions_1.InvalidArgument(`Invalid connection name "${connection}" - expected the name of a connection from db.Connections, eg. "default"`);
160
+ }
161
+ if (env !== undefined) {
162
+ if (!exports.ENV_NAME_REGEXP.test(env)) {
163
+ throw new exceptions_1.InvalidArgument(`Invalid environment name "${env}" - a letter followed by letters, digits or dashes. It becomes both a file suffix and a string inside @Migration().`);
164
+ }
165
+ if (RESERVED_ENV_NAMES.includes(env)) {
166
+ throw new exceptions_1.InvalidArgument(`Invalid environment name "${env}" - "test", "spec" and "d" are carved out by the migration file parser as file-kind markers (a test suite, a TypeScript declaration file), never read back as an environment tag. Choose a different name.`);
167
+ }
168
+ }
169
+ // The timestamp is not decoration: it is the ONLY ordering the migration runner has, and it is
170
+ // read back out of the class name rather than out of the file's mtime or its position on disk.
171
+ const cls = `${name}_${luxon_1.DateTime.now().toFormat('yyyy_MM_dd_HH_mm_ss')}`;
172
+ const dir = options.dir ?? exports.DEFAULT_MIGRATION_DIR;
173
+ const file = path.join(dir, `${cls}${env ? `.${env}` : ''}.ts`);
174
+ fs.mkdirSync(dir, { recursive: true });
175
+ try {
176
+ // 'wx' - never clobber. Two `migrate-create` runs inside the same second produce the same
177
+ // class name, and silently overwriting the first one would delete work that was just written.
178
+ fs.writeFileSync(file, migrationTemplate(cls, connection, env), { flag: 'wx', encoding: 'utf-8' });
179
+ }
180
+ catch (err) {
181
+ if (err.code === 'EEXIST') {
182
+ throw new exceptions_1.IOFail(`Migration file ${file} already exists - a migration with this name was created in the same second. Wait a second and run it again, or pass a different --name.`, err);
183
+ }
184
+ throw err;
185
+ }
186
+ // The path goes to stdout on its own line so `$(spinajs migrate-create -n Foo)` is usable;
187
+ // everything else is guidance and belongs in the log.
188
+ // eslint-disable-next-line no-console
189
+ console.log(file);
190
+ // The scan is about the APPLICATION's own directories ( `system.dirs.migrations`, resolved
191
+ // against ITS cwd at runtime ) - not about where this file happened to be scaffolded. Scaffold
192
+ // it inside a package under `<pkg>/src/migrations` and it sits in a directory this same list
193
+ // would scan too, but only when the consumer boots FROM that package's own cwd, which almost
194
+ // never happens: the running process's cwd is the application, not any of its dependencies. A
195
+ // package's migrations must always be re-exported from its own index, or they never run.
196
+ this.Log.info(`Created migration ${cls} for connection "${connection}"${env ? ` in environment "${env}"` : ''}. A file under the application's own system.dirs.migrations is discovered automatically. Inside a package, always re-export it from the package's index - a package's own directories are never scanned at a consumer's runtime - so the @Migration decorator runs and registers it.`);
197
+ }
198
+ };
199
+ exports.MigrateCreateCommand = MigrateCreateCommand;
200
+ __decorate([
201
+ (0, log_common_1.Logger)('ORM-CLI'),
202
+ __metadata("design:type", log_common_1.Log)
203
+ ], MigrateCreateCommand.prototype, "Log", void 0);
204
+ exports.MigrateCreateCommand = MigrateCreateCommand = __decorate([
205
+ (0, cli_1.Command)('migrate-create', 'Scaffolds a new migration file'),
206
+ (0, cli_1.Option)('-n, --name [name]', true, 'migration name prefix - a plain class-name prefix, letters and digits only'),
207
+ (0, cli_1.Option)('-d, --dir [dir]', false, `target directory, default ${exports.DEFAULT_MIGRATION_DIR}`),
208
+ (0, cli_1.Option)('-c, --connection [connection]', false, `connection the migration runs on, default "${exports.DEFAULT_MIGRATION_CONNECTION}"`),
209
+ (0, cli_1.Option)('-e, --env [env]', false, 'environment this migration belongs to, eg. local - omit to run it in every environment')
210
+ ], MigrateCreateCommand);
211
+ //# sourceMappingURL=MigrateCreate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MigrateCreate.js","sourceRoot":"","sources":["../../../src/cli/MigrateCreate.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ import { CliCommand } from '@spinajs/cli';
2
+ import { Log } from '@spinajs/log-common';
3
+ export interface IMigrateDownCommandOptions {
4
+ name?: string;
5
+ all?: boolean;
6
+ fake?: boolean;
7
+ connection?: string;
8
+ }
9
+ /**
10
+ * The description says LAST BATCH out loud, and so does the pre-run line below: `down()` defaults
11
+ * to the last applied batch, not to everything, and an operator who assumed otherwise would read
12
+ * a short "rolled back 1 migration" as a complete teardown.
13
+ */
14
+ export declare class MigrateDownCommand extends CliCommand {
15
+ protected Log: Log;
16
+ execute(options: IMigrateDownCommandOptions): Promise<void>;
17
+ /**
18
+ * Says what scope is about to be reversed BEFORE it is reversed, because by the time the result
19
+ * line is printed the schema change has already happened.
20
+ */
21
+ protected announce(options: IMigrateDownCommandOptions): void;
22
+ }
23
+ //# sourceMappingURL=MigrateDown.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MigrateDown.d.ts","sourceRoot":"","sources":["../../../src/cli/MigrateDown.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAmB,MAAM,cAAc,CAAC;AAC3D,OAAO,EAAE,GAAG,EAAU,MAAM,qBAAqB,CAAC;AAGlD,MAAM,WAAW,0BAA0B;IACzC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;;GAIG;AACH,qBAKa,kBAAmB,SAAQ,UAAU;IAEhD,SAAS,CAAC,GAAG,EAAE,GAAG,CAAC;IAEN,OAAO,CAAC,OAAO,EAAE,0BAA0B,GAAG,OAAO,CAAC,IAAI,CAAC;IAoBxE;;;OAGG;IACH,SAAS,CAAC,QAAQ,CAAC,OAAO,EAAE,0BAA0B,GAAG,IAAI;CAwB9D"}
@@ -0,0 +1,74 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.MigrateDownCommand = void 0;
13
+ const cli_1 = require("@spinajs/cli");
14
+ const log_common_1 = require("@spinajs/log-common");
15
+ const orm_js_1 = require("../orm.js");
16
+ /**
17
+ * The description says LAST BATCH out loud, and so does the pre-run line below: `down()` defaults
18
+ * to the last applied batch, not to everything, and an operator who assumed otherwise would read
19
+ * a short "rolled back 1 migration" as a complete teardown.
20
+ */
21
+ let MigrateDownCommand = class MigrateDownCommand extends cli_1.CliCommand {
22
+ async execute(options) {
23
+ // Not `DI.resolve(Orm)`: a boot migration pass would apply the pending migrations on every
24
+ // `Migration.OnStartup` connection and this command would then roll back the batch it had
25
+ // just created. See `resolveCliOrm`.
26
+ const orm = await (0, orm_js_1.resolveCliOrm)();
27
+ this.announce(options);
28
+ const executed = await orm.Migration.down(options.name, { all: options.all, fake: options.fake, connection: options.connection });
29
+ if (executed.length === 0) {
30
+ const scope = options.connection ? ` on connection ${options.connection}` : ' on any configured connection';
31
+ this.Log.info(options.name ? `Nothing rolled back - ${options.name} is not applied${scope}` : `Nothing to roll back - no applied migrations found${options.connection ? scope : ''}`);
32
+ return;
33
+ }
34
+ this.Log.success(`${options.fake ? 'Recorded as rolled back ( --fake, nothing was executed )' : 'Rolled back'} ${executed.length} migration(s): ${executed.map((m) => m.constructor.name).join(', ')}`);
35
+ }
36
+ /**
37
+ * Says what scope is about to be reversed BEFORE it is reversed, because by the time the result
38
+ * line is printed the schema change has already happened.
39
+ */
40
+ announce(options) {
41
+ // first, and separate from the three below: it narrows all of them, and an operator reading
42
+ // "rolling back EVERY applied migration" needs to know it means every one on ONE connection
43
+ if (options.connection) {
44
+ this.Log.info(`Limited to connection ${options.connection} - no other configured connection is touched`);
45
+ }
46
+ if (options.name) {
47
+ // A named rollback hands the migration service a one-element unit list, so every other
48
+ // applied row in the target batch looks unmatched to it and it warns about them. Those rows
49
+ // are healthy - only the warning is wrong - and the remedy it suggests is destructive if
50
+ // followed here, which is why this line says what to do with those warnings instead of
51
+ // leaving the operator to act on them.
52
+ this.Log.warn(`Rolling back a single migration: ${options.name}. The migration service may warn during this run that other migrations have no matching registered class - that is an artifact of a named rollback, those rows are fine. Do not edit or delete them.`);
53
+ return;
54
+ }
55
+ if (options.all) {
56
+ this.Log.warn('Rolling back EVERY applied migration on every configured connection ( --all )');
57
+ return;
58
+ }
59
+ this.Log.info('Rolling back the LAST APPLIED BATCH only - pass --all to reverse every applied migration');
60
+ }
61
+ };
62
+ exports.MigrateDownCommand = MigrateDownCommand;
63
+ __decorate([
64
+ (0, log_common_1.Logger)('ORM-CLI'),
65
+ __metadata("design:type", log_common_1.Log)
66
+ ], MigrateDownCommand.prototype, "Log", void 0);
67
+ exports.MigrateDownCommand = MigrateDownCommand = __decorate([
68
+ (0, cli_1.Command)('migrate-down', 'Rolls ORM migrations back - the LAST APPLIED BATCH only, unless --all is given'),
69
+ (0, cli_1.Option)('-n, --name [name]', false, 'roll back a single migration, by class name'),
70
+ (0, cli_1.Option)('-c, --connection [connection]', false, 'limit the rollback to one connection, by name'),
71
+ (0, cli_1.Option)('-a, --all', false, 'roll back EVERY applied migration, not just the last batch'),
72
+ (0, cli_1.Option)('-f, --fake', false, 'record the migrations as rolled back without executing them')
73
+ ], MigrateDownCommand);
74
+ //# sourceMappingURL=MigrateDown.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MigrateDown.js","sourceRoot":"","sources":["../../../src/cli/MigrateDown.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,sCAA2D;AAC3D,oDAAkD;AAClD,sCAA0C;AAS1C;;;;GAIG;AAMI,IAAM,kBAAkB,GAAxB,MAAM,kBAAmB,SAAQ,gBAAU;IAIzC,KAAK,CAAC,OAAO,CAAC,OAAmC;QACtD,2FAA2F;QAC3F,0FAA0F;QAC1F,qCAAqC;QACrC,MAAM,GAAG,GAAG,MAAM,IAAA,sBAAa,GAAE,CAAC;QAElC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QAEvB,MAAM,QAAQ,GAAG,MAAM,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,UAAU,EAAE,OAAO,CAAC,UAAU,EAAE,CAAC,CAAC;QAElI,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC1B,MAAM,KAAK,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,kBAAkB,OAAO,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,+BAA+B,CAAC;YAE5G,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,yBAAyB,OAAO,CAAC,IAAI,kBAAkB,KAAK,EAAE,CAAC,CAAC,CAAC,qDAAqD,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YACtL,OAAO;QACT,CAAC;QAED,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,0DAA0D,CAAC,CAAC,CAAC,aAAa,IAAI,QAAQ,CAAC,MAAM,kBAAkB,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC1M,CAAC;IAED;;;OAGG;IACO,QAAQ,CAAC,OAAmC;QACpD,4FAA4F;QAC5F,4FAA4F;QAC5F,IAAI,OAAO,CAAC,UAAU,EAAE,CAAC;YACvB,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,yBAAyB,OAAO,CAAC,UAAU,8CAA8C,CAAC,CAAC;QAC3G,CAAC;QAED,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;YACjB,uFAAuF;YACvF,4FAA4F;YAC5F,yFAAyF;YACzF,uFAAuF;YACvF,uCAAuC;YACvC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,oCAAoC,OAAO,CAAC,IAAI,sMAAsM,CAAC,CAAC;YACtQ,OAAO;QACT,CAAC;QAED,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;YAChB,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,+EAA+E,CAAC,CAAC;YAC/F,OAAO;QACT,CAAC;QAED,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,0FAA0F,CAAC,CAAC;IAC5G,CAAC;CACF,CAAA;AApDY,gDAAkB;AAEnB;IADT,IAAA,mBAAM,EAAC,SAAS,CAAC;8BACH,gBAAG;+CAAC;6BAFR,kBAAkB;IAL9B,IAAA,aAAO,EAAC,cAAc,EAAE,gFAAgF,CAAC;IACzG,IAAA,YAAM,EAAC,mBAAmB,EAAE,KAAK,EAAE,6CAA6C,CAAC;IACjF,IAAA,YAAM,EAAC,+BAA+B,EAAE,KAAK,EAAE,+CAA+C,CAAC;IAC/F,IAAA,YAAM,EAAC,WAAW,EAAE,KAAK,EAAE,4DAA4D,CAAC;IACxF,IAAA,YAAM,EAAC,YAAY,EAAE,KAAK,EAAE,6DAA6D,CAAC;GAC9E,kBAAkB,CAoD9B"}