@remix-run/cli 0.3.4 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (119) hide show
  1. package/README.md +105 -1
  2. package/dist/index.js +1 -1
  3. package/dist/lib/app-root.d.ts +11 -0
  4. package/dist/lib/app-root.d.ts.map +1 -0
  5. package/dist/lib/app-root.js +38 -0
  6. package/dist/lib/bootstrap-project.js +2 -2
  7. package/dist/lib/cli-context.d.ts +6 -0
  8. package/dist/lib/cli-context.d.ts.map +1 -1
  9. package/dist/lib/cli-context.js +12 -1
  10. package/dist/lib/cli.d.ts +3 -2
  11. package/dist/lib/cli.d.ts.map +1 -1
  12. package/dist/lib/cli.js +42 -14
  13. package/dist/lib/commands/completion.js +4 -4
  14. package/dist/lib/commands/db.d.ts +4 -0
  15. package/dist/lib/commands/db.d.ts.map +1 -0
  16. package/dist/lib/commands/db.js +283 -0
  17. package/dist/lib/commands/doctor.d.ts +5 -0
  18. package/dist/lib/commands/doctor.d.ts.map +1 -1
  19. package/dist/lib/commands/doctor.js +26 -251
  20. package/dist/lib/commands/help.d.ts.map +1 -1
  21. package/dist/lib/commands/help.js +17 -9
  22. package/dist/lib/commands/new.js +6 -6
  23. package/dist/lib/commands/routes.js +6 -6
  24. package/dist/lib/commands/test.d.ts +20 -1
  25. package/dist/lib/commands/test.d.ts.map +1 -1
  26. package/dist/lib/commands/test.js +353 -10
  27. package/dist/lib/commands/version.js +3 -3
  28. package/dist/lib/completion.d.ts +1 -1
  29. package/dist/lib/completion.d.ts.map +1 -1
  30. package/dist/lib/completion.js +229 -6
  31. package/dist/lib/controller-files.d.ts +0 -8
  32. package/dist/lib/controller-files.d.ts.map +1 -1
  33. package/dist/lib/controller-files.js +0 -23
  34. package/dist/lib/controller-ownership.d.ts +4 -12
  35. package/dist/lib/controller-ownership.d.ts.map +1 -1
  36. package/dist/lib/controller-ownership.js +31 -127
  37. package/dist/lib/database-command.d.ts +20 -0
  38. package/dist/lib/database-command.d.ts.map +1 -0
  39. package/dist/lib/database-command.js +7 -0
  40. package/dist/lib/doctor/controller-findings.d.ts +2 -2
  41. package/dist/lib/doctor/controller-findings.d.ts.map +1 -1
  42. package/dist/lib/doctor/controller-findings.js +23 -39
  43. package/dist/lib/doctor/controllers.d.ts +1 -2
  44. package/dist/lib/doctor/controllers.d.ts.map +1 -1
  45. package/dist/lib/doctor/controllers.js +4 -7
  46. package/dist/lib/doctor/environment.js +2 -2
  47. package/dist/lib/doctor/fixes.js +1 -1
  48. package/dist/lib/doctor/project.d.ts.map +1 -1
  49. package/dist/lib/doctor/project.js +3 -212
  50. package/dist/lib/doctor/run.d.ts +9 -0
  51. package/dist/lib/doctor/run.d.ts.map +1 -0
  52. package/dist/lib/doctor/run.js +237 -0
  53. package/dist/lib/doctor/types.d.ts +2 -1
  54. package/dist/lib/doctor/types.d.ts.map +1 -1
  55. package/dist/lib/doctor/types.js +4 -1
  56. package/dist/lib/errors.d.ts +30 -0
  57. package/dist/lib/errors.d.ts.map +1 -1
  58. package/dist/lib/errors.js +55 -0
  59. package/dist/lib/help-text.js +1 -1
  60. package/dist/lib/parse-args.d.ts.map +1 -1
  61. package/dist/lib/parse-args.js +17 -4
  62. package/dist/lib/remix-config.d.ts +75 -0
  63. package/dist/lib/remix-config.d.ts.map +1 -0
  64. package/dist/lib/remix-config.js +411 -0
  65. package/dist/lib/reporter.js +1 -1
  66. package/dist/lib/route-map.js +13 -33
  67. package/package.json +13 -7
  68. package/schema/remix.json +242 -0
  69. package/src/lib/app-root.ts +46 -0
  70. package/src/lib/cli-context.ts +17 -0
  71. package/src/lib/cli.ts +41 -6
  72. package/src/lib/commands/db.ts +377 -0
  73. package/src/lib/commands/doctor.ts +27 -329
  74. package/src/lib/commands/help.ts +10 -1
  75. package/src/lib/commands/test.ts +453 -12
  76. package/src/lib/completion.ts +321 -6
  77. package/src/lib/controller-files.ts +0 -38
  78. package/src/lib/controller-ownership.ts +50 -203
  79. package/src/lib/database-command.ts +31 -0
  80. package/src/lib/doctor/controller-findings.ts +30 -47
  81. package/src/lib/doctor/controllers.ts +2 -6
  82. package/src/lib/doctor/project.ts +0 -236
  83. package/src/lib/doctor/run.ts +311 -0
  84. package/src/lib/doctor/types.ts +6 -5
  85. package/src/lib/errors.ts +65 -0
  86. package/src/lib/parse-args.ts +19 -3
  87. package/src/lib/remix-config.ts +639 -0
  88. package/src/lib/route-map.ts +11 -48
  89. package/template/.agents/skills/remix/SKILL.md +38 -27
  90. package/template/.agents/skills/remix/references/assets-and-browser-modules.md +54 -11
  91. package/template/.agents/skills/remix/references/auth-and-sessions.md +5 -5
  92. package/template/.agents/skills/remix/references/data-and-validation.md +15 -15
  93. package/template/.agents/skills/remix/references/hydration-frames-navigation.md +63 -9
  94. package/template/.agents/skills/remix/references/middleware-and-server.md +68 -6
  95. package/template/.agents/skills/remix/references/mixins-styling-events.md +2 -0
  96. package/template/.agents/skills/remix/references/routing-and-controllers.md +8 -8
  97. package/template/.agents/skills/remix/references/testing-patterns.md +19 -18
  98. package/template/AGENTS.md +6 -3
  99. package/template/README.md +6 -3
  100. package/template/app/actions/controller.tsx +1 -1
  101. package/template/app/{ui → actions}/document.tsx +7 -5
  102. package/template/app/{ui/scaffold-home-page.tsx → actions/home-page.tsx} +1 -2
  103. package/template/app/actions/public/entry.ts +48 -0
  104. package/template/app/{assets → actions/public}/prompt-button.tsx +29 -25
  105. package/template/app/assets.ts +15 -2
  106. package/template/app/middleware/render.tsx +8 -2
  107. package/template/hmr.ts +46 -0
  108. package/template/package.json +1 -0
  109. package/template/server.ts +8 -1
  110. package/template/tsconfig.json +1 -1
  111. package/dist/lib/doctor/controller-fix-plans.d.ts +0 -4
  112. package/dist/lib/doctor/controller-fix-plans.d.ts.map +0 -1
  113. package/dist/lib/doctor/controller-fix-plans.js +0 -119
  114. package/dist/lib/doctor/controller-placeholders.d.ts +0 -3
  115. package/dist/lib/doctor/controller-placeholders.d.ts.map +0 -1
  116. package/dist/lib/doctor/controller-placeholders.js +0 -52
  117. package/src/lib/doctor/controller-fix-plans.ts +0 -168
  118. package/src/lib/doctor/controller-placeholders.ts +0 -70
  119. package/template/app/assets/entry.ts +0 -17
@@ -1,6 +1,39 @@
1
+ import { testCommandFlags } from './commands/test.js';
1
2
  const COMPLETION_SHELLS = ['bash', 'zsh'];
2
- const HELP_COMMANDS = ['completion', 'doctor', 'help', 'new', 'routes', 'test', 'version'];
3
- const ROOT_COMMANDS = ['completion', 'doctor', 'help', 'new', 'routes', 'test', 'version'];
3
+ const DB_COMMANDS = ['migrate', 'reset', 'seed', 'status', 'wipe'];
4
+ const HELP_COMMANDS = [
5
+ 'completion',
6
+ 'db',
7
+ 'doctor',
8
+ 'help',
9
+ 'new',
10
+ 'routes',
11
+ 'test',
12
+ 'version',
13
+ ];
14
+ const ROOT_COMMANDS = [
15
+ 'completion',
16
+ 'db',
17
+ 'doctor',
18
+ 'help',
19
+ 'new',
20
+ 'routes',
21
+ 'test',
22
+ 'version',
23
+ ];
24
+ // Derived from the `remix test` flag table so completion stays in sync with
25
+ // argument parsing and help text.
26
+ const TEST_BOOLEAN_FLAGS = testCommandFlags
27
+ .filter((flag) => flag.type === 'boolean')
28
+ .map((flag) => flag.name);
29
+ const TEST_VALUE_FLAGS = testCommandFlags
30
+ .filter((flag) => flag.type === 'string')
31
+ .map((flag) => flag.name);
32
+ const TEST_BOOLEAN_FLAG_SET = new Set(TEST_BOOLEAN_FLAGS);
33
+ const TEST_VALUE_FLAG_SET = new Set(TEST_VALUE_FLAGS);
34
+ const TEST_REPEATABLE_FLAGS = new Set(testCommandFlags.filter((flag) => flag.multiple).map((flag) => flag.name));
35
+ const TEST_FILE_VALUE_FLAGS = new Set(testCommandFlags.filter((flag) => flag.files).map((flag) => flag.name));
36
+ const TEST_FLAG_ALIASES = new Map(testCommandFlags.flatMap((flag) => flag.alias === undefined ? [] : [[flag.alias, flag.name]]));
4
37
  export function isCompletionShell(value) {
5
38
  return COMPLETION_SHELLS.includes(value);
6
39
  }
@@ -8,6 +41,9 @@ export function getCompletionResult(words, currentIndex) {
8
41
  let resolvedIndex = Math.max(0, currentIndex);
9
42
  let currentWord = words[resolvedIndex] ?? '';
10
43
  let tokens = getTokensBeforeCursor(words, resolvedIndex);
44
+ if (currentWord.startsWith('--config=')) {
45
+ return { mode: 'files' };
46
+ }
11
47
  return completeTopLevel(tokens, currentWord);
12
48
  }
13
49
  export function renderCompletionResult(result) {
@@ -33,7 +69,9 @@ if type complete &>/dev/null; then
33
69
  local lines
34
70
 
35
71
  if type _get_comp_words_by_ref &>/dev/null; then
36
- _get_comp_words_by_ref -w words -i cword
72
+ # -n = keeps --config=<path> together as one word; bash's default
73
+ # COMP_WORDBREAKS would otherwise split it at the equals sign.
74
+ _get_comp_words_by_ref -n = -w words -i cword
37
75
  else
38
76
  cword="$COMP_CWORD"
39
77
  words=("\${COMP_WORDS[@]}")
@@ -52,7 +90,15 @@ if type complete &>/dev/null; then
52
90
  fi
53
91
 
54
92
  if [[ "$mode" == "mode:files" ]]; then
55
- COMPREPLY=($(compgen -f -- "$current"))
93
+ if [[ "$current" == --config=* ]]; then
94
+ # Readline still breaks the insertion point at '=', so complete the
95
+ # path portion alone; prefixing --config= would duplicate the flag.
96
+ local config_value
97
+ config_value="\${current#--config=}"
98
+ COMPREPLY=($(compgen -f -- "$config_value"))
99
+ else
100
+ COMPREPLY=($(compgen -f -- "$current"))
101
+ fi
56
102
  return 0
57
103
  fi
58
104
 
@@ -78,6 +124,9 @@ elif type compdef &>/dev/null; then
78
124
  fi
79
125
 
80
126
  if [[ "$mode" == "mode:files" ]]; then
127
+ if [[ "\${words[CURRENT]}" == --config=* ]]; then
128
+ compset -P '--config='
129
+ fi
81
130
  if autoload -U +X _files 2>/dev/null; then
82
131
  _files
83
132
  else
@@ -99,6 +148,18 @@ function completeTopLevel(tokens, currentWord) {
99
148
  let index = 0;
100
149
  while (index < tokens.length) {
101
150
  let token = tokens[index];
151
+ if (token === '--config') {
152
+ usedFlags.add('--config');
153
+ if (tokens[index + 1] === undefined)
154
+ return { mode: 'files' };
155
+ index += 2;
156
+ continue;
157
+ }
158
+ if (token.startsWith('--config=')) {
159
+ usedFlags.add('--config');
160
+ index++;
161
+ continue;
162
+ }
102
163
  if (token === '--no-color') {
103
164
  usedFlags.add('--no-color');
104
165
  index++;
@@ -117,17 +178,24 @@ function completeTopLevel(tokens, currentWord) {
117
178
  return completeValues(getTopLevelSuggestions(currentWord, usedFlags), currentWord);
118
179
  }
119
180
  function completeCommand(command, tokens, currentWord, usedGlobalFlags) {
181
+ if (expectsGlobalConfigValue(tokens)) {
182
+ return { mode: 'files' };
183
+ }
120
184
  if (command === 'help') {
121
185
  return completeHelp(tokens, currentWord, usedGlobalFlags);
122
186
  }
123
187
  if (command === 'new') {
124
188
  return completeNew(tokens, currentWord, usedGlobalFlags);
125
189
  }
190
+ if (command === 'db') {
191
+ return completeDb(tokens, currentWord, usedGlobalFlags);
192
+ }
126
193
  if (command === 'doctor') {
127
194
  return completeSimpleFlags(tokens, currentWord, usedGlobalFlags, [
128
195
  '--fix',
129
196
  '--json',
130
197
  '--strict',
198
+ '--no-strict',
131
199
  ]);
132
200
  }
133
201
  if (command === 'routes') {
@@ -137,13 +205,87 @@ function completeCommand(command, tokens, currentWord, usedGlobalFlags) {
137
205
  return completeSimpleFlags(tokens, currentWord, usedGlobalFlags, []);
138
206
  }
139
207
  if (command === 'test') {
140
- return completeSimpleFlags(tokens, currentWord, usedGlobalFlags, ['--coverage', '--watch']);
208
+ return completeTest(tokens, currentWord, usedGlobalFlags);
141
209
  }
142
210
  if (command === 'completion') {
143
211
  return completeCompletionCommand(tokens, currentWord, usedGlobalFlags);
144
212
  }
145
213
  return completeValues([], currentWord);
146
214
  }
215
+ function completeTest(tokens, currentWord, usedGlobalFlags) {
216
+ let filteredTokens = filterGlobalCommandTokens(tokens, usedGlobalFlags);
217
+ if (filteredTokens == null) {
218
+ return completeValues([], currentWord);
219
+ }
220
+ let usedFlags = new Set();
221
+ let expectedValueFor;
222
+ let afterSeparator = false;
223
+ for (let token of filteredTokens) {
224
+ if (expectedValueFor != null) {
225
+ expectedValueFor = undefined;
226
+ continue;
227
+ }
228
+ // Everything after a bare `--` is a positional test file glob
229
+ if (afterSeparator) {
230
+ continue;
231
+ }
232
+ if (token === '--') {
233
+ afterSeparator = true;
234
+ continue;
235
+ }
236
+ if (token.startsWith('--')) {
237
+ // `--flag=value` carries its value inline
238
+ let equalsIndex = token.indexOf('=');
239
+ let flag = equalsIndex === -1 ? token : token.slice(0, equalsIndex);
240
+ if (TEST_VALUE_FLAG_SET.has(flag)) {
241
+ usedFlags.add(flag);
242
+ if (equalsIndex === -1) {
243
+ expectedValueFor = flag;
244
+ }
245
+ continue;
246
+ }
247
+ if (TEST_BOOLEAN_FLAG_SET.has(flag)) {
248
+ usedFlags.add(flag);
249
+ continue;
250
+ }
251
+ return completeValues([], currentWord);
252
+ }
253
+ if (token.startsWith('-') && token !== '-') {
254
+ let aliased = TEST_FLAG_ALIASES.get(token.slice(0, 2));
255
+ // A value alias may carry its value inline (`-c1`); a bare alias
256
+ // expects the value in the next token.
257
+ if (aliased !== undefined && TEST_VALUE_FLAG_SET.has(aliased)) {
258
+ usedFlags.add(aliased);
259
+ if (token.length === 2) {
260
+ expectedValueFor = aliased;
261
+ }
262
+ continue;
263
+ }
264
+ // A group of boolean aliases (`-qw`)
265
+ let group = [...token.slice(1)].map((char) => TEST_FLAG_ALIASES.get(`-${char}`));
266
+ if (group.every((flag) => flag !== undefined && TEST_BOOLEAN_FLAG_SET.has(flag))) {
267
+ for (let flag of group) {
268
+ usedFlags.add(flag);
269
+ }
270
+ continue;
271
+ }
272
+ return completeValues([], currentWord);
273
+ }
274
+ // Anything else is a positional test file glob
275
+ }
276
+ if (expectedValueFor != null) {
277
+ return TEST_FILE_VALUE_FLAGS.has(expectedValueFor) ? { mode: 'files' } : { mode: 'none' };
278
+ }
279
+ if (afterSeparator) {
280
+ return { mode: 'files' };
281
+ }
282
+ let longFlags = [...TEST_BOOLEAN_FLAGS, ...TEST_VALUE_FLAGS].filter((flag) => !usedFlags.has(flag) || TEST_REPEATABLE_FLAGS.has(flag));
283
+ let shortFlags = [...TEST_FLAG_ALIASES].flatMap(([short, long]) => !usedFlags.has(long) || TEST_REPEATABLE_FLAGS.has(long) ? [short] : []);
284
+ let suggestions = withHelpFlags([...longFlags, ...shortFlags], usedGlobalFlags);
285
+ return currentWord.startsWith('-') || currentWord === ''
286
+ ? completeValues(suggestions, currentWord)
287
+ : { mode: 'files' };
288
+ }
147
289
  function completeHelp(tokens, currentWord, usedGlobalFlags) {
148
290
  let filteredTokens = filterGlobalCommandTokens(tokens, usedGlobalFlags);
149
291
  if (filteredTokens == null) {
@@ -235,6 +377,62 @@ function completeRoutes(tokens, currentWord, usedGlobalFlags) {
235
377
  ], usedGlobalFlags);
236
378
  return completeValues(flags, currentWord);
237
379
  }
380
+ function completeDb(tokens, currentWord, usedGlobalFlags) {
381
+ let filteredTokens = filterGlobalCommandTokens(tokens, usedGlobalFlags);
382
+ if (filteredTokens == null) {
383
+ return completeValues([], currentWord);
384
+ }
385
+ if (filteredTokens.length === 0) {
386
+ return completeValues(withHelpFlags([...DB_COMMANDS], usedGlobalFlags), currentWord);
387
+ }
388
+ let [subcommand, ...rest] = filteredTokens;
389
+ if (subcommand === 'migrate') {
390
+ return completeDbOptions(rest, currentWord, usedGlobalFlags, [], ['--connection-env', '--journal-table', '--migrations', '--to']);
391
+ }
392
+ if (subcommand === 'reset') {
393
+ return completeDbOptions(rest, currentWord, usedGlobalFlags, ['--force'], ['--connection-env', '--journal-table', '--migrations', '--seed']);
394
+ }
395
+ if (subcommand === 'wipe') {
396
+ return completeDbOptions(rest, currentWord, usedGlobalFlags, ['--force'], ['--connection-env']);
397
+ }
398
+ if (subcommand === 'seed') {
399
+ return completeDbOptions(rest, currentWord, usedGlobalFlags, [], ['--connection-env', '--seed']);
400
+ }
401
+ if (subcommand === 'status') {
402
+ return completeDbOptions(rest, currentWord, usedGlobalFlags, [], ['--connection-env', '--journal-table', '--migrations']);
403
+ }
404
+ return completeValues([], currentWord);
405
+ }
406
+ function completeDbOptions(tokens, currentWord, usedGlobalFlags, booleanFlags, valueFlags) {
407
+ let usedFlags = new Set();
408
+ let expectedValueFor;
409
+ for (let token of tokens) {
410
+ if (expectedValueFor !== undefined) {
411
+ expectedValueFor = undefined;
412
+ continue;
413
+ }
414
+ let equalsIndex = token.indexOf('=');
415
+ let flag = equalsIndex === -1 ? token : token.slice(0, equalsIndex);
416
+ if (booleanFlags.includes(flag)) {
417
+ usedFlags.add(flag);
418
+ continue;
419
+ }
420
+ if (valueFlags.includes(flag)) {
421
+ usedFlags.add(flag);
422
+ if (equalsIndex === -1)
423
+ expectedValueFor = flag;
424
+ continue;
425
+ }
426
+ return completeValues([], currentWord);
427
+ }
428
+ if (expectedValueFor !== undefined) {
429
+ return expectedValueFor === '--migrations' || expectedValueFor === '--seed'
430
+ ? { mode: 'files' }
431
+ : { mode: 'none' };
432
+ }
433
+ let available = [...booleanFlags, ...valueFlags].filter((flag) => !usedFlags.has(flag));
434
+ return completeValues(withHelpFlags(available, usedGlobalFlags), currentWord);
435
+ }
238
436
  function completeCompletionCommand(tokens, currentWord, usedGlobalFlags) {
239
437
  let filteredTokens = filterGlobalCommandTokens(tokens, usedGlobalFlags);
240
438
  if (filteredTokens == null) {
@@ -262,6 +460,7 @@ function completeSimpleFlags(tokens, currentWord, usedGlobalFlags, commandFlags)
262
460
  }
263
461
  function getTopLevelSuggestions(currentWord, usedFlags) {
264
462
  let flags = [
463
+ ...(!usedFlags.has('--config') ? ['--config'] : []),
265
464
  ...(!usedFlags.has('-h') ? ['-h', '--help'] : []),
266
465
  ...(!usedFlags.has('--no-color') ? ['--no-color'] : []),
267
466
  ...(!usedFlags.has('-v') ? ['-v', '--version'] : []),
@@ -274,13 +473,24 @@ function getTopLevelSuggestions(currentWord, usedFlags) {
274
473
  function withHelpFlags(values, usedGlobalFlags) {
275
474
  return [
276
475
  ...values,
476
+ ...(!usedGlobalFlags.has('--config') ? ['--config'] : []),
277
477
  ...(!usedGlobalFlags.has('-h') ? ['-h', '--help'] : []),
278
478
  ...(!usedGlobalFlags.has('--no-color') ? ['--no-color'] : []),
279
479
  ];
280
480
  }
281
481
  function filterGlobalCommandTokens(tokens, usedGlobalFlags) {
282
482
  let filtered = [];
283
- for (let token of tokens) {
483
+ for (let index = 0; index < tokens.length; index++) {
484
+ let token = tokens[index];
485
+ if (token === '--config') {
486
+ usedGlobalFlags.add('--config');
487
+ index++;
488
+ continue;
489
+ }
490
+ if (token.startsWith('--config=')) {
491
+ usedGlobalFlags.add('--config');
492
+ continue;
493
+ }
284
494
  if (token === '--no-color') {
285
495
  usedGlobalFlags.add('--no-color');
286
496
  continue;
@@ -293,6 +503,19 @@ function filterGlobalCommandTokens(tokens, usedGlobalFlags) {
293
503
  }
294
504
  return filtered;
295
505
  }
506
+ function expectsGlobalConfigValue(tokens) {
507
+ let expectsValue = false;
508
+ for (let token of tokens) {
509
+ if (expectsValue) {
510
+ expectsValue = false;
511
+ continue;
512
+ }
513
+ if (token === '--config') {
514
+ expectsValue = true;
515
+ }
516
+ }
517
+ return expectsValue;
518
+ }
296
519
  function completeValues(values, currentWord) {
297
520
  return {
298
521
  mode: 'values',
@@ -1,13 +1,5 @@
1
- declare const OWNER_FILE_EXTENSIONS: readonly [".ts", ".tsx", ".js", ".jsx"];
2
- export type OwnerFileExtension = (typeof OWNER_FILE_EXTENSIONS)[number];
3
1
  export declare function getControllerOwnerCandidates(segments: string[]): string[];
4
2
  export declare function getRouteSubtreePath(segments: string[]): string;
5
3
  export declare function getPreferredOwnerDisplayPath(candidates: string[]): string;
6
- export declare function getOwnerCandidateForExtension(candidates: string[], extension: OwnerFileExtension): string | null;
7
- export declare function getOwnerFileExtension(filePath: string): OwnerFileExtension | null;
8
4
  export declare function toDiskSegment(segment: string): string;
9
- export declare function isControllerEntryFileName(fileName: string): boolean;
10
- export declare function getOwnerModuleBaseName(fileName: string): string | null;
11
- export declare function isActionFileName(fileName: string): boolean;
12
- export {};
13
5
  //# sourceMappingURL=controller-files.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"controller-files.d.ts","sourceRoot":"","sources":["../../src/lib/controller-files.ts"],"names":[],"mappings":"AAEA,QAAA,MAAM,qBAAqB,yCAA0C,CAAA;AAGrE,MAAM,MAAM,kBAAkB,GAAG,CAAC,OAAO,qBAAqB,CAAC,CAAC,MAAM,CAAC,CAAA;AAEvE,wBAAgB,4BAA4B,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,CAIzE;AAED,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,MAAM,CAE9D;AAED,wBAAgB,4BAA4B,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,MAAM,CAGzE;AAED,wBAAgB,6BAA6B,CAC3C,UAAU,EAAE,MAAM,EAAE,EACpB,SAAS,EAAE,kBAAkB,GAC5B,MAAM,GAAG,IAAI,CAEf;AAED,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,kBAAkB,GAAG,IAAI,CAEjF;AAED,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAYrD;AAED,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAEnE;AAED,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAQtE;AAED,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAS1D"}
1
+ {"version":3,"file":"controller-files.d.ts","sourceRoot":"","sources":["../../src/lib/controller-files.ts"],"names":[],"mappings":"AAKA,wBAAgB,4BAA4B,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,CAIzE;AAED,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,MAAM,CAE9D;AAED,wBAAgB,4BAA4B,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,MAAM,CAGzE;AAED,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAYrD"}
@@ -11,12 +11,6 @@ export function getPreferredOwnerDisplayPath(candidates) {
11
11
  let tsxCandidate = candidates.find((candidate) => candidate.endsWith('.tsx'));
12
12
  return tsxCandidate ?? candidates[0] ?? '';
13
13
  }
14
- export function getOwnerCandidateForExtension(candidates, extension) {
15
- return candidates.find((candidate) => candidate.endsWith(extension)) ?? null;
16
- }
17
- export function getOwnerFileExtension(filePath) {
18
- return OWNER_FILE_EXTENSIONS.find((extension) => filePath.endsWith(extension)) ?? null;
19
- }
20
14
  export function toDiskSegment(segment) {
21
15
  let diskSegment = segment
22
16
  .replace(/[\\/]+/g, '-')
@@ -29,23 +23,6 @@ export function toDiskSegment(segment) {
29
23
  .replace(/^-+|-+$/g, '');
30
24
  return diskSegment.length === 0 ? 'route' : diskSegment;
31
25
  }
32
- export function isControllerEntryFileName(fileName) {
33
- return OWNER_FILE_EXTENSIONS.some((extension) => fileName === `controller${extension}`);
34
- }
35
- export function getOwnerModuleBaseName(fileName) {
36
- let extension = OWNER_FILE_EXTENSIONS.find((ownerExtension) => fileName.endsWith(ownerExtension));
37
- if (extension == null) {
38
- return null;
39
- }
40
- return fileName.slice(0, -extension.length);
41
- }
42
- export function isActionFileName(fileName) {
43
- let baseName = getOwnerModuleBaseName(fileName);
44
- return (baseName != null &&
45
- baseName !== 'controller' &&
46
- !baseName.endsWith('.test') &&
47
- !baseName.endsWith('.spec'));
48
- }
49
26
  function normalizeRelativePath(filePath) {
50
27
  return filePath.split(path.sep).join('/');
51
28
  }
@@ -7,15 +7,13 @@ export interface OwnershipRouteNode {
7
7
  method?: string;
8
8
  name: string;
9
9
  }
10
- export interface ControllerDirectoryScan {
11
- controllerEntryPaths: Set<string>;
12
- routeDirectoryPaths: Set<string>;
13
- routeLocalFilePaths: Set<string>;
14
- }
15
10
  export interface RouteDirectoryPlan {
16
11
  directoryPath: string;
17
12
  routeName: string;
18
13
  }
14
+ export interface RouteDirectory extends RouteDirectoryPlan {
15
+ exists: boolean;
16
+ }
19
17
  export interface OwnedSubtreePlan {
20
18
  entryCandidates: string[];
21
19
  entryDisplayPath: string;
@@ -24,15 +22,9 @@ export interface OwnedSubtreePlan {
24
22
  }
25
23
  export interface OwnedSubtree extends OwnedSubtreePlan {
26
24
  actualEntryPath: string | null;
27
- actualEntryPaths: string[];
28
- claimedFilePaths: string[];
29
- claimedRouteLocalFilePaths: string[];
30
25
  }
31
26
  export interface ControllerOwnership {
32
- orphanControllerPaths: string[];
33
- orphanRouteDirectoryPaths: string[];
34
- routeDirectories: RouteDirectoryPlan[];
35
- scan: ControllerDirectoryScan;
27
+ routeDirectories: RouteDirectory[];
36
28
  subtrees: OwnedSubtree[];
37
29
  }
38
30
  export declare function inspectControllerOwnership(appRoot: string, tree: OwnershipRouteNode[]): Promise<ControllerOwnership>;
@@ -1 +1 @@
1
- {"version":3,"file":"controller-ownership.d.ts","sourceRoot":"","sources":["../../src/lib/controller-ownership.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAA;AAIvD,eAAO,MAAM,eAAe,WAAW,CAAA;AAEvC,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,kBAAkB,EAAE,CAAA;IAC9B,GAAG,EAAE,MAAM,CAAA;IACX,IAAI,EAAE,iBAAiB,CAAA;IACvB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,MAAM,CAAA;CACb;AAED,MAAM,WAAW,uBAAuB;IACtC,oBAAoB,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;IACjC,mBAAmB,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;IAChC,mBAAmB,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;CACjC;AAED,MAAM,WAAW,kBAAkB;IACjC,aAAa,EAAE,MAAM,CAAA;IACrB,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,WAAW,gBAAgB;IAC/B,eAAe,EAAE,MAAM,EAAE,CAAA;IACzB,gBAAgB,EAAE,MAAM,CAAA;IACxB,SAAS,EAAE,MAAM,CAAA;IACjB,WAAW,EAAE,MAAM,CAAA;CACpB;AAED,MAAM,WAAW,YAAa,SAAQ,gBAAgB;IACpD,eAAe,EAAE,MAAM,GAAG,IAAI,CAAA;IAC9B,gBAAgB,EAAE,MAAM,EAAE,CAAA;IAC1B,gBAAgB,EAAE,MAAM,EAAE,CAAA;IAC1B,0BAA0B,EAAE,MAAM,EAAE,CAAA;CACrC;AAED,MAAM,WAAW,mBAAmB;IAClC,qBAAqB,EAAE,MAAM,EAAE,CAAA;IAC/B,yBAAyB,EAAE,MAAM,EAAE,CAAA;IACnC,gBAAgB,EAAE,kBAAkB,EAAE,CAAA;IACtC,IAAI,EAAE,uBAAuB,CAAA;IAC7B,QAAQ,EAAE,YAAY,EAAE,CAAA;CACzB;AAED,wBAAsB,0BAA0B,CAC9C,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,kBAAkB,EAAE,GACzB,OAAO,CAAC,mBAAmB,CAAC,CAa9B;AAED,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,kBAAkB,EAAE,EAC1B,cAAc,GAAE,MAAM,EAAO,EAC7B,QAAQ,GAAE,gBAAgB,EAAO,GAChC,gBAAgB,EAAE,CAsBpB;AAED,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,kBAAkB,EAAE,EAC1B,cAAc,GAAE,MAAM,EAAO,EAC7B,WAAW,GAAE,kBAAkB,EAAO,GACrC,kBAAkB,EAAE,CAetB"}
1
+ {"version":3,"file":"controller-ownership.d.ts","sourceRoot":"","sources":["../../src/lib/controller-ownership.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAA;AAEvD,eAAO,MAAM,eAAe,WAAW,CAAA;AAEvC,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,kBAAkB,EAAE,CAAA;IAC9B,GAAG,EAAE,MAAM,CAAA;IACX,IAAI,EAAE,iBAAiB,CAAA;IACvB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,MAAM,CAAA;CACb;AAED,MAAM,WAAW,kBAAkB;IACjC,aAAa,EAAE,MAAM,CAAA;IACrB,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,WAAW,cAAe,SAAQ,kBAAkB;IACxD,MAAM,EAAE,OAAO,CAAA;CAChB;AAED,MAAM,WAAW,gBAAgB;IAC/B,eAAe,EAAE,MAAM,EAAE,CAAA;IACzB,gBAAgB,EAAE,MAAM,CAAA;IACxB,SAAS,EAAE,MAAM,CAAA;IACjB,WAAW,EAAE,MAAM,CAAA;CACpB;AAED,MAAM,WAAW,YAAa,SAAQ,gBAAgB;IACpD,eAAe,EAAE,MAAM,GAAG,IAAI,CAAA;CAC/B;AAED,MAAM,WAAW,mBAAmB;IAClC,gBAAgB,EAAE,cAAc,EAAE,CAAA;IAClC,QAAQ,EAAE,YAAY,EAAE,CAAA;CACzB;AAED,wBAAsB,0BAA0B,CAC9C,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,kBAAkB,EAAE,GACzB,OAAO,CAAC,mBAAmB,CAAC,CAY9B;AAED,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,kBAAkB,EAAE,EAC1B,cAAc,GAAE,MAAM,EAAO,EAC7B,QAAQ,GAAE,gBAAgB,EAAO,GAChC,gBAAgB,EAAE,CAsBpB;AAED,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,kBAAkB,EAAE,EAC1B,cAAc,GAAE,MAAM,EAAO,EAC7B,WAAW,GAAE,kBAAkB,EAAO,GACrC,kBAAkB,EAAE,CAetB"}
@@ -1,18 +1,16 @@
1
1
  import * as fs from 'node:fs/promises';
2
2
  import * as path from 'node:path';
3
- import { getControllerOwnerCandidates, getOwnerModuleBaseName, getPreferredOwnerDisplayPath, getRouteSubtreePath, isControllerEntryFileName, toDiskSegment, } from "./controller-files.js";
4
- const ACTIONS_PATH_PREFIX = 'app/actions/';
3
+ import { getControllerOwnerCandidates, getPreferredOwnerDisplayPath, getRouteSubtreePath, toDiskSegment, } from './controller-files.js';
5
4
  export const ROOT_ROUTE_NAME = '<root>';
6
5
  export async function inspectControllerOwnership(appRoot, tree) {
7
6
  let subtreePlans = buildOwnedSubtrees(tree);
8
- let routeDirectories = buildRouteDirectories(tree);
9
- let scan = await scanControllersDirectory(appRoot);
10
- let subtrees = applyScanToSubtrees(subtreePlans, scan);
7
+ let routeDirectoryPlans = buildRouteDirectories(tree);
8
+ let [routeDirectories, subtrees] = await Promise.all([
9
+ inspectRouteDirectories(appRoot, routeDirectoryPlans),
10
+ inspectOwnedSubtrees(appRoot, subtreePlans),
11
+ ]);
11
12
  return {
12
- orphanControllerPaths: getOrphanControllerPaths(subtreePlans, scan),
13
- orphanRouteDirectoryPaths: getOrphanRouteDirectoryPaths(routeDirectories, scan),
14
13
  routeDirectories,
15
- scan,
16
14
  subtrees,
17
15
  };
18
16
  }
@@ -60,130 +58,36 @@ function addSubtreePlan(routeName, segments, subtrees) {
60
58
  subtreePath: getRouteSubtreePath(segments),
61
59
  });
62
60
  }
63
- async function scanControllersDirectory(appRoot) {
64
- let actionsDir = path.join(appRoot, 'app', 'actions');
65
- let controllerEntryPaths = new Set();
66
- let routeDirectoryPaths = new Set();
67
- let routeLocalFilePaths = new Set();
68
- async function walk(currentDir, isRoot) {
69
- let entries;
70
- try {
71
- entries = await fs.readdir(currentDir, { withFileTypes: true });
72
- }
73
- catch (error) {
74
- let nodeError = error;
75
- if (nodeError.code === 'ENOENT' && isRoot) {
76
- return;
77
- }
78
- throw error;
79
- }
80
- for (let entry of entries) {
81
- let entryPath = path.join(currentDir, entry.name);
82
- let relativePath = normalizeRelativePath(path.relative(appRoot, entryPath));
83
- if (entry.isDirectory()) {
84
- routeDirectoryPaths.add(relativePath);
85
- await walk(entryPath, false);
86
- continue;
87
- }
88
- if (!entry.isFile()) {
89
- continue;
90
- }
91
- if (isControllerEntryFileName(entry.name)) {
92
- controllerEntryPaths.add(relativePath);
93
- continue;
94
- }
95
- if (isRouteLocalFileName(entry.name)) {
96
- routeLocalFilePaths.add(relativePath);
97
- }
98
- }
99
- }
100
- await walk(actionsDir, true);
101
- return {
102
- controllerEntryPaths,
103
- routeDirectoryPaths,
104
- routeLocalFilePaths,
105
- };
61
+ async function inspectRouteDirectories(appRoot, plans) {
62
+ return Promise.all(plans.map(async (plan) => ({
63
+ ...plan,
64
+ exists: await pathHasType(appRoot, plan.directoryPath, 'directory'),
65
+ })));
106
66
  }
107
- function applyScanToSubtrees(subtreePlans, scan) {
108
- let claimedRouteLocalPaths = claimFilesToDeepestSubtree([...scan.routeLocalFilePaths], subtreePlans);
109
- let claimedContentPaths = claimFilesToDeepestSubtree(getNestedContentPaths(scan), subtreePlans);
110
- return subtreePlans.map((subtree) => {
111
- let actualEntryPaths = findOwnerPaths(scan, subtree.entryCandidates);
112
- return {
113
- ...subtree,
114
- actualEntryPath: actualEntryPaths[0] ?? null,
115
- actualEntryPaths,
116
- claimedFilePaths: claimedContentPaths.get(subtree.routeName) ?? [],
117
- claimedRouteLocalFilePaths: claimedRouteLocalPaths.get(subtree.routeName) ?? [],
118
- };
119
- });
120
- }
121
- function getNestedContentPaths(scan) {
122
- let nestedControllerPaths = [...scan.controllerEntryPaths].filter((filePath) => isNestedControllerPath(filePath));
123
- return [...new Set([...nestedControllerPaths, ...scan.routeLocalFilePaths])].sort();
67
+ async function inspectOwnedSubtrees(appRoot, plans) {
68
+ return Promise.all(plans.map(async (plan) => ({
69
+ ...plan,
70
+ actualEntryPath: await findControllerEntryPath(appRoot, plan.entryCandidates),
71
+ })));
124
72
  }
125
- function claimFilesToDeepestSubtree(filePaths, subtreePlans) {
126
- let subtreesByDepth = [...subtreePlans].sort((left, right) => {
127
- if (right.subtreePath.length !== left.subtreePath.length) {
128
- return right.subtreePath.length - left.subtreePath.length;
129
- }
130
- return left.routeName.localeCompare(right.routeName);
131
- });
132
- let claims = new Map();
133
- for (let filePath of filePaths.sort()) {
134
- let matchingSubtree = subtreesByDepth.find((subtree) => isWithinDirectory(filePath, subtree.subtreePath));
135
- if (matchingSubtree == null) {
136
- continue;
137
- }
138
- let claimedPaths = claims.get(matchingSubtree.routeName);
139
- if (claimedPaths == null) {
140
- claims.set(matchingSubtree.routeName, [filePath]);
141
- continue;
73
+ async function findControllerEntryPath(appRoot, candidatePaths) {
74
+ for (let candidatePath of candidatePaths) {
75
+ if (await pathHasType(appRoot, candidatePath, 'file')) {
76
+ return candidatePath;
142
77
  }
143
- claimedPaths.push(filePath);
144
78
  }
145
- return claims;
146
- }
147
- function getOrphanControllerPaths(subtreePlans, scan) {
148
- let expectedControllerPaths = new Set(subtreePlans.flatMap((subtree) => subtree.entryCandidates));
149
- return [...scan.controllerEntryPaths]
150
- .filter((filePath) => !expectedControllerPaths.has(filePath))
151
- .sort();
79
+ return null;
152
80
  }
153
- function getOrphanRouteDirectoryPaths(routeDirectories, scan) {
154
- let expectedRouteDirectories = new Set(routeDirectories.map((routeDirectory) => routeDirectory.directoryPath));
155
- let actualControllerDirectories = [...scan.controllerEntryPaths].map((controllerPath) => normalizeRelativePath(path.dirname(controllerPath)));
156
- return [...scan.routeDirectoryPaths]
157
- .filter((directoryPath) => !expectedRouteDirectories.has(directoryPath))
158
- .filter((directoryPath) => !actualControllerDirectories.some((controllerPath) => isDirectoryWithinDirectory(controllerPath, directoryPath)))
159
- .sort();
160
- }
161
- function isRouteLocalFileName(fileName) {
162
- let baseName = getOwnerModuleBaseName(fileName);
163
- return (baseName != null &&
164
- baseName !== 'controller' &&
165
- !baseName.endsWith('.test') &&
166
- !baseName.endsWith('.spec'));
167
- }
168
- function findOwnerPaths(scan, candidatePaths) {
169
- let existingPaths = [];
170
- for (let candidatePath of candidatePaths) {
171
- if (scan.controllerEntryPaths.has(candidatePath)) {
172
- existingPaths.push(candidatePath);
81
+ async function pathHasType(appRoot, relativePath, type) {
82
+ try {
83
+ let stats = await fs.stat(path.join(appRoot, relativePath));
84
+ return type === 'directory' ? stats.isDirectory() : stats.isFile();
85
+ }
86
+ catch (error) {
87
+ let nodeError = error;
88
+ if (nodeError.code === 'ENOENT' || nodeError.code === 'ENOTDIR') {
89
+ return false;
173
90
  }
91
+ throw error;
174
92
  }
175
- return existingPaths;
176
- }
177
- function isNestedControllerPath(filePath) {
178
- return (filePath.startsWith(ACTIONS_PATH_PREFIX) &&
179
- filePath.slice(ACTIONS_PATH_PREFIX.length).includes('/'));
180
- }
181
- function isWithinDirectory(filePath, directoryPath) {
182
- return filePath.startsWith(`${directoryPath}/`);
183
- }
184
- function isDirectoryWithinDirectory(directoryPath, parentDirectoryPath) {
185
- return (directoryPath === parentDirectoryPath || isWithinDirectory(directoryPath, parentDirectoryPath));
186
- }
187
- function normalizeRelativePath(filePath) {
188
- return filePath.split(path.sep).join('/');
189
93
  }
@@ -0,0 +1,20 @@
1
+ import type { RemixDbAdapterConfig } from './remix-config.ts';
2
+ export type DatabaseCommand = 'migrate' | 'reset' | 'seed' | 'status' | 'wipe';
3
+ export interface DatabaseCommandInvocation {
4
+ command: DatabaseCommand;
5
+ connectionEnv?: string;
6
+ journalTable?: string;
7
+ migrations?: string;
8
+ seed?: string;
9
+ to?: string;
10
+ }
11
+ export interface DatabaseCommandPlan {
12
+ adapter: RemixDbAdapterConfig;
13
+ command: DatabaseCommand;
14
+ journalTable?: string;
15
+ migrations?: string;
16
+ seed?: string;
17
+ to?: string;
18
+ }
19
+ export declare function isDatabaseCommand(value: unknown): value is DatabaseCommand;
20
+ //# sourceMappingURL=database-command.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"database-command.d.ts","sourceRoot":"","sources":["../../src/lib/database-command.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAA;AAE7D,MAAM,MAAM,eAAe,GAAG,SAAS,GAAG,OAAO,GAAG,MAAM,GAAG,QAAQ,GAAG,MAAM,CAAA;AAE9E,MAAM,WAAW,yBAAyB;IACxC,OAAO,EAAE,eAAe,CAAA;IACxB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,EAAE,CAAC,EAAE,MAAM,CAAA;CACZ;AAED,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,oBAAoB,CAAA;IAC7B,OAAO,EAAE,eAAe,CAAA;IACxB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,EAAE,CAAC,EAAE,MAAM,CAAA;CACZ;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,eAAe,CAQ1E"}
@@ -0,0 +1,7 @@
1
+ export function isDatabaseCommand(value) {
2
+ return (value === 'migrate' ||
3
+ value === 'reset' ||
4
+ value === 'seed' ||
5
+ value === 'status' ||
6
+ value === 'wipe');
7
+ }
@@ -1,4 +1,4 @@
1
1
  import { type ControllerOwnership } from '../controller-ownership.ts';
2
- import type { DoctorFinding, DoctorFixPlan } from './types.ts';
3
- export declare function getControllerFindings(ownership: ControllerOwnership, fixPlans: DoctorFixPlan[]): DoctorFinding[];
2
+ import type { DoctorFinding } from './types.ts';
3
+ export declare function getControllerFindings(ownership: ControllerOwnership): DoctorFinding[];
4
4
  //# sourceMappingURL=controller-findings.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"controller-findings.d.ts","sourceRoot":"","sources":["../../../src/lib/doctor/controller-findings.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,mBAAmB,EAEzB,MAAM,4BAA4B,CAAA;AACnC,OAAO,KAAK,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAE9D,wBAAgB,qBAAqB,CACnC,SAAS,EAAE,mBAAmB,EAC9B,QAAQ,EAAE,aAAa,EAAE,GACxB,aAAa,EAAE,CAkBjB"}
1
+ {"version":3,"file":"controller-findings.d.ts","sourceRoot":"","sources":["../../../src/lib/doctor/controller-findings.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,mBAAmB,EAGzB,MAAM,4BAA4B,CAAA;AACnC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAE/C,wBAAgB,qBAAqB,CAAC,SAAS,EAAE,mBAAmB,GAAG,aAAa,EAAE,CAWrF"}