@aurorajs.dev/catalyst-cli 1.0.0 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,13 +1,13 @@
1
- # aurora-cli2
1
+ # aurora-catalyst-cli
2
2
 
3
3
  A new CLI generated with oclif
4
4
 
5
5
  [![oclif](https://img.shields.io/badge/cli-oclif-brightgreen.svg)](https://oclif.io)
6
- [![Version](https://img.shields.io/npm/v/aurora-cli2.svg)](https://npmjs.org/package/aurora-cli2)
7
- [![Downloads/week](https://img.shields.io/npm/dw/aurora-cli2.svg)](https://npmjs.org/package/aurora-cli2)
6
+ [![Version](https://img.shields.io/npm/v/aurora-catalyst-cli.svg)](https://npmjs.org/package/aurora-catalyst-cli)
7
+ [![Downloads/week](https://img.shields.io/npm/dw/aurora-catalyst-cli.svg)](https://npmjs.org/package/aurora-catalyst-cli)
8
8
 
9
9
  <!-- toc -->
10
- * [aurora-cli2](#aurora-cli2)
10
+ * [aurora-catalyst-cli](#aurora-catalyst-cli)
11
11
  * [Usage](#usage)
12
12
  * [Commands](#commands)
13
13
  * [Command Topics](#command-topics)
@@ -48,7 +48,7 @@ $ npm install -g @aurorajs.dev/catalyst-cli
48
48
  $ catalyst COMMAND
49
49
  running command...
50
50
  $ catalyst (--version)
51
- @aurorajs.dev/catalyst-cli/1.0.0 darwin-arm64 node-v24.14.0
51
+ @aurorajs.dev/catalyst-cli/1.0.2 darwin-arm64 node-v24.14.0
52
52
  $ catalyst --help [COMMAND]
53
53
  USAGE
54
54
  $ catalyst COMMAND
@@ -121,24 +121,24 @@ YAML schemas are expected in `./cliter/{bounded_context}/*.aurora.yaml`.
121
121
 
122
122
  ```bash
123
123
  # List configured bounded contexts
124
- aurora sheets list
124
+ catalyst sheets list
125
125
 
126
126
  # Validate connection to a spreadsheet
127
- aurora sheets validate --bc iam
127
+ catalyst sheets validate --bc iam
128
128
 
129
129
  # Push YAML schemas to Google Sheets
130
- aurora sheets push --bc iam
131
- aurora sheets push --all
132
- aurora sheets push --all --dry-run
130
+ catalyst sheets push --bc iam
131
+ catalyst sheets push --all
132
+ catalyst sheets push --all --dry-run
133
133
 
134
134
  # Pull from Google Sheets to YAML files
135
- aurora sheets pull --bc iam
136
- aurora sheets pull --all
137
- aurora sheets pull --all --no-backup
135
+ catalyst sheets pull --bc iam
136
+ catalyst sheets pull --all
137
+ catalyst sheets pull --all --no-backup
138
138
 
139
139
  # Show differences between YAML and Sheets
140
- aurora sheets diff --bc iam
141
- aurora sheets diff --all --summary
140
+ catalyst sheets diff --bc iam
141
+ catalyst sheets diff --all --summary
142
142
  ```
143
143
 
144
144
  Without `--bc` or `--all`, commands prompt interactively for the bounded context.
@@ -161,16 +161,16 @@ environments:
161
161
 
162
162
  ```bash
163
163
  # Deploy all environments
164
- aurora deploy
164
+ catalyst deploy
165
165
 
166
166
  # Deploy only dev
167
- aurora deploy --env dev
167
+ catalyst deploy --env dev
168
168
 
169
169
  # Dry run — show what would be pushed without executing
170
- aurora deploy --dry-run
170
+ catalyst deploy --dry-run
171
171
 
172
172
  # Custom config path
173
- aurora deploy --env prod -c ../aurora.yaml
173
+ catalyst deploy --env prod -c ../aurora.yaml
174
174
  ```
175
175
 
176
176
  Internally, the command runs `git push origin {source}:environments/{env}` for each resolved environment.
@@ -234,22 +234,22 @@ Copy `aurora.example.yaml` to `aurora.yaml` in your project and fill in your val
234
234
 
235
235
  ```bash
236
236
  # Provision all environments (passwords from .env)
237
- aurora provision
237
+ catalyst provision
238
238
 
239
239
  # Provision only dev
240
- aurora provision --env dev
240
+ catalyst provision --env dev
241
241
 
242
242
  # Provision only prod backend
243
- aurora provision --env prod --target back
243
+ catalyst provision --env prod --target back
244
244
 
245
245
  # Provision only frontend (all environments)
246
- aurora provision --target front
246
+ catalyst provision --target front
247
247
 
248
248
  # Skip DNS and CI/CD (applies to all plans)
249
- aurora provision --skip-dns --skip-cicd
249
+ catalyst provision --skip-dns --skip-cicd
250
250
 
251
251
  # Pass passwords inline (overrides .env)
252
- aurora provision --ftp-password s3cret --db-password dbpass
252
+ catalyst provision --ftp-password s3cret --db-password dbpass
253
253
  ```
254
254
 
255
255
  GitHub secrets and variables are prefixed with `ENV_TARGET_` (e.g., `DEV_BACK_SSH_PRIVATE_KEY`, `PROD_FRONT_SERVER_HOST`).
@@ -9,12 +9,15 @@ export default class Generate extends Command {
9
9
  static flags: {
10
10
  force: import("@oclif/core/interfaces").BooleanFlag<boolean>;
11
11
  name: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
12
+ 'no-review': import("@oclif/core/interfaces").BooleanFlag<boolean>;
12
13
  noGraphQLTypes: import("@oclif/core/interfaces").BooleanFlag<boolean>;
13
14
  overwriteInterface: import("@oclif/core/interfaces").BooleanFlag<boolean>;
15
+ review: import("@oclif/core/interfaces").BooleanFlag<boolean>;
14
16
  target: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
15
17
  tests: import("@oclif/core/interfaces").BooleanFlag<boolean>;
16
18
  verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
17
19
  };
18
20
  run(): Promise<void>;
21
+ private logOriginSummary;
19
22
  private parseName;
20
23
  }
@@ -1,9 +1,13 @@
1
1
  import { Args, Command, Flags } from '@oclif/core';
2
2
  import { execFileSync } from 'node:child_process';
3
+ import * as fs from 'node:fs';
3
4
  import { loadSchema } from '../../generator/domain/schema-loader.js';
4
5
  import { loadLockFiles } from '../../generator/engine/lock-file.js';
5
6
  import { generateBackModule } from '../../generator/handlers/back.handler.js';
6
7
  import { generateFrontModule } from '../../generator/handlers/front.handler.js';
8
+ import { toCurrentPath } from '../../origin/domain/origin-file.js';
9
+ import { resolveReviewMode } from '../../origin/interactive/review-mode.js';
10
+ import { createDefaultReviewSessionIo, runReviewSession, } from '../../origin/interactive/review-session.js';
7
11
  export default class Generate extends Command {
8
12
  static args = {
9
13
  scope: Args.string({
@@ -36,6 +40,11 @@ export default class Generate extends Command {
36
40
  description: 'Bounded context and module (e.g. "iam/account").',
37
41
  required: true,
38
42
  }),
43
+ 'no-review': Flags.boolean({
44
+ default: false,
45
+ description: 'Skip the interactive review of generated `.origin` files.',
46
+ exclusive: ['review'],
47
+ }),
39
48
  noGraphQLTypes: Flags.boolean({
40
49
  char: 'g',
41
50
  default: false,
@@ -46,6 +55,11 @@ export default class Generate extends Command {
46
55
  default: false,
47
56
  description: 'Overwrite front-end interfaces.',
48
57
  }),
58
+ review: Flags.boolean({
59
+ default: false,
60
+ description: 'Force the interactive review of generated `.origin` files.',
61
+ exclusive: ['no-review'],
62
+ }),
49
63
  target: Flags.string({
50
64
  description: 'Output subdirectory (default: "backend" for back, "frontend" for front).',
51
65
  }),
@@ -106,11 +120,34 @@ export default class Generate extends Command {
106
120
  originFiles = result.originFiles;
107
121
  this.log(`Front-end module "${boundedContextName}/${moduleName}" generated.`);
108
122
  }
109
- if (originFiles.length > 0) {
110
- const count = originFiles.length;
111
- const noun = count === 1 ? 'file' : 'files';
112
- this.log(`${count} origin ${noun} pending review. Run "catalyst origin list" to inspect or "catalyst origin review" for an interactive walk-through.`);
123
+ // Nothing produced no review, no hint, regardless of mode or TTY.
124
+ if (originFiles.length === 0)
125
+ return;
126
+ const mode = resolveReviewMode({
127
+ isTty: Boolean(process.stdout.isTTY),
128
+ noReview: flags['no-review'],
129
+ review: flags.review,
130
+ });
131
+ if (mode === 'skip') {
132
+ this.logOriginSummary(originFiles.length);
133
+ return;
113
134
  }
135
+ // Interactive: walk the user through the files this run produced, then
136
+ // report any they left pending (e.g. they chose Finish early).
137
+ const entries = originFiles.map((origin) => ({
138
+ current: toCurrentPath(origin),
139
+ origin,
140
+ }));
141
+ await runReviewSession(entries, createDefaultReviewSessionIo((message) => {
142
+ this.log(message);
143
+ }));
144
+ const residual = originFiles.filter((origin) => fs.existsSync(origin));
145
+ if (residual.length > 0)
146
+ this.logOriginSummary(residual.length);
147
+ }
148
+ logOriginSummary(count) {
149
+ const noun = count === 1 ? 'file' : 'files';
150
+ this.log(`${count} origin ${noun} pending review. Run "catalyst origin list" to inspect or "catalyst origin review" for an interactive walk-through.`);
114
151
  }
115
152
  parseName(name) {
116
153
  const parts = name.split('/');
@@ -1,97 +1,17 @@
1
- import select from '@inquirer/select';
2
1
  import { Command } from '@oclif/core';
3
- import chalk from 'chalk';
4
- import { execFileSync } from 'node:child_process';
5
- import path from 'node:path';
6
- import { acceptOrigin, ignoreOrigin, rejectOrigin, } from '../../origin/domain/actions.js';
7
- import { discoverOrigins, } from '../../origin/domain/origin-file.js';
2
+ import { discoverOrigins } from '../../origin/domain/origin-file.js';
3
+ import { createDefaultReviewSessionIo, runReviewSession, } from '../../origin/interactive/review-session.js';
8
4
  export default class OriginReview extends Command {
9
5
  static description = 'Interactive walk-through of pending `.origin` files (inquirer + VS Code diff).';
10
6
  static examples = ['<%= config.bin %> origin review'];
11
7
  async run() {
12
- let remaining = discoverOrigins();
13
- if (remaining.length === 0) {
8
+ const entries = discoverOrigins();
9
+ if (entries.length === 0) {
14
10
  this.log('No origin files to review.');
15
11
  return;
16
12
  }
17
- while (remaining.length > 0) {
18
- // eslint-disable-next-line no-await-in-loop
19
- const selected = await selectFile(remaining);
20
- if (!selected)
21
- return;
22
- openDiffInVsCode(selected);
23
- // eslint-disable-next-line no-await-in-loop
24
- const action = await selectAction();
25
- switch (action) {
26
- case 'accept': {
27
- const result = acceptOrigin(selected.current);
28
- this.log(`${chalk.green('[ACCEPTED]')} ${result.path}${result.status === 'noop' ? chalk.dim(' (noop)') : ''}`);
29
- break;
30
- }
31
- case 'finish': {
32
- return;
33
- }
34
- case 'go-back': {
35
- // Loop again to refresh the file picker.
36
- break;
37
- }
38
- case 'ignore': {
39
- const result = ignoreOrigin(selected.current);
40
- this.log(`${chalk.cyan('[IGNORED]')} ${result.path}${result.status === 'noop' ? chalk.dim(' (noop)') : ''}`);
41
- break;
42
- }
43
- case 'reject': {
44
- const result = rejectOrigin(selected.current);
45
- this.log(`${chalk.yellow('[REJECTED]')} ${result.path}${result.status === 'noop' ? chalk.dim(' (noop)') : ''}`);
46
- break;
47
- }
48
- }
49
- // Refresh the list — the action just removed an entry (or kept it on
50
- // 'go-back'). Re-scanning is cheap and avoids manual list bookkeeping.
51
- remaining = discoverOrigins();
52
- }
53
- this.log('All origin files processed.');
54
- }
55
- }
56
- async function selectFile(entries) {
57
- if (entries.length === 0)
58
- return undefined;
59
- return select({
60
- choices: entries.map((entry) => ({ name: entry.current, value: entry })),
61
- message: 'Select file to review:',
62
- });
63
- }
64
- async function selectAction() {
65
- return select({
66
- choices: [
67
- { name: 'Reject (keep my current file as-is)', value: 'reject' },
68
- { name: 'Accept (adopt the codegen proposal)', value: 'accept' },
69
- {
70
- name: 'Ignore (keep mine + mark file as ignored on future regens)',
71
- value: 'ignore',
72
- },
73
- { name: 'Go back to file selection', value: 'go-back' },
74
- { name: 'Finish review', value: 'finish' },
75
- ],
76
- message: 'Select an action:',
77
- });
78
- }
79
- function openDiffInVsCode(entry) {
80
- const absCurrent = path.resolve(entry.current);
81
- const absOrigin = path.resolve(entry.origin);
82
- try {
83
- // Order matters: `code --diff <left> <right>` puts the first path on the
84
- // left (source of changes) and the second on the right (editable
85
- // destination). Reviewing means importing pieces of the codegen proposal
86
- // (`.origin`) into the user's file, so `.origin` goes left and the
87
- // current file goes right — that way the inline arrows move chunks from
88
- // proposal → current file, not the other way around.
89
- execFileSync('code', ['--diff', absOrigin, absCurrent], {
90
- stdio: 'ignore',
91
- });
92
- }
93
- catch {
94
- // VS Code not on PATH — silently continue. The user can still pick an
95
- // action without the side-by-side diff.
13
+ await runReviewSession(entries, createDefaultReviewSessionIo((message) => {
14
+ this.log(message);
15
+ }));
96
16
  }
97
17
  }
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Pure resolution of `catalyst generate`'s end-of-run review mode. Kept free
3
+ * of I/O so the precedence (flags over TTY) is unit-testable without spawning
4
+ * a process or a prompt.
5
+ */
6
+ export type ReviewMode = 'interactive' | 'skip';
7
+ export interface ReviewModeInputs {
8
+ /** `Boolean(process.stdout.isTTY)` at the call site. */
9
+ isTty: boolean;
10
+ /** The `--no-review` flag. */
11
+ noReview: boolean;
12
+ /** The `--review` flag. */
13
+ review: boolean;
14
+ }
15
+ /**
16
+ * Precedence: an explicit flag always wins over TTY detection.
17
+ * - `--review` → interactive
18
+ * - `--no-review` → skip
19
+ * - neither → interactive on a TTY, skip otherwise
20
+ *
21
+ * `--review` and `--no-review` are declared mutually exclusive at the oclif
22
+ * flag layer, so both being true never reaches here; if it ever did,
23
+ * `--review` wins.
24
+ */
25
+ export declare function resolveReviewMode({ isTty, noReview, review, }: ReviewModeInputs): ReviewMode;
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Pure resolution of `catalyst generate`'s end-of-run review mode. Kept free
3
+ * of I/O so the precedence (flags over TTY) is unit-testable without spawning
4
+ * a process or a prompt.
5
+ */
6
+ /**
7
+ * Precedence: an explicit flag always wins over TTY detection.
8
+ * - `--review` → interactive
9
+ * - `--no-review` → skip
10
+ * - neither → interactive on a TTY, skip otherwise
11
+ *
12
+ * `--review` and `--no-review` are declared mutually exclusive at the oclif
13
+ * flag layer, so both being true never reaches here; if it ever did,
14
+ * `--review` wins.
15
+ */
16
+ export function resolveReviewMode({ isTty, noReview, review, }) {
17
+ if (review)
18
+ return 'interactive';
19
+ if (noReview)
20
+ return 'skip';
21
+ return isTty ? 'interactive' : 'skip';
22
+ }
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Shared interactive `.origin` walk-through. This is the single
3
+ * implementation of the review loop; both `catalyst origin review` and
4
+ * `catalyst generate`'s interactive mode delegate to `runReviewSession`.
5
+ *
6
+ * Action logic lives in the domain (`acceptOrigin`/`rejectOrigin`/
7
+ * `ignoreOrigin`). This module owns only the orchestration plus the I/O
8
+ * concerns (prompts, the `code --diff` viewer, log formatting), which are
9
+ * injectable via `ReviewSessionIo` so the loop is testable without driving
10
+ * real prompts. These I/O concerns MUST NOT bleed into the domain.
11
+ */
12
+ import { type OriginEntry } from '../domain/origin-file.js';
13
+ export type ReviewAction = 'accept' | 'finish' | 'go-back' | 'ignore' | 'reject';
14
+ /**
15
+ * The injectable I/O seam. The default implementation uses `@inquirer/select`
16
+ * for prompts and `code --diff` for the diff view; tests inject a stub.
17
+ */
18
+ export interface ReviewSessionIo {
19
+ log(message: string): void;
20
+ openDiff(entry: OriginEntry): void;
21
+ selectAction(): Promise<ReviewAction>;
22
+ selectFile(entries: OriginEntry[]): Promise<OriginEntry | undefined>;
23
+ }
24
+ /**
25
+ * Walk the user through the given `.origin` entries. The entry set is scoped
26
+ * by the caller: `origin review` passes the full cwd discovery, `generate`
27
+ * passes only the files its run produced. "Remaining" is refreshed by
28
+ * checking which `.origin` files still exist on disk after each action — an
29
+ * entry is done when its `.origin` is gone — so the loop never re-scans cwd.
30
+ */
31
+ export declare function runReviewSession(entries: OriginEntry[], io?: ReviewSessionIo): Promise<void>;
32
+ /**
33
+ * Default I/O: inquirer prompts + VS Code diff. `log` defaults to stdout but
34
+ * callers (oclif commands) inject `this.log` to route through oclif output.
35
+ */
36
+ export declare function createDefaultReviewSessionIo(log?: (message: string) => void): ReviewSessionIo;
@@ -0,0 +1,123 @@
1
+ /**
2
+ * Shared interactive `.origin` walk-through. This is the single
3
+ * implementation of the review loop; both `catalyst origin review` and
4
+ * `catalyst generate`'s interactive mode delegate to `runReviewSession`.
5
+ *
6
+ * Action logic lives in the domain (`acceptOrigin`/`rejectOrigin`/
7
+ * `ignoreOrigin`). This module owns only the orchestration plus the I/O
8
+ * concerns (prompts, the `code --diff` viewer, log formatting), which are
9
+ * injectable via `ReviewSessionIo` so the loop is testable without driving
10
+ * real prompts. These I/O concerns MUST NOT bleed into the domain.
11
+ */
12
+ import select from '@inquirer/select';
13
+ import chalk from 'chalk';
14
+ import { execFileSync } from 'node:child_process';
15
+ import * as fs from 'node:fs';
16
+ import path from 'node:path';
17
+ import { acceptOrigin, ignoreOrigin, rejectOrigin, } from '../domain/actions.js';
18
+ /**
19
+ * Walk the user through the given `.origin` entries. The entry set is scoped
20
+ * by the caller: `origin review` passes the full cwd discovery, `generate`
21
+ * passes only the files its run produced. "Remaining" is refreshed by
22
+ * checking which `.origin` files still exist on disk after each action — an
23
+ * entry is done when its `.origin` is gone — so the loop never re-scans cwd.
24
+ */
25
+ export async function runReviewSession(entries, io = createDefaultReviewSessionIo()) {
26
+ let remaining = entries.filter((entry) => stillPending(entry));
27
+ if (remaining.length === 0)
28
+ return;
29
+ while (remaining.length > 0) {
30
+ // eslint-disable-next-line no-await-in-loop
31
+ const selected = await io.selectFile(remaining);
32
+ if (!selected)
33
+ return;
34
+ io.openDiff(selected);
35
+ // eslint-disable-next-line no-await-in-loop
36
+ const action = await io.selectAction();
37
+ if (action === 'finish')
38
+ return;
39
+ if (action !== 'go-back') {
40
+ io.log(applyAction(action, selected));
41
+ }
42
+ // Re-derive from the scoped set: an entry is done once its `.origin` is
43
+ // gone (accept renames it away; reject/ignore delete it). 'go-back' keeps
44
+ // the entry, so the list is unchanged on that branch.
45
+ remaining = remaining.filter((entry) => stillPending(entry));
46
+ }
47
+ io.log('All origin files processed.');
48
+ }
49
+ function applyAction(action, entry) {
50
+ switch (action) {
51
+ case 'accept': {
52
+ const result = acceptOrigin(entry.current);
53
+ return `${chalk.green('[ACCEPTED]')} ${result.path}${result.status === 'noop' ? chalk.dim(' (noop)') : ''}`;
54
+ }
55
+ case 'ignore': {
56
+ const result = ignoreOrigin(entry.current);
57
+ return `${chalk.cyan('[IGNORED]')} ${result.path}${result.status === 'noop' ? chalk.dim(' (noop)') : ''}`;
58
+ }
59
+ case 'reject': {
60
+ const result = rejectOrigin(entry.current);
61
+ return `${chalk.yellow('[REJECTED]')} ${result.path}${result.status === 'noop' ? chalk.dim(' (noop)') : ''}`;
62
+ }
63
+ }
64
+ }
65
+ function stillPending(entry) {
66
+ return fs.existsSync(entry.origin);
67
+ }
68
+ /**
69
+ * Default I/O: inquirer prompts + VS Code diff. `log` defaults to stdout but
70
+ * callers (oclif commands) inject `this.log` to route through oclif output.
71
+ */
72
+ export function createDefaultReviewSessionIo(log = (message) => {
73
+ console.log(message);
74
+ }) {
75
+ return {
76
+ log,
77
+ openDiff: openDiffInVsCode,
78
+ selectAction,
79
+ selectFile,
80
+ };
81
+ }
82
+ async function selectFile(entries) {
83
+ if (entries.length === 0)
84
+ return undefined;
85
+ return select({
86
+ choices: entries.map((entry) => ({ name: entry.current, value: entry })),
87
+ message: 'Select file to review:',
88
+ });
89
+ }
90
+ async function selectAction() {
91
+ return select({
92
+ choices: [
93
+ { name: 'Reject (keep my current file as-is)', value: 'reject' },
94
+ { name: 'Accept (adopt the codegen proposal)', value: 'accept' },
95
+ {
96
+ name: 'Ignore (keep mine + mark file as ignored on future regens)',
97
+ value: 'ignore',
98
+ },
99
+ { name: 'Go back to file selection', value: 'go-back' },
100
+ { name: 'Finish review', value: 'finish' },
101
+ ],
102
+ message: 'Select an action:',
103
+ });
104
+ }
105
+ function openDiffInVsCode(entry) {
106
+ const absCurrent = path.resolve(entry.current);
107
+ const absOrigin = path.resolve(entry.origin);
108
+ try {
109
+ // Order matters: `code --diff <left> <right>` puts the first path on the
110
+ // left (source of changes) and the second on the right (editable
111
+ // destination). Reviewing means importing pieces of the codegen proposal
112
+ // (`.origin`) into the user's file, so `.origin` goes left and the
113
+ // current file goes right — that way the inline arrows move chunks from
114
+ // proposal → current file, not the other way around.
115
+ execFileSync('code', ['--diff', absOrigin, absCurrent], {
116
+ stdio: 'ignore',
117
+ });
118
+ }
119
+ catch {
120
+ // VS Code not on PATH — silently continue. The user can still pick an
121
+ // action without the side-by-side diff.
122
+ }
123
+ }
@@ -11,7 +11,7 @@ APP_SSR = false
11
11
  APP_FALLBACK_LANG = es
12
12
  # options: id | iso6392 | iso6393 | ietf
13
13
  APP_SEARCH_KEY_LANG = iso6392
14
- APP_CORS_ORIGIN = *
14
+ APP_CORS_ORIGIN =
15
15
 
16
16
  # https://github.com/zeit/ms
17
17
  APP_ACCESS_TOKEN_EXPIRE = 10m
@@ -13,8 +13,13 @@
13
13
  // [inputId] binding ties <label for=...> to the internal Spartan
14
14
  // <hlm-combobox-input id=...>.
15
15
  const relSingular = it.fmt.toCamelCase(it.mappers.getRelationshipSingularName(it.property));
16
- const optionsName = relSingular + 'Options';
17
- const displayFieldTemplate = it.crossSchema.getTargetDisplayFieldTemplate(it.property.relationship.modulePath);
16
+ const optionsName = it.property.relationship?.modulePath ? relSingular + 'Options' : it.propCamel;
17
+ // A relational widget can land on a property WITHOUT a relationship (e.g. a
18
+ // scalar `array<varchar>`). Only resolve the target displayField when there
19
+ // is a relationship; otherwise render without the pipe (pre-regression shape).
20
+ const displayFieldTemplate = it.property.relationship?.modulePath
21
+ ? it.crossSchema.getTargetDisplayFieldTemplate(it.property.relationship.modulePath)
22
+ : null;
18
23
  const isRequired = it.property.nullable === false && it.property.type !== 'boolean';
19
24
  _%>
20
25
  <hlm-field class="<%= it.colSpan %>">
@@ -22,7 +27,7 @@ _%>
22
27
  <au-async-multiple-search-select
23
28
  [inputId]="'<%= it.propCamel %>'"
24
29
  [value]="signalForm.controls['<%= it.propCamel %>'].value ?? []"
25
- [options]="<%= optionsName %>() | toOptions:'<%= displayFieldTemplate %>'"
30
+ [options]="<%= optionsName %>()<% if (displayFieldTemplate) { %> | toOptions:'<%= displayFieldTemplate %>'<% } %>"
26
31
  [loading]="<%= relSingular %>Loading()"
27
32
  [placeholder]="t('Select')"
28
33
  [emptyMessage]="t('NoResults')"
@@ -14,8 +14,13 @@
14
14
  // [inputId] binding ties <label for=...> to the internal Spartan
15
15
  // <hlm-combobox-input id=...>.
16
16
  const relSingular = it.fmt.toCamelCase(it.mappers.getRelationshipSingularName(it.property));
17
- const optionsName = relSingular + 'Options';
18
- const displayFieldTemplate = it.crossSchema.getTargetDisplayFieldTemplate(it.property.relationship.modulePath);
17
+ const optionsName = it.property.relationship?.modulePath ? relSingular + 'Options' : it.propCamel;
18
+ // A relational widget can land on a property WITHOUT a relationship (e.g. a
19
+ // scalar `array<varchar>`). Only resolve the target displayField when there
20
+ // is a relationship; otherwise render without the pipe (pre-regression shape).
21
+ const displayFieldTemplate = it.property.relationship?.modulePath
22
+ ? it.crossSchema.getTargetDisplayFieldTemplate(it.property.relationship.modulePath)
23
+ : null;
19
24
  const isRequired = it.property.nullable === false && it.property.type !== 'boolean';
20
25
  _%>
21
26
  <hlm-field class="<%= it.colSpan %>">
@@ -23,7 +28,7 @@ _%>
23
28
  <au-async-search-select
24
29
  [inputId]="'<%= it.propCamel %>'"
25
30
  [value]="signalForm.controls['<%= it.propCamel %>'].value"
26
- [options]="<%= optionsName %>() | toOptions:'<%= displayFieldTemplate %>'"
31
+ [options]="<%= optionsName %>()<% if (displayFieldTemplate) { %> | toOptions:'<%= displayFieldTemplate %>'<% } %>"
27
32
  [loading]="<%= relSingular %>Loading()"
28
33
  [placeholder]="t('Select')"
29
34
  [emptyMessage]="t('NoResults')"
@@ -11,8 +11,13 @@
11
11
  // [inputId] binding ties <label for=...> to the internal Spartan
12
12
  // <hlm-combobox-input id=...>.
13
13
  const relSingular = it.fmt.toCamelCase(it.mappers.getRelationshipSingularName(it.property));
14
- const optionsName = relSingular + 'Options';
15
- const displayFieldTemplate = it.crossSchema.getTargetDisplayFieldTemplate(it.property.relationship.modulePath);
14
+ const optionsName = it.property.relationship?.modulePath ? relSingular + 'Options' : it.propCamel;
15
+ // A relational widget can land on a property WITHOUT a relationship (e.g. a
16
+ // scalar `array<varchar>`). Only resolve the target displayField when there
17
+ // is a relationship; otherwise render without the pipe (pre-regression shape).
18
+ const displayFieldTemplate = it.property.relationship?.modulePath
19
+ ? it.crossSchema.getTargetDisplayFieldTemplate(it.property.relationship.modulePath)
20
+ : null;
16
21
  const isRequired = it.property.nullable === false && it.property.type !== 'boolean';
17
22
  _%>
18
23
  <hlm-field class="<%= it.colSpan %>">
@@ -20,7 +25,7 @@ _%>
20
25
  <au-multiple-search-select
21
26
  [inputId]="'<%= it.propCamel %>'"
22
27
  [value]="signalForm.controls['<%= it.propCamel %>'].value ?? []"
23
- [options]="<%= optionsName %>() | toOptions:'<%= displayFieldTemplate %>'"
28
+ [options]="<%= optionsName %>()<% if (displayFieldTemplate) { %> | toOptions:'<%= displayFieldTemplate %>'<% } %>"
24
29
  [placeholder]="t('Select')"
25
30
  [emptyMessage]="t('NoResults')"
26
31
  <% if (isRequired) { -%>
@@ -17,8 +17,13 @@
17
17
  // input (depends on the companion `aurora-catalyst` PR exposing `inputId`
18
18
  // on the wrapper).
19
19
  const relSingular = it.fmt.toCamelCase(it.mappers.getRelationshipSingularName(it.property));
20
- const optionsName = relSingular + 'Options';
21
- const displayFieldTemplate = it.crossSchema.getTargetDisplayFieldTemplate(it.property.relationship.modulePath);
20
+ const optionsName = it.property.relationship?.modulePath ? relSingular + 'Options' : it.propCamel;
21
+ // A relational widget can land on a property WITHOUT a relationship (e.g. a
22
+ // scalar `array<varchar>`). Only resolve the target displayField when there
23
+ // is a relationship; otherwise render without the pipe (pre-regression shape).
24
+ const displayFieldTemplate = it.property.relationship?.modulePath
25
+ ? it.crossSchema.getTargetDisplayFieldTemplate(it.property.relationship.modulePath)
26
+ : null;
22
27
  const isRequired = it.property.nullable === false && it.property.type !== 'boolean';
23
28
  _%>
24
29
  <hlm-field class="<%= it.colSpan %>">
@@ -26,7 +31,7 @@ _%>
26
31
  <au-search-select
27
32
  [inputId]="'<%= it.propCamel %>'"
28
33
  [value]="signalForm.controls['<%= it.propCamel %>'].value"
29
- [options]="<%= optionsName %>() | toOptions:'<%= displayFieldTemplate %>'"
34
+ [options]="<%= optionsName %>()<% if (displayFieldTemplate) { %> | toOptions:'<%= displayFieldTemplate %>'<% } %>"
30
35
  [placeholder]="t('Select')"
31
36
  [emptyMessage]="t('NoResults')"
32
37
  <% if (isRequired) { -%>
@@ -322,6 +322,15 @@
322
322
  "multiple": false,
323
323
  "type": "option"
324
324
  },
325
+ "no-review": {
326
+ "description": "Skip the interactive review of generated `.origin` files.",
327
+ "exclusive": [
328
+ "review"
329
+ ],
330
+ "name": "no-review",
331
+ "allowNo": false,
332
+ "type": "boolean"
333
+ },
325
334
  "noGraphQLTypes": {
326
335
  "char": "g",
327
336
  "description": "Skip GraphQL type generation.",
@@ -336,6 +345,15 @@
336
345
  "allowNo": false,
337
346
  "type": "boolean"
338
347
  },
348
+ "review": {
349
+ "description": "Force the interactive review of generated `.origin` files.",
350
+ "exclusive": [
351
+ "no-review"
352
+ ],
353
+ "name": "review",
354
+ "allowNo": false,
355
+ "type": "boolean"
356
+ },
339
357
  "target": {
340
358
  "description": "Output subdirectory (default: \"backend\" for back, \"frontend\" for front).",
341
359
  "name": "target",
@@ -374,32 +392,27 @@
374
392
  "index.js"
375
393
  ]
376
394
  },
377
- "new": {
395
+ "keys": {
378
396
  "aliases": [],
379
- "args": {
380
- "name": {
381
- "description": "Name of the project to create.",
382
- "name": "name",
383
- "required": true
384
- }
385
- },
386
- "description": "Create a new Aurora monorepo project.",
397
+ "args": {},
398
+ "description": "Generate RSA private key and public key (defaults to ./backend/.keys)",
387
399
  "examples": [
388
- "<%= config.bin %> new my-project",
389
- "<%= config.bin %> new my-project --force"
400
+ "<%= config.bin %> <%= command.id %>",
401
+ "<%= config.bin %> <%= command.id %> --out ./keys"
390
402
  ],
391
403
  "flags": {
392
- "force": {
393
- "char": "f",
394
- "description": "Overwrite existing directory.",
395
- "name": "force",
396
- "allowNo": false,
397
- "type": "boolean"
404
+ "out": {
405
+ "char": "o",
406
+ "description": "Output directory for the key files (default: backend/.keys)",
407
+ "name": "out",
408
+ "hasDynamicHelp": false,
409
+ "multiple": false,
410
+ "type": "option"
398
411
  }
399
412
  },
400
413
  "hasDynamicHelp": false,
401
414
  "hiddenAliases": [],
402
- "id": "new",
415
+ "id": "keys",
403
416
  "pluginAlias": "@aurorajs.dev/catalyst-cli",
404
417
  "pluginName": "@aurorajs.dev/catalyst-cli",
405
418
  "pluginType": "core",
@@ -409,31 +422,36 @@
409
422
  "relativePath": [
410
423
  "dist",
411
424
  "commands",
412
- "new",
425
+ "keys",
413
426
  "index.js"
414
427
  ]
415
428
  },
416
- "keys": {
429
+ "new": {
417
430
  "aliases": [],
418
- "args": {},
419
- "description": "Generate RSA private key and public key (defaults to ./backend/.keys)",
431
+ "args": {
432
+ "name": {
433
+ "description": "Name of the project to create.",
434
+ "name": "name",
435
+ "required": true
436
+ }
437
+ },
438
+ "description": "Create a new Aurora monorepo project.",
420
439
  "examples": [
421
- "<%= config.bin %> <%= command.id %>",
422
- "<%= config.bin %> <%= command.id %> --out ./keys"
440
+ "<%= config.bin %> new my-project",
441
+ "<%= config.bin %> new my-project --force"
423
442
  ],
424
443
  "flags": {
425
- "out": {
426
- "char": "o",
427
- "description": "Output directory for the key files (default: backend/.keys)",
428
- "name": "out",
429
- "hasDynamicHelp": false,
430
- "multiple": false,
431
- "type": "option"
444
+ "force": {
445
+ "char": "f",
446
+ "description": "Overwrite existing directory.",
447
+ "name": "force",
448
+ "allowNo": false,
449
+ "type": "boolean"
432
450
  }
433
451
  },
434
452
  "hasDynamicHelp": false,
435
453
  "hiddenAliases": [],
436
- "id": "keys",
454
+ "id": "new",
437
455
  "pluginAlias": "@aurorajs.dev/catalyst-cli",
438
456
  "pluginName": "@aurorajs.dev/catalyst-cli",
439
457
  "pluginType": "core",
@@ -443,7 +461,7 @@
443
461
  "relativePath": [
444
462
  "dist",
445
463
  "commands",
446
- "keys",
464
+ "new",
447
465
  "index.js"
448
466
  ]
449
467
  },
@@ -1080,5 +1098,5 @@
1080
1098
  ]
1081
1099
  }
1082
1100
  },
1083
- "version": "1.0.0"
1101
+ "version": "1.0.2"
1084
1102
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@aurorajs.dev/catalyst-cli",
3
3
  "description": "A CLI tool to manage Aurora Catalyst projects",
4
- "version": "1.0.0",
4
+ "version": "1.0.2",
5
5
  "author": "José Carlos Rodríguez Palacín",
6
6
  "bin": {
7
7
  "catalyst": "./bin/run.js"