goke 6.9.0 → 6.10.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.
@@ -132,6 +132,35 @@ describe('error formatting', () => {
132
132
  expect(text).toMatch(/at /);
133
133
  });
134
134
  });
135
+ describe('anonymous action naming', () => {
136
+ test('inline anonymous function gets named after the command', () => {
137
+ const cli = gokeTestable('mycli');
138
+ const cmd = cli.command('deploy', 'Deploy app');
139
+ // Inline arrow functions passed directly to .action() have no name,
140
+ // so goke assigns one based on the command name for better stack traces.
141
+ cmd.action(() => { });
142
+ expect(cmd.commandAction.name).toBe('command:deploy');
143
+ });
144
+ test('inline anonymous function on multi-word command gets full name', () => {
145
+ const cli = gokeTestable('mycli');
146
+ const cmd = cli.command('db migrate', 'Run migrations');
147
+ cmd.action(() => { });
148
+ expect(cmd.commandAction.name).toBe('command:db migrate');
149
+ });
150
+ test('named function keeps its original name', () => {
151
+ const cli = gokeTestable('mycli');
152
+ const cmd = cli.command('build', 'Build app');
153
+ function myBuildAction() { }
154
+ cmd.action(myBuildAction);
155
+ expect(cmd.commandAction.name).toBe('myBuildAction');
156
+ });
157
+ test('default command action gets "command:default" name', () => {
158
+ const cli = gokeTestable('mycli');
159
+ const cmd = cli.command('', 'Default command');
160
+ cmd.action(() => { });
161
+ expect(cmd.commandAction.name).toBe('command:default');
162
+ });
163
+ });
135
164
  describe('injected fs', () => {
136
165
  test('command actions can use the default node fs for cli storage', async () => {
137
166
  const stdout = createTestOutputStream();
@@ -1955,3 +1984,38 @@ describe('use() with sub-CLI composition', () => {
1955
1984
  expect(matched).toBe('rollback');
1956
1985
  });
1957
1986
  });
1987
+ describe('getAction()', () => {
1988
+ test('returns the action callable with correct behavior', () => {
1989
+ const stdout = createTestOutputStream();
1990
+ const cli = goke('mycli', { stdout, exit: () => { } });
1991
+ const cmd = cli
1992
+ .command('deploy', 'Deploy the app')
1993
+ .option('--env <env>', z.enum(['staging', 'production']).describe('Target environment'))
1994
+ .action((options, { console }) => {
1995
+ console.log(`Deploying to ${options.env}`);
1996
+ });
1997
+ const action = cmd.getAction();
1998
+ const ctx = cli.createExecutionContext();
1999
+ action({ env: 'staging', '--': [] }, ctx);
2000
+ expect(stdout.text).toBe('Deploying to staging\n');
2001
+ });
2002
+ test('works with positional args', () => {
2003
+ const stdout = createTestOutputStream();
2004
+ const cli = goke('mycli', { stdout, exit: () => { } });
2005
+ const cmd = cli
2006
+ .command('get <id>', 'Fetch by id')
2007
+ .option('--format <format>', z.string().describe('Output format'))
2008
+ .action((id, options, { console }) => {
2009
+ console.log(`${id}:${options.format}`);
2010
+ });
2011
+ const action = cmd.getAction();
2012
+ const ctx = cli.createExecutionContext();
2013
+ action('abc123', { format: 'json', '--': [] }, ctx);
2014
+ expect(stdout.text).toBe('abc123:json\n');
2015
+ });
2016
+ test('throws when no action is registered', () => {
2017
+ const cli = goke('mycli');
2018
+ const cmd = cli.command('noop', 'No action');
2019
+ expect(() => cmd.getAction()).toThrow(/No action registered/);
2020
+ });
2021
+ });
@@ -3,7 +3,7 @@
3
3
  */
4
4
  import { describe, expect, test } from 'vitest';
5
5
  import { z } from 'zod';
6
- import goke, { openInBrowser } from '../index.js';
6
+ import goke, { openInBrowser, generateDocs } from '../index.js';
7
7
  const ANSI_RE = /\x1B\[[0-9;]*m/g;
8
8
  const stripAnsi = (text) => text.replace(ANSI_RE, '');
9
9
  function createTestOutputStream() {
@@ -133,7 +133,7 @@ describe('documented command APIs', () => {
133
133
  expect(help).toContain('Deploy safely first');
134
134
  expect(stdout.text).toBe('');
135
135
  });
136
- test('openInBrowser prints the URL to stdout in non-tty environments', () => {
136
+ test('openInBrowser prints the URL to stdout in non-tty environments', async () => {
137
137
  const url = 'https://example.com/dashboard';
138
138
  const originalStdoutWrite = process.stdout.write;
139
139
  const originalStderrWrite = process.stderr.write;
@@ -153,7 +153,7 @@ describe('documented command APIs', () => {
153
153
  return true;
154
154
  });
155
155
  try {
156
- openInBrowser(url);
156
+ await openInBrowser(url);
157
157
  }
158
158
  finally {
159
159
  process.stdout.write = originalStdoutWrite;
@@ -163,7 +163,143 @@ describe('documented command APIs', () => {
163
163
  value: originalIsTTY,
164
164
  });
165
165
  }
166
- expect(stdout).toBe(`${url}\n`);
167
- expect(stderr).toBe('');
166
+ expect(stdout).toBe('');
167
+ expect(stderr).toBe(`${url}\n`);
168
+ });
169
+ });
170
+ describe('generateDocs', () => {
171
+ test('generates pages for CLI with multiple commands', () => {
172
+ const cli = gokeTestable('sentry')
173
+ .version('1.0.0')
174
+ .help();
175
+ cli
176
+ .command('event view <id>', 'View details of a specific event')
177
+ .option('-w, --web', 'Open in browser')
178
+ .option('--spans <spans>', z.string().default('3').describe('Span tree depth limit'))
179
+ .example('```\nsentry event view abc123\n```');
180
+ cli
181
+ .command('event list <issue>', 'List events for an issue')
182
+ .option('-n, --limit <limit>', z.number().default(25).describe('Number of events'))
183
+ .option('-q, --query <query>', 'Search query');
184
+ cli
185
+ .command('hidden-cmd', 'Should not appear')
186
+ .hidden();
187
+ const pages = generateDocs({ cli });
188
+ expect(pages.map((p) => p.slug)).toMatchInlineSnapshot(`
189
+ [
190
+ "index",
191
+ "event-view",
192
+ "event-list",
193
+ ]
194
+ `);
195
+ // Index page
196
+ expect(pages[0].content).toMatchInlineSnapshot(`
197
+ "# sentry
198
+
199
+ Version: 1.0.0
200
+
201
+ ## Commands
202
+
203
+ | Command | Description |
204
+ |---------|-------------|
205
+ | [\`event view\`](./event-view.md) | View details of a specific event |
206
+ | [\`event list\`](./event-list.md) | List events for an issue |
207
+
208
+ ## Global Options
209
+
210
+ | Option | Default | Description |
211
+ |--------|---------|-------------|
212
+ | \`-v, --version\` | - | Display version number |
213
+ | \`-h, --help\` | - | Display this message |
214
+ "
215
+ `);
216
+ // Command page with examples
217
+ expect(pages[1].content).toMatchInlineSnapshot(`
218
+ "# event view
219
+
220
+ View details of a specific event
221
+
222
+ ## Usage
223
+
224
+ \`\`\`sh
225
+ sentry event view <id>
226
+ \`\`\`
227
+
228
+ ## Arguments
229
+
230
+ | Argument | Required | Description |
231
+ |----------|----------|-------------|
232
+ | \`<id>\` | Yes | id |
233
+
234
+ ## Options
235
+
236
+ | Option | Default | Description |
237
+ |--------|---------|-------------|
238
+ | \`-w, --web\` | - | Open in browser |
239
+ | \`--spans <spans>\` | \`3\` | Span tree depth limit |
240
+
241
+ ## Global Options
242
+
243
+ | Option | Default | Description |
244
+ |--------|---------|-------------|
245
+ | \`-v, --version\` | - | Display version number |
246
+ | \`-h, --help\` | - | Display this message |
247
+
248
+ ## Examples
249
+
250
+ \`\`\`
251
+ sentry event view abc123
252
+ \`\`\`
253
+ "
254
+ `);
255
+ // Command page without examples
256
+ expect(pages[2].content).toMatchInlineSnapshot(`
257
+ "# event list
258
+
259
+ List events for an issue
260
+
261
+ ## Usage
262
+
263
+ \`\`\`sh
264
+ sentry event list <issue>
265
+ \`\`\`
266
+
267
+ ## Arguments
268
+
269
+ | Argument | Required | Description |
270
+ |----------|----------|-------------|
271
+ | \`<issue>\` | Yes | issue |
272
+
273
+ ## Options
274
+
275
+ | Option | Default | Description |
276
+ |--------|---------|-------------|
277
+ | \`-n, --limit <limit>\` | \`25\` | Number of events |
278
+ | \`-q, --query <query>\` | - | Search query |
279
+
280
+ ## Global Options
281
+
282
+ | Option | Default | Description |
283
+ |--------|---------|-------------|
284
+ | \`-v, --version\` | - | Display version number |
285
+ | \`-h, --help\` | - | Display this message |
286
+ "
287
+ `);
288
+ });
289
+ test('handles CLI with no commands', () => {
290
+ const cli = gokeTestable('empty');
291
+ const pages = generateDocs({ cli });
292
+ expect(pages).toEqual([]);
293
+ });
294
+ test('skips deprecated options', () => {
295
+ const cli = gokeTestable('mycli');
296
+ cli
297
+ .command('deploy', 'Deploy the app')
298
+ .option('--force', 'Skip confirmation')
299
+ .option('--old-flag', z.boolean().meta({ deprecated: true }).describe('Use --force instead'));
300
+ const pages = generateDocs({ cli });
301
+ const deployPage = pages.find((p) => p.slug === 'deploy');
302
+ // The deprecated option should not appear
303
+ expect(deployPage.content).not.toContain('old-flag');
168
304
  });
169
305
  });
@@ -434,6 +434,33 @@ describe('type-level: README TypeScript examples', () => {
434
434
  options.subOnly;
435
435
  });
436
436
  });
437
+ test('getAction() returns correctly typed function', () => {
438
+ const cmd = goke('test')
439
+ .command('convert <input> <output>', 'Convert file format')
440
+ .option('--quality <quality>', z.number())
441
+ .option('--format <format>', z.enum(['png', 'jpg']))
442
+ .action((input, output, options, ctx) => {
443
+ void input;
444
+ void output;
445
+ void options;
446
+ void ctx;
447
+ });
448
+ const action = cmd.getAction();
449
+ expectTypeOf(action).parameter(0).toEqualTypeOf(); // input
450
+ expectTypeOf(action).parameter(1).toEqualTypeOf(); // output
451
+ });
452
+ test('getAction() with no positional args has (options, ctx) signature', () => {
453
+ const cmd = goke('test')
454
+ .command('deploy', 'Deploy')
455
+ .option('--env <env>', z.enum(['staging', 'production']))
456
+ .action((options, ctx) => {
457
+ void options;
458
+ void ctx;
459
+ });
460
+ const action = cmd.getAction();
461
+ // First param is options with env
462
+ expectTypeOf(action).parameter(0).toMatchTypeOf();
463
+ });
437
464
  test('README global options and middleware example stays typed end-to-end', () => {
438
465
  // `z.boolean().default(false)` and `z.string().default(...)` are
439
466
  // effectively required at runtime: the default applies when the flag is
@@ -0,0 +1,38 @@
1
+ /**
2
+ * AI coding agent detection for goke CLIs.
3
+ *
4
+ * Detects whether the current process is running inside an AI coding agent
5
+ * (Claude, Cursor, Codex, Gemini, etc.) by checking environment variables.
6
+ * Ported from unjs/std-env with the same detection logic.
7
+ *
8
+ * CLI authors can use this to adjust behavior: skip interactive prompts,
9
+ * prefer structured output, avoid browser opens, etc.
10
+ */
11
+ /**
12
+ * Known AI coding agent names.
13
+ */
14
+ export type AgentName = (string & {}) | 'cursor' | 'claude' | 'devin' | 'replit' | 'gemini' | 'codex' | 'auggie' | 'opencode' | 'kiro' | 'goose' | 'pi';
15
+ /**
16
+ * Information about the detected AI coding agent.
17
+ */
18
+ export type AgentInfo = {
19
+ /** The name of the detected agent, or undefined if no agent was detected. */
20
+ name?: AgentName;
21
+ };
22
+ /**
23
+ * Detect the current AI coding agent from environment variables.
24
+ *
25
+ * Checks `AI_AGENT` env var first (explicit override), then scans for
26
+ * known agent-specific env vars in priority order.
27
+ *
28
+ * Supported agents: `cursor`, `claude`, `devin`, `replit`, `gemini`,
29
+ * `codex`, `auggie`, `opencode`, `kiro`, `goose`, `pi`
30
+ */
31
+ export declare function detectAgent(): AgentInfo;
32
+ /** Detected agent info, evaluated once at import time. */
33
+ export declare const agentInfo: AgentInfo;
34
+ /** Name of the detected agent, or undefined if not running inside one. */
35
+ export declare const agent: AgentName | undefined;
36
+ /** Whether the current process is running inside an AI coding agent. */
37
+ export declare const isAgent: boolean;
38
+ //# sourceMappingURL=agents.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agents.d.ts","sourceRoot":"","sources":["../src/agents.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAKH;;GAEG;AACH,MAAM,MAAM,SAAS,GACjB,CAAC,MAAM,GAAG,EAAE,CAAC,GACb,QAAQ,GACR,QAAQ,GACR,OAAO,GACP,QAAQ,GACR,QAAQ,GACR,OAAO,GACP,QAAQ,GACR,UAAU,GACV,MAAM,GACN,OAAO,GACP,IAAI,CAAA;AAgCR;;GAEG;AACH,MAAM,MAAM,SAAS,GAAG;IACtB,6EAA6E;IAC7E,IAAI,CAAC,EAAE,SAAS,CAAA;CACjB,CAAA;AAED;;;;;;;;GAQG;AACH,wBAAgB,WAAW,IAAI,SAAS,CAavC;AAED,0DAA0D;AAC1D,eAAO,MAAM,SAAS,EAAE,SAAyC,CAAA;AAEjE,0EAA0E;AAC1E,eAAO,MAAM,KAAK,EAAE,SAAS,GAAG,SAA0B,CAAA;AAE1D,wEAAwE;AACxE,eAAO,MAAM,OAAO,EAAE,OAA0B,CAAA"}
package/dist/agents.js ADDED
@@ -0,0 +1,63 @@
1
+ /**
2
+ * AI coding agent detection for goke CLIs.
3
+ *
4
+ * Detects whether the current process is running inside an AI coding agent
5
+ * (Claude, Cursor, Codex, Gemini, etc.) by checking environment variables.
6
+ * Ported from unjs/std-env with the same detection logic.
7
+ *
8
+ * CLI authors can use this to adjust behavior: skip interactive prompts,
9
+ * prefer structured output, avoid browser opens, etc.
10
+ */
11
+ const env = globalThis.process?.env || Object.create(null);
12
+ function envMatcher(envKey, regex) {
13
+ return () => {
14
+ const value = env[envKey];
15
+ return value ? regex.test(value) : false;
16
+ };
17
+ }
18
+ // Detection order matters: specific agents first, IDE-based agents last
19
+ // so that agents running inside those IDEs are detected by their own env vars first.
20
+ const agents = [
21
+ // CLI agents
22
+ ['claude', ['CLAUDECODE', 'CLAUDE_CODE']],
23
+ ['replit', ['REPL_ID']],
24
+ ['gemini', ['GEMINI_CLI']],
25
+ ['codex', ['CODEX_SANDBOX', 'CODEX_THREAD_ID']],
26
+ ['opencode', ['OPENCODE']],
27
+ ['pi', [envMatcher('PATH', /\.pi[\\/]agent/)]],
28
+ ['auggie', ['AUGMENT_AGENT']],
29
+ ['goose', ['GOOSE_PROVIDER']],
30
+ // IDE-based agents (checked last)
31
+ ['devin', [envMatcher('EDITOR', /devin/)]],
32
+ ['cursor', ['CURSOR_AGENT']],
33
+ ['kiro', [envMatcher('TERM_PROGRAM', /kiro/)]],
34
+ ];
35
+ /**
36
+ * Detect the current AI coding agent from environment variables.
37
+ *
38
+ * Checks `AI_AGENT` env var first (explicit override), then scans for
39
+ * known agent-specific env vars in priority order.
40
+ *
41
+ * Supported agents: `cursor`, `claude`, `devin`, `replit`, `gemini`,
42
+ * `codex`, `auggie`, `opencode`, `kiro`, `goose`, `pi`
43
+ */
44
+ export function detectAgent() {
45
+ const aiAgent = env.AI_AGENT;
46
+ if (aiAgent) {
47
+ return { name: aiAgent.toLowerCase() };
48
+ }
49
+ for (const [name, checks] of agents) {
50
+ for (const check of checks) {
51
+ if (typeof check === 'string' ? env[check] : check()) {
52
+ return { name };
53
+ }
54
+ }
55
+ }
56
+ return {};
57
+ }
58
+ /** Detected agent info, evaluated once at import time. */
59
+ export const agentInfo = /* #__PURE__ */ detectAgent();
60
+ /** Name of the detected agent, or undefined if not running inside one. */
61
+ export const agent = agentInfo.name;
62
+ /** Whether the current process is running inside an AI coding agent. */
63
+ export const isAgent = !!agentInfo.name;
@@ -0,0 +1,88 @@
1
+ /**
2
+ * Shell completion support for goke CLIs.
3
+ *
4
+ * Two pieces work together:
5
+ * 1. A hidden `--get-goke-completions` flag in the CLI binary. When present,
6
+ * the CLI skips normal execution and prints matching completions to stdout.
7
+ * 2. A shell-specific shim script installed into an fpath/completion directory.
8
+ * On each Tab press the shell calls the CLI binary with the flag + current words.
9
+ *
10
+ * The `installCompletions` function finds a writable completion directory and
11
+ * writes the shim. Since the shim calls the binary on every Tab, completions
12
+ * are always up-to-date with the installed CLI.
13
+ */
14
+ /** The hidden flag the shell script passes to the CLI on each Tab press */
15
+ export declare const COMPLETION_FLAG = "get-goke-completions";
16
+ /**
17
+ * Zsh completion script template.
18
+ *
19
+ * The `#compdef` header lets zsh autoload this as an fpath function.
20
+ * On Tab press it calls the CLI binary with `--get-goke-completions` and
21
+ * all typed words. The binary returns `name:description` pairs (one per line)
22
+ * and zsh renders them with `_describe`.
23
+ */
24
+ export declare const zshTemplate = "#compdef {{app_name}}\n###-begin-{{app_name}}-completions-###\n_{{app_name_safe}}_completions() {\n local reply\n local si=$IFS\n IFS=$'\\n' reply=($(COMP_CWORD=\"$((CURRENT-1))\" COMP_LINE=\"$BUFFER\" COMP_POINT=\"$CURSOR\" GOKE_COMPLETION_SHELL=zsh {{app_path}} --get-goke-completions \"${words[@]}\"))\n IFS=$si\n if [[ ${#reply} -gt 0 ]]; then\n _describe 'values' reply\n else\n _default\n fi\n}\nif [[ \"'${zsh_eval_context[-1]}\" == \"loadautofunc\" ]]; then\n _{{app_name_safe}}_completions \"$@\"\nelse\n compdef _{{app_name_safe}}_completions {{app_name}}\nfi\n###-end-{{app_name}}-completions-###\n";
25
+ /**
26
+ * Bash completion script template.
27
+ *
28
+ * On Tab press bash calls the function which invokes the CLI binary with
29
+ * `--get-goke-completions` and all typed words. The binary returns plain
30
+ * completion strings (one per line).
31
+ */
32
+ export declare const bashTemplate = "###-begin-{{app_name}}-completions-###\n_{{app_name_safe}}_completions()\n{\n local cur_word args type_list\n\n cur_word=\"${COMP_WORDS[COMP_CWORD]}\"\n args=(\"${COMP_WORDS[@]}\")\n\n # Bash 3 compatible (no mapfile). Works on macOS default bash.\n local IFS=$'\\n'\n type_list=($(GOKE_COMPLETION_SHELL=bash {{app_path}} --get-goke-completions \"${args[@]}\"))\n unset IFS\n COMPREPLY=($(compgen -W \"$( printf '%q ' \"${type_list[@]}\" )\" -- \"${cur_word}\" |\n awk '/ / { print \"\\\"\"$0\"\\\"\" } /^[^ ]+$/ { print $0 }'))\n\n if [ ${#COMPREPLY[@]} -eq 0 ]; then\n COMPREPLY=()\n fi\n\n return 0\n}\ncomplete -o bashdefault -o default -F _{{app_name_safe}}_completions {{app_name}}\n###-end-{{app_name}}-completions-###\n";
33
+ export type ShellType = 'zsh' | 'bash';
34
+ /**
35
+ * Detect the current shell from environment variables.
36
+ * Returns 'zsh', 'bash', or null if unrecognized.
37
+ */
38
+ export declare function detectShell(): ShellType | null;
39
+ /**
40
+ * Validate and normalize a shell value from user input.
41
+ * Returns a valid ShellType or throws if the value is invalid.
42
+ */
43
+ export declare function validateShell(value: unknown): ShellType | undefined;
44
+ /**
45
+ * Detect which shell format to use for completion output.
46
+ * Prefers the explicit GOKE_COMPLETION_SHELL env var (set by the shell shim)
47
+ * over the login $SHELL. This prevents format mismatch when a bash shim runs
48
+ * on a system where $SHELL is zsh.
49
+ */
50
+ export declare function detectCompletionShell(): ShellType | null;
51
+ /**
52
+ * Generate a completion script for the given shell.
53
+ *
54
+ * @param shell - Target shell ('zsh' or 'bash')
55
+ * @param cliName - The CLI binary name (e.g. 'my-cli')
56
+ * @param cliPath - Full path to the CLI binary. If not provided, uses cliName.
57
+ */
58
+ export declare function generateCompletionScript(shell: ShellType, cliName: string, cliPath?: string): string;
59
+ interface InstallResult {
60
+ /** The file path where the completion script was written */
61
+ path: string;
62
+ /** The shell the script was generated for */
63
+ shell: ShellType;
64
+ }
65
+ /**
66
+ * Find a writable completion directory, generate the shell script, and write it.
67
+ *
68
+ * For zsh, scans `$fpath` directories plus well-known fallbacks.
69
+ * For bash, scans XDG and legacy user completion dirs.
70
+ *
71
+ * Throws if no writable directory is found.
72
+ *
73
+ * @param cliName - The CLI binary name
74
+ * @param cliPath - Full path to the CLI binary
75
+ * @param shell - Target shell. Auto-detected from $SHELL if omitted.
76
+ */
77
+ export declare function installCompletions(cliName: string, cliPath: string, shell?: ShellType): Promise<InstallResult>;
78
+ /**
79
+ * Remove an installed completion script.
80
+ *
81
+ * Searches the same candidate directories as `installCompletions` and removes
82
+ * any matching completion files found.
83
+ *
84
+ * @returns The paths that were removed, or empty array if none found.
85
+ */
86
+ export declare function uninstallCompletions(cliName: string, shell?: ShellType): Promise<string[]>;
87
+ export {};
88
+ //# sourceMappingURL=completions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"completions.d.ts","sourceRoot":"","sources":["../src/completions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAMH,2EAA2E;AAC3E,eAAO,MAAM,eAAe,yBAAyB,CAAA;AAIrD;;;;;;;GAOG;AACH,eAAO,MAAM,WAAW,qnBAmBvB,CAAA;AAED;;;;;;GAMG;AACH,eAAO,MAAM,YAAY,ywBAuBxB,CAAA;AAID,MAAM,MAAM,SAAS,GAAG,KAAK,GAAG,MAAM,CAAA;AAEtC;;;GAGG;AACH,wBAAgB,WAAW,IAAI,SAAS,GAAG,IAAI,CAK9C;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,SAAS,GAAG,SAAS,CAInE;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,IAAI,SAAS,GAAG,IAAI,CAIxD;AAUD;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CACtC,KAAK,EAAE,SAAS,EAChB,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE,MAAM,GACf,MAAM,CASR;AA+BD,UAAU,aAAa;IACrB,4DAA4D;IAC5D,IAAI,EAAE,MAAM,CAAA;IACZ,6CAA6C;IAC7C,KAAK,EAAE,SAAS,CAAA;CACjB;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,EACf,KAAK,CAAC,EAAE,SAAS,GAChB,OAAO,CAAC,aAAa,CAAC,CA4FxB;AAED;;;;;;;GAOG;AACH,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,MAAM,EACf,KAAK,CAAC,EAAE,SAAS,GAChB,OAAO,CAAC,MAAM,EAAE,CAAC,CAsDnB"}