import { SpawnOptions as NodeSpawnOptions } from 'child_process'; import { Command } from 'commander'; import DreamApp, { DreamAppInitOptions } from '../dream-app/index.js'; import DreamCliLogger from './logger/DreamCliLogger.js'; export type SpawnOptions = Omit & { onStdout?: (str: string) => void; /** * Argv elements passed to the child as separate arguments. Defaults to `[]`. * Each entry is passed literally — shell meta-characters (`$`, backticks, * `&&`, spaces, etc.) inside any element are not interpreted by a shell. * This is the right shape for paths and other non-secret values. Pass * credentials through the child's environment so they do not appear in * the process command line or a refused-spawn error. */ args?: string[]; }; export declare const CLI_INDENT = " "; export declare const baseColumnsWithTypesDescription = "space separated snake-case properties like this:\n title:citext subtitle:string body_markdown:text style:enum:post_styles:formal,informal\n \n all properties default to not nullable; null can be allowed by appending ':optional':\n subtitle:string:optional\n \n supported types:\n - uuid:\n - uuid[]:\n a column optimized for storing UUIDs\n \n - citext:\n - citext[]:\n case insensitive text (indexes and queries are automatically case insensitive)\n \n - encrypted:\n encrypted text (used in conjunction with the @deco.Encrypted decorator)\n \n - string:\n - string[]:\n varchar; allowed length defaults to 255, but may be customized, e.g.: subtitle:string:128 or subtitle:string:128:optional\n \n - text\n - text[]\n - date\n - date[]\n - datetime\n - datetime[]\n - time\n - time[]\n - timetz\n - timetz[]\n - integer\n - integer[]\n - boolean\n - boolean[]\n \n - decimal:\n - decimal[]:\n precision,scale is required, e.g.: volume:decimal:3,2 or volume:decimal:3,2:optional\n \n leveraging arrays, add the \"[]\" suffix, e.g.: volume:decimal[]:3,2\n \n - enum:\n - enum[]:\n include the enum name to automatically create the enum:\n type:enum:room_types:bathroom,kitchen,bedroom or type:enum:room_types:bathroom,kitchen,bedroom:optional\n \n omit the enum values to leverage an existing enum (omits the enum type creation):\n type:enum:room_types or type:enum:room_types:optional\n \n leveraging arrays, add the \"[]\" suffix, e.g.: type:enum[]:room_types:bathroom,kitchen,bedroom"; /** * The `[columnsWithTypes...]` argument description for `g:model`. Exported so * that spec/unit/cli/generateMigrationContent.spec.ts can run each * `:belongs_to` example in this text through the migration generator and check * the FK column the example's comment names: an example that drifts from what * the generator actually produces then fails the suite instead of shipping. */ export declare const columnsWithTypesDescription: string; export declare const columnsWithTypesDescriptionForStiChild: string; /** * The `[columnsWithTypes...]` argument description for `g:migration`. Exported * for the same `:belongs_to` example check as `columnsWithTypesDescription`. */ export declare const columnsWithTypesDescriptionForMigration: string; /** * The `` argument description for `g:migration`. Exported so * that spec/unit/helpers/cli/generateMigration.spec.ts can read the example * migration names out of this text and run them through the generator: a help * example that drifts from what the generator actually produces then fails the * suite instead of shipping. */ export declare const migrationNameArgumentDescription = "Kebab-case name describing the change. A -to-\n anywhere in the name \u2014 not just at the end \u2014 auto-generates\n an add-columns alterTable scaffold for that table;\n -from- generates a remove-columns scaffold, but\n only when the name contains no -to- at all. The text after\n the marker's first occurrence is snake-cased and pluralized\n into the table name, so add-a-to-b-to-cs targets b_to_cs. A\n hand-written migration must avoid both strings anywhere in\n its name.\n \n Examples:\n add-phone-to-users phone:encrypted # scaffolds alterTable('users', ...)\n remove-status-from-posts status:string # scaffolds alterTable('posts', ...)\n create-many-to-many-posts-users # WRONG: selects 'many_posts_users'\n # -to- inside \"many-to-many\" matches: with no columns\n # this errors; with columns it silently scaffolds it\n create-posts-users-join-table # correct: no marker match\n # with no columns, generates empty up/down methods to\n # write by hand; with columns, scaffolds\n # alterTable('') to hand-edit"; export default class DreamCLI { /** * Starts the Dream console */ static loadRepl(context: Record): Promise; /** * use this method for initializing a standalone dream application. If using Psychic and Dream together, * a different pattern is used, which leverages the `generateDreamCli` method instead. */ static provide(program: Command, { initializeDreamApp, seedDb, }: { initializeDreamApp: (opts?: DreamAppInitOptions) => Promise; seedDb: () => Promise | void; }): void; /** * @internal * * Called by Psychic (and other consumers) to programmatically generate a * Dream model with all associated files. */ static generateDream(opts: { fullyQualifiedModelName: string; columnsWithTypes: string[]; options: { connectionName: string; serializer: boolean; stiBaseSerializer: boolean; includeAdminSerializers: boolean; includeInternalSerializers?: boolean; tableName?: string | undefined; /** * Optional model class name override. If provided, generators use it * instead of deriving the class name from the fully qualified model name. */ modelName?: string | undefined; /** * When true (and the generated model is NOT an STI child), decorates * the model with `@SoftDelete()` and auto-emits a nullable * `deleted_at` column. Defaults to false at this programmatic entry * point — the CLI layer opts users in by default. */ softDelete?: boolean; }; fullyQualifiedParentName?: string | undefined; }): Promise; /** * called under the hood when provisioning both psychic and dream applications. */ static generateDreamCli(program: Command, { initializeDreamApp, seedDb, onSync, }: { initializeDreamApp: (opts?: DreamAppInitOptions) => Promise; seedDb: () => Promise | void; onSync: () => Promise | void; }): void; /** * Run a developer-authored CLI command. Always runs in argv form (the * underlying child_process `spawn` is called with `shell: false`): * `command` is exec'd literally and `opts.args` are passed as separate * argv elements. Shell-form invocation is intentionally not supported — * there is no caller that needs `&&`-chaining, globs, or other shell * features that can't be expressed as argv. * * For backward compatibility, `command` may contain implicit args * separated by whitespace (e.g. `'pnpm psy sync'`); the leading token * becomes the program and the rest are split out and prepended to any * `opts.args` so the original argument order is preserved: * * DreamCLI.spawn('pnpm psy sync') * → spawn('pnpm', ['psy', 'sync']) * * DreamCLI.spawn('pnpm psy', { args: ['sync', '--flag'] }) * → spawn('pnpm', ['psy', 'sync', '--flag']) * * ## Threat model (R-015) * * For dev-time CLI glue only (scaffolding, doc generation, type sync). * **No runtime HTTP request input ever reaches this function.** Inputs * are constant literals or composed from developer-supplied config * (package.json scripts, CLI argv, scaffold templates) — never from * runtime request input or any other untrusted external source. * * Argv-form passes config values and paths literally to the child, * without interpreting shell metacharacters. Credentials belong in * the child's environment, since argv is visible in process listings * and in the refused-spawn error. * * ## Layered defense * * Primary gate: every caller restricts spawn use to dev/test code paths * (CLI commands, the dev watcher, scaffold-time code generators, * generated `cli:sync` initializers wrapped in * `if (AppEnv.isDevelopmentOrTest)`). * * Backstop: throws `SspawnRequiresDevelopmentOrTest` when `NODE_ENV` is * anything other than `development` or `test`. Checking * `!isDevelopmentOrTest` (rather than `isProduction`) means staging-style * envs and any unforeseen NODE_ENV value also fail closed. */ static spawn(command: string, opts?: SpawnOptions): Promise; static get logger(): DreamCliLogger; private static _logger; }