@happier-dev/stack 0.1.0-preview.5.1
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 +501 -0
- package/bin/hstack.mjs +348 -0
- package/docs/codex-mcp-resume.md +129 -0
- package/docs/edison.md +74 -0
- package/docs/forking-and-branding.md +189 -0
- package/docs/happy-development.md +22 -0
- package/docs/isolated-linux-vm.md +243 -0
- package/docs/menubar.md +244 -0
- package/docs/mobile-ios.md +322 -0
- package/docs/monorepo-migration.md +20 -0
- package/docs/paths-and-env.md +154 -0
- package/docs/remote-access.md +43 -0
- package/docs/server-flavors.md +147 -0
- package/docs/stacks.md +330 -0
- package/docs/tauri.md +60 -0
- package/docs/worktrees-and-forks.md +133 -0
- package/extras/swiftbar/auth-login.sh +29 -0
- package/extras/swiftbar/git-cache-refresh.sh +122 -0
- package/extras/swiftbar/hstack-term.sh +133 -0
- package/extras/swiftbar/hstack.5s.sh +296 -0
- package/extras/swiftbar/hstack.sh +35 -0
- package/extras/swiftbar/icons/happy-green.png +0 -0
- package/extras/swiftbar/icons/happy-orange.png +0 -0
- package/extras/swiftbar/icons/happy-red.png +0 -0
- package/extras/swiftbar/icons/logo-white.png +0 -0
- package/extras/swiftbar/install.sh +265 -0
- package/extras/swiftbar/lib/git.sh +629 -0
- package/extras/swiftbar/lib/icons.sh +92 -0
- package/extras/swiftbar/lib/render.sh +999 -0
- package/extras/swiftbar/lib/system.sh +244 -0
- package/extras/swiftbar/lib/utils.sh +717 -0
- package/extras/swiftbar/set-interval.sh +65 -0
- package/extras/swiftbar/set-server-flavor.sh +61 -0
- package/extras/swiftbar/wt-pr.sh +140 -0
- package/node_modules/@happier-dev/cli-common/README.md +6 -0
- package/node_modules/@happier-dev/cli-common/dist/index.d.ts +4 -0
- package/node_modules/@happier-dev/cli-common/dist/index.d.ts.map +1 -0
- package/node_modules/@happier-dev/cli-common/dist/index.js +4 -0
- package/node_modules/@happier-dev/cli-common/dist/index.js.map +1 -0
- package/node_modules/@happier-dev/cli-common/dist/links/index.d.ts +18 -0
- package/node_modules/@happier-dev/cli-common/dist/links/index.d.ts.map +1 -0
- package/node_modules/@happier-dev/cli-common/dist/links/index.js +25 -0
- package/node_modules/@happier-dev/cli-common/dist/links/index.js.map +1 -0
- package/node_modules/@happier-dev/cli-common/dist/links.d.ts +2 -0
- package/node_modules/@happier-dev/cli-common/dist/links.d.ts.map +1 -0
- package/node_modules/@happier-dev/cli-common/dist/links.js +2 -0
- package/node_modules/@happier-dev/cli-common/dist/links.js.map +1 -0
- package/node_modules/@happier-dev/cli-common/dist/update/index.d.ts +67 -0
- package/node_modules/@happier-dev/cli-common/dist/update/index.d.ts.map +1 -0
- package/node_modules/@happier-dev/cli-common/dist/update/index.js +259 -0
- package/node_modules/@happier-dev/cli-common/dist/update/index.js.map +1 -0
- package/node_modules/@happier-dev/cli-common/dist/workspaces/index.d.ts +17 -0
- package/node_modules/@happier-dev/cli-common/dist/workspaces/index.d.ts.map +1 -0
- package/node_modules/@happier-dev/cli-common/dist/workspaces/index.js +80 -0
- package/node_modules/@happier-dev/cli-common/dist/workspaces/index.js.map +1 -0
- package/node_modules/@happier-dev/cli-common/package.json +26 -0
- package/package.json +77 -0
- package/scripts/auth.mjs +1829 -0
- package/scripts/auth_copy_from_pglite_lock_in_use.integration.test.mjs +90 -0
- package/scripts/auth_copy_from_runCapture.integration.test.mjs +447 -0
- package/scripts/auth_help_cmd.test.mjs +28 -0
- package/scripts/auth_login_flow_in_tty.test.mjs +100 -0
- package/scripts/auth_login_force_default.test.mjs +66 -0
- package/scripts/auth_login_guided_server_no_expo.test.mjs +126 -0
- package/scripts/auth_login_method_override.test.mjs +67 -0
- package/scripts/auth_login_print_includes_configure_links.test.mjs +99 -0
- package/scripts/auth_status_server_validation.integration.test.mjs +140 -0
- package/scripts/build.mjs +266 -0
- package/scripts/bundleWorkspaceDeps.mjs +38 -0
- package/scripts/bundleWorkspaceDeps.test.mjs +77 -0
- package/scripts/ci.mjs +135 -0
- package/scripts/ci.test.mjs +50 -0
- package/scripts/cli-link.mjs +57 -0
- package/scripts/completion.mjs +395 -0
- package/scripts/contrib.mjs +333 -0
- package/scripts/daemon.mjs +1160 -0
- package/scripts/daemon.status_scope.test.mjs +51 -0
- package/scripts/daemon_cmd.mjs +26 -0
- package/scripts/daemon_dist_guard.test.mjs +171 -0
- package/scripts/daemon_invalid_auth_reseed_stack_name.integration.test.mjs +608 -0
- package/scripts/daemon_server_scoped_state.test.mjs +49 -0
- package/scripts/daemon_start_verification.integration.test.mjs +296 -0
- package/scripts/dev.mjs +545 -0
- package/scripts/doctor.mjs +340 -0
- package/scripts/doctor_cmd.test.mjs +22 -0
- package/scripts/doctor_ui_index_missing.test.mjs +37 -0
- package/scripts/eas.mjs +367 -0
- package/scripts/eas_platform_parsing.test.mjs +63 -0
- package/scripts/edison.mjs +1848 -0
- package/scripts/env.mjs +149 -0
- package/scripts/env_cmd.test.mjs +118 -0
- package/scripts/exit_cleanup_kills_detached_children_on_crash.integration.test.mjs +80 -0
- package/scripts/happier.mjs +82 -0
- package/scripts/import.mjs +1327 -0
- package/scripts/init.mjs +464 -0
- package/scripts/install.mjs +550 -0
- package/scripts/lint.mjs +177 -0
- package/scripts/menubar.mjs +202 -0
- package/scripts/migrate.mjs +318 -0
- package/scripts/mobile.mjs +353 -0
- package/scripts/mobile_dev_client.mjs +87 -0
- package/scripts/monorepo.mjs +2234 -0
- package/scripts/monorepo_port.apply.integration.test.mjs +680 -0
- package/scripts/monorepo_port.conflicts.integration.test.mjs +454 -0
- package/scripts/monorepo_port.validation.integration.test.mjs +486 -0
- package/scripts/orchestrated_stack_auth_flow.test.mjs +134 -0
- package/scripts/orchestrated_stack_auth_flow_resolve_port.test.mjs +98 -0
- package/scripts/orchestrated_stack_auth_flow_webapp_url.test.mjs +119 -0
- package/scripts/pack.mjs +257 -0
- package/scripts/pack.test.mjs +68 -0
- package/scripts/pglite_lock.integration.test.mjs +152 -0
- package/scripts/provision/linux-ubuntu-e2e.sh +132 -0
- package/scripts/provision/linux-ubuntu-review-pr.sh +66 -0
- package/scripts/provision/macos-lima-happy-vm.sh +192 -0
- package/scripts/provision/macos-lima-hstack-e2e.sh +100 -0
- package/scripts/release.mjs +53 -0
- package/scripts/release_binary_smoke.integration.test.mjs +159 -0
- package/scripts/review.mjs +1752 -0
- package/scripts/review_pr.mjs +435 -0
- package/scripts/run.mjs +561 -0
- package/scripts/run_script_with_stack_env.restart_port_reuse.test.mjs +30 -0
- package/scripts/self.mjs +465 -0
- package/scripts/self_host.mjs +9 -0
- package/scripts/self_host_binary_smoke.integration.test.mjs +94 -0
- package/scripts/self_host_runtime.mjs +883 -0
- package/scripts/self_host_runtime.test.mjs +82 -0
- package/scripts/self_host_systemd.real.integration.test.mjs +367 -0
- package/scripts/server_flavor.mjs +148 -0
- package/scripts/service.mjs +868 -0
- package/scripts/service_mode_help.test.mjs +27 -0
- package/scripts/setup.mjs +1324 -0
- package/scripts/setup_non_interactive_flag.test.mjs +60 -0
- package/scripts/setup_pr.mjs +605 -0
- package/scripts/setup_pr_orchestrated_auth_flow_util_import.test.mjs +117 -0
- package/scripts/stack/command_arguments.mjs +91 -0
- package/scripts/stack/copy_auth_from_stack.mjs +111 -0
- package/scripts/stack/delegated_script_commands.mjs +92 -0
- package/scripts/stack/help_text.mjs +110 -0
- package/scripts/stack/port_reservation.mjs +74 -0
- package/scripts/stack/repo_checkout_resolution.mjs +31 -0
- package/scripts/stack/run_script_with_stack_env.mjs +634 -0
- package/scripts/stack/stack_daemon_command.mjs +219 -0
- package/scripts/stack/stack_delegated_help.mjs +81 -0
- package/scripts/stack/stack_environment.mjs +151 -0
- package/scripts/stack/stack_environment.sanitization.test.mjs +75 -0
- package/scripts/stack/stack_happier_passthrough_command.mjs +63 -0
- package/scripts/stack/stack_info_snapshot.mjs +167 -0
- package/scripts/stack/stack_mobile_install_command.mjs +61 -0
- package/scripts/stack/stack_resume_command.mjs +76 -0
- package/scripts/stack/stack_stop_command.mjs +34 -0
- package/scripts/stack/stack_workspace_command.mjs +83 -0
- package/scripts/stack/transient_repo_overrides.mjs +29 -0
- package/scripts/stack.mjs +2388 -0
- package/scripts/stack_archive_cmd.integration.test.mjs +31 -0
- package/scripts/stack_audit_fix_light_env.test.mjs +129 -0
- package/scripts/stack_background_pinned_stack_json.test.mjs +81 -0
- package/scripts/stack_copy_auth_server_scoped.test.mjs +243 -0
- package/scripts/stack_daemon_cmd.integration.test.mjs +484 -0
- package/scripts/stack_eas_help.test.mjs +72 -0
- package/scripts/stack_editor_workspace_monorepo_root.test.mjs +102 -0
- package/scripts/stack_env_cmd.test.mjs +107 -0
- package/scripts/stack_guided_login_bundle_error_parse.test.mjs +20 -0
- package/scripts/stack_guided_login_inner_invocation.test.mjs +46 -0
- package/scripts/stack_happy_cmd.integration.test.mjs +263 -0
- package/scripts/stack_info_snapshot_running_status.test.mjs +186 -0
- package/scripts/stack_interactive_monorepo_group.test.mjs +128 -0
- package/scripts/stack_monorepo_defaults.test.mjs +31 -0
- package/scripts/stack_monorepo_repo_dev_token.test.mjs +32 -0
- package/scripts/stack_monorepo_server_light_from_happy_spec.test.mjs +37 -0
- package/scripts/stack_new_name_normalize_cmd.test.mjs +38 -0
- package/scripts/stack_pr_name_normalize_cmd.test.mjs +84 -0
- package/scripts/stack_resume_cmd.integration.test.mjs +134 -0
- package/scripts/stack_server_flavors_defaults.test.mjs +64 -0
- package/scripts/stack_shorthand_cmd.integration.test.mjs +74 -0
- package/scripts/stack_stop_sweeps_legacy_infra_without_kind.integration.test.mjs +44 -0
- package/scripts/stack_stop_sweeps_when_runtime_missing.integration.test.mjs +42 -0
- package/scripts/stack_stop_sweeps_when_runtime_stale.integration.test.mjs +50 -0
- package/scripts/stack_wt_list.test.mjs +117 -0
- package/scripts/start_ui_required_default.test.mjs +63 -0
- package/scripts/stop.mjs +190 -0
- package/scripts/stopStackWithEnv_no_autosweep_when_runtime_missing.integration.test.mjs +95 -0
- package/scripts/swiftbar_git_monorepo_cmd.test.mjs +75 -0
- package/scripts/swiftbar_render_monorepo_wt_actions.integration.test.mjs +116 -0
- package/scripts/swiftbar_utils_cmd.test.mjs +92 -0
- package/scripts/swiftbar_wt_pr_backcompat.test.mjs +162 -0
- package/scripts/systemd_unit_info.test.mjs +24 -0
- package/scripts/tailscale.mjs +490 -0
- package/scripts/test_ci.mjs +36 -0
- package/scripts/test_cmd.mjs +274 -0
- package/scripts/test_cmd.test.mjs +133 -0
- package/scripts/test_integration.mjs +33 -0
- package/scripts/testkit/auth_testkit.mjs +121 -0
- package/scripts/testkit/doctor_testkit.mjs +68 -0
- package/scripts/testkit/monorepo_port_testkit.mjs +157 -0
- package/scripts/testkit/stack_archive_command_testkit.mjs +55 -0
- package/scripts/testkit/stack_new_monorepo_testkit.mjs +83 -0
- package/scripts/testkit/stack_script_command_testkit.mjs +27 -0
- package/scripts/testkit/stack_stop_sweeps_testkit.mjs +172 -0
- package/scripts/testkit/worktrees_monorepo_testkit.mjs +53 -0
- package/scripts/tools.mjs +70 -0
- package/scripts/tui.mjs +914 -0
- package/scripts/tui_stopStackForTuiExit_no_autosweep.integration.test.mjs +95 -0
- package/scripts/typecheck.mjs +178 -0
- package/scripts/ui_gateway.mjs +247 -0
- package/scripts/uninstall.mjs +179 -0
- package/scripts/utils/auth/credentials_paths.mjs +181 -0
- package/scripts/utils/auth/credentials_paths.test.mjs +187 -0
- package/scripts/utils/auth/daemon_gate.mjs +66 -0
- package/scripts/utils/auth/daemon_gate.test.mjs +116 -0
- package/scripts/utils/auth/decode_jwt_payload_unsafe.mjs +16 -0
- package/scripts/utils/auth/dev_key.mjs +163 -0
- package/scripts/utils/auth/files.mjs +56 -0
- package/scripts/utils/auth/guided_pr_auth.mjs +86 -0
- package/scripts/utils/auth/guided_stack_web_login.mjs +56 -0
- package/scripts/utils/auth/handy_master_secret.mjs +42 -0
- package/scripts/utils/auth/interactive_stack_auth.mjs +70 -0
- package/scripts/utils/auth/login_ux.mjs +105 -0
- package/scripts/utils/auth/orchestrated_stack_auth_flow.mjs +291 -0
- package/scripts/utils/auth/sources.mjs +28 -0
- package/scripts/utils/auth/stable_scope_id.mjs +91 -0
- package/scripts/utils/auth/stable_scope_id.test.mjs +51 -0
- package/scripts/utils/auth/stack_guided_login.mjs +438 -0
- package/scripts/utils/cli/arg_values.mjs +23 -0
- package/scripts/utils/cli/arg_values.test.mjs +43 -0
- package/scripts/utils/cli/args.mjs +17 -0
- package/scripts/utils/cli/cli.mjs +24 -0
- package/scripts/utils/cli/cli_registry.mjs +440 -0
- package/scripts/utils/cli/cwd_scope.mjs +158 -0
- package/scripts/utils/cli/cwd_scope.test.mjs +154 -0
- package/scripts/utils/cli/flags.mjs +17 -0
- package/scripts/utils/cli/log_forwarder.mjs +157 -0
- package/scripts/utils/cli/normalize.mjs +16 -0
- package/scripts/utils/cli/prereqs.mjs +103 -0
- package/scripts/utils/cli/prereqs.test.mjs +33 -0
- package/scripts/utils/cli/progress.mjs +141 -0
- package/scripts/utils/cli/smoke_help.mjs +44 -0
- package/scripts/utils/cli/verbosity.mjs +11 -0
- package/scripts/utils/cli/wizard.mjs +139 -0
- package/scripts/utils/cli/wizard_promptSelect.test.mjs +44 -0
- package/scripts/utils/cli/wizard_prompt_worktree_source_lazy.test.mjs +132 -0
- package/scripts/utils/cli/wizard_worktree_slug.test.mjs +33 -0
- package/scripts/utils/crypto/tokens.mjs +14 -0
- package/scripts/utils/dev/daemon.mjs +232 -0
- package/scripts/utils/dev/daemon_watch_resilience.test.mjs +224 -0
- package/scripts/utils/dev/expo_dev.buildEnv.test.mjs +35 -0
- package/scripts/utils/dev/expo_dev.mjs +478 -0
- package/scripts/utils/dev/expo_dev.test.mjs +89 -0
- package/scripts/utils/dev/expo_dev_restart_port_reservation.test.mjs +120 -0
- package/scripts/utils/dev/expo_dev_verbose_logs.test.mjs +60 -0
- package/scripts/utils/dev/server.mjs +180 -0
- package/scripts/utils/dev_auth_key.mjs +7 -0
- package/scripts/utils/edison/git_roots.mjs +30 -0
- package/scripts/utils/edison/git_roots.test.mjs +49 -0
- package/scripts/utils/env/config.mjs +52 -0
- package/scripts/utils/env/dotenv.mjs +32 -0
- package/scripts/utils/env/dotenv.test.mjs +32 -0
- package/scripts/utils/env/env.mjs +130 -0
- package/scripts/utils/env/env_file.mjs +98 -0
- package/scripts/utils/env/env_file.test.mjs +49 -0
- package/scripts/utils/env/env_local.mjs +25 -0
- package/scripts/utils/env/load_env_file.mjs +34 -0
- package/scripts/utils/env/read.mjs +30 -0
- package/scripts/utils/env/sandbox.mjs +13 -0
- package/scripts/utils/env/scrub_env.mjs +69 -0
- package/scripts/utils/env/scrub_env.test.mjs +102 -0
- package/scripts/utils/env/values.mjs +13 -0
- package/scripts/utils/expo/command.mjs +65 -0
- package/scripts/utils/expo/expo.mjs +139 -0
- package/scripts/utils/expo/expo_state_running.test.mjs +48 -0
- package/scripts/utils/expo/metro_ports.mjs +101 -0
- package/scripts/utils/expo/metro_ports.test.mjs +35 -0
- package/scripts/utils/fs/atomic_dir_swap.mjs +55 -0
- package/scripts/utils/fs/atomic_dir_swap.test.mjs +54 -0
- package/scripts/utils/fs/file_has_content.mjs +10 -0
- package/scripts/utils/fs/fs.mjs +11 -0
- package/scripts/utils/fs/json.mjs +25 -0
- package/scripts/utils/fs/ops.mjs +29 -0
- package/scripts/utils/fs/package_json.mjs +8 -0
- package/scripts/utils/fs/tail.mjs +12 -0
- package/scripts/utils/git/dev_checkout.mjs +127 -0
- package/scripts/utils/git/dev_checkout.test.mjs +115 -0
- package/scripts/utils/git/git.mjs +67 -0
- package/scripts/utils/git/parse_name_status_z.mjs +21 -0
- package/scripts/utils/git/refs.mjs +26 -0
- package/scripts/utils/git/worktrees.mjs +323 -0
- package/scripts/utils/git/worktrees_monorepo.test.mjs +60 -0
- package/scripts/utils/git/worktrees_pathstyle.test.mjs +53 -0
- package/scripts/utils/llm/assist.mjs +260 -0
- package/scripts/utils/llm/codex_exec.mjs +61 -0
- package/scripts/utils/llm/codex_exec.test.mjs +46 -0
- package/scripts/utils/llm/hstack_runner.mjs +59 -0
- package/scripts/utils/llm/tools.mjs +56 -0
- package/scripts/utils/llm/tools.test.mjs +67 -0
- package/scripts/utils/menubar/swiftbar.mjs +121 -0
- package/scripts/utils/menubar/swiftbar.test.mjs +85 -0
- package/scripts/utils/mobile/config.mjs +35 -0
- package/scripts/utils/mobile/dev_client_links.mjs +59 -0
- package/scripts/utils/mobile/identifiers.mjs +46 -0
- package/scripts/utils/mobile/identifiers.test.mjs +41 -0
- package/scripts/utils/mobile/ios_xcodeproj_patch.mjs +128 -0
- package/scripts/utils/mobile/ios_xcodeproj_patch.test.mjs +131 -0
- package/scripts/utils/net/bind_mode.mjs +39 -0
- package/scripts/utils/net/dns.mjs +10 -0
- package/scripts/utils/net/lan_ip.mjs +24 -0
- package/scripts/utils/net/ports.mjs +110 -0
- package/scripts/utils/net/tcp_forward.mjs +162 -0
- package/scripts/utils/net/url.mjs +30 -0
- package/scripts/utils/net/url.test.mjs +29 -0
- package/scripts/utils/paths/canonical_home.mjs +15 -0
- package/scripts/utils/paths/canonical_home.test.mjs +28 -0
- package/scripts/utils/paths/localhost_host.mjs +112 -0
- package/scripts/utils/paths/localhost_host.test.mjs +58 -0
- package/scripts/utils/paths/paths.mjs +302 -0
- package/scripts/utils/paths/paths_env_win32.test.mjs +36 -0
- package/scripts/utils/paths/paths_monorepo.test.mjs +58 -0
- package/scripts/utils/paths/paths_server_flavors.test.mjs +50 -0
- package/scripts/utils/paths/runtime.mjs +41 -0
- package/scripts/utils/pglite_lock.mjs +107 -0
- package/scripts/utils/proc/commands.mjs +33 -0
- package/scripts/utils/proc/exit_cleanup.mjs +57 -0
- package/scripts/utils/proc/happy_monorepo_deps.mjs +37 -0
- package/scripts/utils/proc/happy_monorepo_deps.test.mjs +89 -0
- package/scripts/utils/proc/ownership.mjs +217 -0
- package/scripts/utils/proc/ownership_killProcessGroupOwnedByStack.test.mjs +216 -0
- package/scripts/utils/proc/ownership_listPidsWithEnvNeedles.test.mjs +88 -0
- package/scripts/utils/proc/package_scripts.mjs +38 -0
- package/scripts/utils/proc/package_scripts.test.mjs +58 -0
- package/scripts/utils/proc/parallel.mjs +25 -0
- package/scripts/utils/proc/pids.mjs +11 -0
- package/scripts/utils/proc/pm.mjs +478 -0
- package/scripts/utils/proc/pm_spawn.integration.test.mjs +131 -0
- package/scripts/utils/proc/pm_stack_cache_env.test.mjs +313 -0
- package/scripts/utils/proc/proc.mjs +331 -0
- package/scripts/utils/proc/proc.test.mjs +85 -0
- package/scripts/utils/proc/terminate.mjs +69 -0
- package/scripts/utils/proc/terminate.test.mjs +54 -0
- package/scripts/utils/proc/watch.mjs +63 -0
- package/scripts/utils/review/augment_runner_integration.test.mjs +105 -0
- package/scripts/utils/review/base_ref.mjs +82 -0
- package/scripts/utils/review/base_ref.test.mjs +89 -0
- package/scripts/utils/review/chunks.mjs +55 -0
- package/scripts/utils/review/chunks.test.mjs +107 -0
- package/scripts/utils/review/detached_worktree.mjs +61 -0
- package/scripts/utils/review/detached_worktree.test.mjs +61 -0
- package/scripts/utils/review/findings.mjs +278 -0
- package/scripts/utils/review/findings.test.mjs +203 -0
- package/scripts/utils/review/head_slice.mjs +132 -0
- package/scripts/utils/review/head_slice.test.mjs +117 -0
- package/scripts/utils/review/instructions/deep.md +20 -0
- package/scripts/utils/review/prompts.mjs +279 -0
- package/scripts/utils/review/prompts.test.mjs +77 -0
- package/scripts/utils/review/run_reviewers_safe.mjs +12 -0
- package/scripts/utils/review/run_reviewers_safe.test.mjs +45 -0
- package/scripts/utils/review/runners/augment.mjs +91 -0
- package/scripts/utils/review/runners/augment.test.mjs +64 -0
- package/scripts/utils/review/runners/claude.mjs +92 -0
- package/scripts/utils/review/runners/claude.test.mjs +47 -0
- package/scripts/utils/review/runners/coderabbit.mjs +105 -0
- package/scripts/utils/review/runners/coderabbit.test.mjs +32 -0
- package/scripts/utils/review/runners/codex.mjs +129 -0
- package/scripts/utils/review/runners/codex.test.mjs +115 -0
- package/scripts/utils/review/slice_mode.mjs +20 -0
- package/scripts/utils/review/slice_mode.test.mjs +69 -0
- package/scripts/utils/review/sliced_runner.mjs +39 -0
- package/scripts/utils/review/sliced_runner.test.mjs +57 -0
- package/scripts/utils/review/slices.mjs +140 -0
- package/scripts/utils/review/slices.test.mjs +41 -0
- package/scripts/utils/review/targets.mjs +23 -0
- package/scripts/utils/review/targets.test.mjs +31 -0
- package/scripts/utils/review/tool_home_seed.mjs +106 -0
- package/scripts/utils/review/tool_home_seed.test.mjs +124 -0
- package/scripts/utils/review/uncommitted_ops.mjs +77 -0
- package/scripts/utils/review/uncommitted_ops.test.mjs +117 -0
- package/scripts/utils/sandbox/review_pr_sandbox.mjs +105 -0
- package/scripts/utils/server/apply_server_light_env_defaults.mjs +14 -0
- package/scripts/utils/server/flavor_scripts.mjs +138 -0
- package/scripts/utils/server/flavor_scripts.test.mjs +115 -0
- package/scripts/utils/server/infra/happy_server_infra.mjs +444 -0
- package/scripts/utils/server/mobile_api_url.mjs +60 -0
- package/scripts/utils/server/mobile_api_url.test.mjs +58 -0
- package/scripts/utils/server/port.mjs +55 -0
- package/scripts/utils/server/prisma_import.mjs +36 -0
- package/scripts/utils/server/prisma_import.test.mjs +78 -0
- package/scripts/utils/server/server.mjs +109 -0
- package/scripts/utils/server/ui_build_check.mjs +37 -0
- package/scripts/utils/server/ui_build_check.test.mjs +70 -0
- package/scripts/utils/server/ui_env.mjs +13 -0
- package/scripts/utils/server/ui_env.test.mjs +57 -0
- package/scripts/utils/server/urls.mjs +100 -0
- package/scripts/utils/server/validate.mjs +60 -0
- package/scripts/utils/server/validate.test.mjs +76 -0
- package/scripts/utils/service/autostart_darwin.mjs +198 -0
- package/scripts/utils/service/autostart_darwin.test.mjs +49 -0
- package/scripts/utils/service/autostart_darwin_keepalive.test.mjs +19 -0
- package/scripts/utils/stack/cli_identities.mjs +29 -0
- package/scripts/utils/stack/context.mjs +19 -0
- package/scripts/utils/stack/dirs.mjs +26 -0
- package/scripts/utils/stack/editor_workspace.mjs +126 -0
- package/scripts/utils/stack/interactive_stack_config.mjs +266 -0
- package/scripts/utils/stack/interactive_stack_config.port_validation.test.mjs +93 -0
- package/scripts/utils/stack/interactive_stack_config.remote_validation.test.mjs +122 -0
- package/scripts/utils/stack/interactive_stack_config.stack_name_validation.test.mjs +76 -0
- package/scripts/utils/stack/interactive_stack_config_testkit.mjs +18 -0
- package/scripts/utils/stack/names.mjs +27 -0
- package/scripts/utils/stack/names.test.mjs +26 -0
- package/scripts/utils/stack/pr_stack_name.mjs +16 -0
- package/scripts/utils/stack/runtime_state.mjs +88 -0
- package/scripts/utils/stack/stacks.mjs +40 -0
- package/scripts/utils/stack/startup.mjs +370 -0
- package/scripts/utils/stack/startup_server_light_dirs.test.mjs +119 -0
- package/scripts/utils/stack/startup_server_light_generate.test.mjs +20 -0
- package/scripts/utils/stack/startup_server_light_legacy.test.mjs +79 -0
- package/scripts/utils/stack/startup_server_light_testkit.mjs +106 -0
- package/scripts/utils/stack/stop.mjs +284 -0
- package/scripts/utils/stack_context.mjs +1 -0
- package/scripts/utils/stack_runtime_state.mjs +1 -0
- package/scripts/utils/stacks.mjs +1 -0
- package/scripts/utils/tailscale/ip.mjs +116 -0
- package/scripts/utils/tauri/stack_overrides.mjs +22 -0
- package/scripts/utils/test/collect_test_files.mjs +29 -0
- package/scripts/utils/time/get_today_ymd.mjs +7 -0
- package/scripts/utils/tui/cleanup.mjs +38 -0
- package/scripts/utils/ui/ansi.mjs +47 -0
- package/scripts/utils/ui/browser.mjs +31 -0
- package/scripts/utils/ui/browser.test.mjs +56 -0
- package/scripts/utils/ui/clipboard.mjs +38 -0
- package/scripts/utils/ui/layout.mjs +44 -0
- package/scripts/utils/ui/qr.mjs +17 -0
- package/scripts/utils/ui/terminal_launcher.mjs +129 -0
- package/scripts/utils/ui/text.mjs +16 -0
- package/scripts/utils/update/auto_update_notice.mjs +93 -0
- package/scripts/utils/validate.mjs +5 -0
- package/scripts/where.mjs +138 -0
- package/scripts/worktrees.mjs +2174 -0
- package/scripts/worktrees_archive_cmd.integration.test.mjs +228 -0
- package/scripts/worktrees_cursor_monorepo_root.test.mjs +23 -0
- package/scripts/worktrees_list_specs_no_recurse.test.mjs +32 -0
- package/scripts/worktrees_monorepo_testkit.test.mjs +29 -0
- package/scripts/worktrees_monorepo_use_group.test.mjs +41 -0
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { chmod, mkdtemp, rm, writeFile } from 'node:fs/promises';
|
|
4
|
+
import { tmpdir } from 'node:os';
|
|
5
|
+
import { join } from 'node:path';
|
|
6
|
+
|
|
7
|
+
import { run } from '../proc/proc.mjs';
|
|
8
|
+
import { assertSafeRelativeRepoPath, getUncommittedOps } from './uncommitted_ops.mjs';
|
|
9
|
+
|
|
10
|
+
function gitEnv() {
|
|
11
|
+
const clean = {};
|
|
12
|
+
for (const [k, v] of Object.entries(process.env)) {
|
|
13
|
+
if (k.startsWith('HAPPIER_STACK_')) continue;
|
|
14
|
+
clean[k] = v;
|
|
15
|
+
}
|
|
16
|
+
return {
|
|
17
|
+
...clean,
|
|
18
|
+
GIT_AUTHOR_NAME: 'Test',
|
|
19
|
+
GIT_AUTHOR_EMAIL: 'test@example.com',
|
|
20
|
+
GIT_COMMITTER_NAME: 'Test',
|
|
21
|
+
GIT_COMMITTER_EMAIL: 'test@example.com',
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
test('getUncommittedOps includes modified, deleted, and untracked paths', async () => {
|
|
26
|
+
const repo = await mkdtemp(join(tmpdir(), 'happy-review-uncommitted-ops-'));
|
|
27
|
+
const env = gitEnv();
|
|
28
|
+
try {
|
|
29
|
+
await run('git', ['init', '-q'], { cwd: repo, env });
|
|
30
|
+
await run('git', ['checkout', '-q', '-b', 'main'], { cwd: repo, env });
|
|
31
|
+
|
|
32
|
+
await writeFile(join(repo, 'a.txt'), 'base-a\n', 'utf-8');
|
|
33
|
+
await writeFile(join(repo, 'b.txt'), 'base-b\n', 'utf-8');
|
|
34
|
+
await run('git', ['add', '.'], { cwd: repo, env });
|
|
35
|
+
await run('git', ['commit', '-q', '-m', 'chore: base'], { cwd: repo, env });
|
|
36
|
+
|
|
37
|
+
// Uncommitted changes: modify, delete, and add an untracked file.
|
|
38
|
+
await writeFile(join(repo, 'a.txt'), 'changed-a\n', 'utf-8');
|
|
39
|
+
await rm(join(repo, 'b.txt'));
|
|
40
|
+
await writeFile(join(repo, 'c.txt'), 'untracked-c\n', 'utf-8');
|
|
41
|
+
|
|
42
|
+
const ops = await getUncommittedOps({ cwd: repo, env });
|
|
43
|
+
assert.ok(ops.checkout.has('a.txt'));
|
|
44
|
+
assert.ok(ops.checkout.has('c.txt'));
|
|
45
|
+
assert.ok(ops.remove.has('b.txt'));
|
|
46
|
+
assert.equal(ops.all.size, 3);
|
|
47
|
+
} finally {
|
|
48
|
+
await rm(repo, { recursive: true, force: true });
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
test('getUncommittedOps handles repositories with no commits', async () => {
|
|
53
|
+
const repo = await mkdtemp(join(tmpdir(), 'happy-review-uncommitted-ops-empty-head-'));
|
|
54
|
+
const env = gitEnv();
|
|
55
|
+
try {
|
|
56
|
+
await run('git', ['init', '-q'], { cwd: repo, env });
|
|
57
|
+
await run('git', ['checkout', '-q', '-b', 'main'], { cwd: repo, env });
|
|
58
|
+
|
|
59
|
+
await writeFile(join(repo, 'first.txt'), 'hello\n', 'utf-8');
|
|
60
|
+
|
|
61
|
+
const ops = await getUncommittedOps({ cwd: repo, env });
|
|
62
|
+
assert.ok(ops.checkout.has('first.txt'));
|
|
63
|
+
assert.equal(ops.remove.size, 0);
|
|
64
|
+
assert.equal(ops.all.size, 1);
|
|
65
|
+
} finally {
|
|
66
|
+
await rm(repo, { recursive: true, force: true });
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
test('assertSafeRelativeRepoPath rejects traversal and absolute paths', () => {
|
|
71
|
+
assert.throws(() => assertSafeRelativeRepoPath('../outside'), /unsafe path/i);
|
|
72
|
+
assert.throws(() => assertSafeRelativeRepoPath('/etc/passwd'), /unsafe path/i);
|
|
73
|
+
assert.throws(() => assertSafeRelativeRepoPath('C:\\tmp\\x.txt'), /unsafe path/i);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
test('assertSafeRelativeRepoPath allows normalized nested relative paths', () => {
|
|
77
|
+
assert.equal(assertSafeRelativeRepoPath('apps/cli/src/index.ts'), 'apps/cli/src/index.ts');
|
|
78
|
+
assert.equal(assertSafeRelativeRepoPath('./apps/cli/../cli/src/index.ts'), 'apps/cli/src/index.ts');
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
test('getUncommittedOps rejects absolute paths from git output', async (t) => {
|
|
82
|
+
if (process.platform === 'win32') {
|
|
83
|
+
t.skip('POSIX executable shim required for fake git binary');
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const tmp = await mkdtemp(join(tmpdir(), 'happy-review-uncommitted-ops-abs-'));
|
|
88
|
+
try {
|
|
89
|
+
const fakeGit = join(tmp, 'git');
|
|
90
|
+
await writeFile(
|
|
91
|
+
fakeGit,
|
|
92
|
+
[
|
|
93
|
+
'#!/usr/bin/env node',
|
|
94
|
+
'const args = process.argv.slice(2);',
|
|
95
|
+
"if (args[0] === 'diff') {",
|
|
96
|
+
" process.stdout.write('M\\0/etc/passwd\\0');",
|
|
97
|
+
' process.exit(0);',
|
|
98
|
+
'}',
|
|
99
|
+
"if (args[0] === 'ls-files') {",
|
|
100
|
+
' process.exit(0);',
|
|
101
|
+
'}',
|
|
102
|
+
'process.exit(0);',
|
|
103
|
+
'',
|
|
104
|
+
].join('\n'),
|
|
105
|
+
'utf-8'
|
|
106
|
+
);
|
|
107
|
+
await chmod(fakeGit, 0o755);
|
|
108
|
+
|
|
109
|
+
const env = {
|
|
110
|
+
...process.env,
|
|
111
|
+
PATH: `${tmp}:${process.env.PATH ?? ''}`,
|
|
112
|
+
};
|
|
113
|
+
await assert.rejects(() => getUncommittedOps({ cwd: tmp, env }), /unsafe path/i);
|
|
114
|
+
} finally {
|
|
115
|
+
await rm(tmp, { recursive: true, force: true });
|
|
116
|
+
}
|
|
117
|
+
});
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
2
|
+
import { readFile, readdir, stat, writeFile } from 'node:fs/promises';
|
|
3
|
+
import { tmpdir } from 'node:os';
|
|
4
|
+
import { basename, join, resolve } from 'node:path';
|
|
5
|
+
|
|
6
|
+
export const REVIEW_PR_MARKER_FILENAME = '.happier-stack-sandbox-marker';
|
|
7
|
+
export const REVIEW_PR_META_FILENAME = '.happier-stack-review-pr.json';
|
|
8
|
+
|
|
9
|
+
export function reviewPrSandboxPrefixBase(baseStackName) {
|
|
10
|
+
const base = String(baseStackName ?? '').trim() || 'pr';
|
|
11
|
+
// Keep prefix stable for listing/reuse; mkdtemp adds a random suffix.
|
|
12
|
+
return `hstack-review-pr-${base}-`;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function reviewPrSandboxPrefixPath(baseStackName) {
|
|
16
|
+
return join(tmpdir(), reviewPrSandboxPrefixBase(baseStackName));
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
async function readJsonBestEffort(path) {
|
|
20
|
+
try {
|
|
21
|
+
const raw = await readFile(path, 'utf-8');
|
|
22
|
+
return JSON.parse(raw);
|
|
23
|
+
} catch {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export async function listReviewPrSandboxes({ baseStackName }) {
|
|
29
|
+
const prefixBase = reviewPrSandboxPrefixBase(baseStackName);
|
|
30
|
+
const root = tmpdir();
|
|
31
|
+
let entries = [];
|
|
32
|
+
try {
|
|
33
|
+
entries = await readdir(root, { withFileTypes: true });
|
|
34
|
+
} catch {
|
|
35
|
+
entries = [];
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const out = [];
|
|
39
|
+
for (const e of entries) {
|
|
40
|
+
if (!e.isDirectory()) continue;
|
|
41
|
+
if (!e.name.startsWith(prefixBase)) continue;
|
|
42
|
+
const dir = resolve(join(root, e.name));
|
|
43
|
+
const markerPath = join(dir, REVIEW_PR_MARKER_FILENAME);
|
|
44
|
+
if (!existsSync(markerPath)) continue;
|
|
45
|
+
|
|
46
|
+
let markerOk = false;
|
|
47
|
+
try {
|
|
48
|
+
const marker = await readFile(markerPath, 'utf-8');
|
|
49
|
+
markerOk = marker.trim().startsWith('review-pr');
|
|
50
|
+
} catch {
|
|
51
|
+
markerOk = false;
|
|
52
|
+
}
|
|
53
|
+
if (!markerOk) continue;
|
|
54
|
+
|
|
55
|
+
const metaPath = join(dir, REVIEW_PR_META_FILENAME);
|
|
56
|
+
const meta = await readJsonBestEffort(metaPath);
|
|
57
|
+
|
|
58
|
+
let mtimeMs = 0;
|
|
59
|
+
try {
|
|
60
|
+
mtimeMs = (await stat(dir)).mtimeMs;
|
|
61
|
+
} catch {
|
|
62
|
+
mtimeMs = 0;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
out.push({
|
|
66
|
+
dir,
|
|
67
|
+
name: basename(dir),
|
|
68
|
+
markerPath,
|
|
69
|
+
metaPath,
|
|
70
|
+
baseStackName: String(meta?.baseStackName ?? '').trim() || String(baseStackName ?? '').trim() || null,
|
|
71
|
+
stackName: String(meta?.stackName ?? '').trim() || null,
|
|
72
|
+
createdAtMs: typeof meta?.createdAtMs === 'number' ? meta.createdAtMs : null,
|
|
73
|
+
lastTouchedAtMs: Number.isFinite(mtimeMs) ? mtimeMs : null,
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
out.sort((a, b) => (b.lastTouchedAtMs ?? 0) - (a.lastTouchedAtMs ?? 0));
|
|
78
|
+
return out;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export async function writeReviewPrSandboxMeta({ sandboxDir, baseStackName, stackName, argv }) {
|
|
82
|
+
const dir = resolve(String(sandboxDir ?? '').trim());
|
|
83
|
+
const markerPath = join(dir, REVIEW_PR_MARKER_FILENAME);
|
|
84
|
+
const metaPath = join(dir, REVIEW_PR_META_FILENAME);
|
|
85
|
+
|
|
86
|
+
// Marker (for safe deletion) + meta (for reuse menu).
|
|
87
|
+
await writeFile(markerPath, 'review-pr\n', 'utf-8');
|
|
88
|
+
await writeFile(
|
|
89
|
+
metaPath,
|
|
90
|
+
JSON.stringify(
|
|
91
|
+
{
|
|
92
|
+
kind: 'review-pr',
|
|
93
|
+
createdAtMs: Date.now(),
|
|
94
|
+
baseStackName: String(baseStackName ?? '').trim() || null,
|
|
95
|
+
stackName: String(stackName ?? '').trim() || null,
|
|
96
|
+
argv: Array.isArray(argv) ? argv : null,
|
|
97
|
+
},
|
|
98
|
+
null,
|
|
99
|
+
2
|
|
100
|
+
) + '\n',
|
|
101
|
+
'utf-8'
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
return { markerPath, metaPath };
|
|
105
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { join } from 'node:path';
|
|
2
|
+
|
|
3
|
+
export function applyServerLightEnvDefaults({ baseEnv, serverEnv, baseDir }) {
|
|
4
|
+
const dataDir = baseEnv.HAPPIER_SERVER_LIGHT_DATA_DIR?.trim()
|
|
5
|
+
? baseEnv.HAPPIER_SERVER_LIGHT_DATA_DIR.trim()
|
|
6
|
+
: join(baseDir, 'server-light');
|
|
7
|
+
serverEnv.HAPPIER_SERVER_LIGHT_DATA_DIR = dataDir;
|
|
8
|
+
serverEnv.HAPPIER_SERVER_LIGHT_FILES_DIR = baseEnv.HAPPIER_SERVER_LIGHT_FILES_DIR?.trim()
|
|
9
|
+
? baseEnv.HAPPIER_SERVER_LIGHT_FILES_DIR.trim()
|
|
10
|
+
: join(dataDir, 'files');
|
|
11
|
+
serverEnv.HAPPIER_SERVER_LIGHT_DB_DIR = baseEnv.HAPPIER_SERVER_LIGHT_DB_DIR?.trim()
|
|
12
|
+
? baseEnv.HAPPIER_SERVER_LIGHT_DB_DIR.trim()
|
|
13
|
+
: join(dataDir, 'pglite');
|
|
14
|
+
}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { pathToFileURL } from 'node:url';
|
|
4
|
+
|
|
5
|
+
function readScripts(serverDir) {
|
|
6
|
+
try {
|
|
7
|
+
const pkgPath = join(serverDir, 'package.json');
|
|
8
|
+
const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
|
|
9
|
+
const scripts = pkg?.scripts && typeof pkg.scripts === 'object' ? pkg.scripts : {};
|
|
10
|
+
return scripts;
|
|
11
|
+
} catch {
|
|
12
|
+
return {};
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function hasScript(scripts, name) {
|
|
17
|
+
return typeof scripts?.[name] === 'string' && scripts[name].trim().length > 0;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function detectServerLightDbFlavor({ serverDir }) {
|
|
21
|
+
const scripts = readScripts(serverDir);
|
|
22
|
+
if (hasScript(scripts, 'migrate:light:deploy')) {
|
|
23
|
+
// Current Happier "light" flavor uses embedded Postgres via PGlite.
|
|
24
|
+
return 'pglite';
|
|
25
|
+
}
|
|
26
|
+
// Legacy SQLite-era layouts (kept for old checkouts and potential future reintroduction).
|
|
27
|
+
if (existsSync(join(serverDir, 'prisma', 'sqlite', 'schema.prisma'))) return 'sqlite';
|
|
28
|
+
if (existsSync(join(serverDir, 'prisma', 'schema.sqlite.prisma'))) return 'sqlite';
|
|
29
|
+
return 'unknown';
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function isUnifiedHappyServerLight({ serverDir }) {
|
|
33
|
+
// "Unified" means the monorepo server package supports a first-class light flavor.
|
|
34
|
+
// Today that is PGlite-based.
|
|
35
|
+
return detectServerLightDbFlavor({ serverDir }) === 'pglite';
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function resolveServerLightPrismaSchemaArgs({ serverDir }) {
|
|
39
|
+
const flavor = detectServerLightDbFlavor({ serverDir });
|
|
40
|
+
if (flavor === 'pglite') {
|
|
41
|
+
// Light flavor uses the main Postgres schema.
|
|
42
|
+
return ['--schema', 'prisma/schema.prisma'];
|
|
43
|
+
}
|
|
44
|
+
if (flavor === 'sqlite') {
|
|
45
|
+
if (existsSync(join(serverDir, 'prisma', 'sqlite', 'schema.prisma'))) {
|
|
46
|
+
return ['--schema', 'prisma/sqlite/schema.prisma'];
|
|
47
|
+
}
|
|
48
|
+
if (existsSync(join(serverDir, 'prisma', 'schema.sqlite.prisma'))) {
|
|
49
|
+
return ['--schema', 'prisma/schema.sqlite.prisma'];
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return [];
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function resolveServerLightPrismaMigrateDeployArgs({ serverDir }) {
|
|
56
|
+
return ['migrate', 'deploy', ...resolveServerLightPrismaSchemaArgs({ serverDir })];
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function resolveServerLightPrismaClientImport({ serverDir }) {
|
|
60
|
+
const flavor = detectServerLightDbFlavor({ serverDir });
|
|
61
|
+
if (flavor === 'pglite') {
|
|
62
|
+
// Light flavor uses the standard Postgres Prisma client (schema.prisma).
|
|
63
|
+
return '@prisma/client';
|
|
64
|
+
}
|
|
65
|
+
if (flavor === 'sqlite') {
|
|
66
|
+
const clientPath = join(serverDir, 'generated', 'sqlite-client', 'index.js');
|
|
67
|
+
return pathToFileURL(clientPath).href;
|
|
68
|
+
}
|
|
69
|
+
return '@prisma/client';
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function resolvePrismaClientImportForServerComponent({ serverComponentName, serverComponent, serverDir }) {
|
|
73
|
+
const name = serverComponentName ?? serverComponent;
|
|
74
|
+
if (name === 'happier-server-light') {
|
|
75
|
+
return resolveServerLightPrismaClientImport({ serverDir });
|
|
76
|
+
}
|
|
77
|
+
return '@prisma/client';
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function resolveGeneratedClientEntrypoint({ serverDir, provider }) {
|
|
81
|
+
const p = String(provider ?? '').trim().toLowerCase();
|
|
82
|
+
if (p === 'sqlite') {
|
|
83
|
+
return join(serverDir, 'generated', 'sqlite-client', 'index.js');
|
|
84
|
+
}
|
|
85
|
+
if (p === 'mysql') {
|
|
86
|
+
return join(serverDir, 'generated', 'mysql-client', 'index.js');
|
|
87
|
+
}
|
|
88
|
+
return '';
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export function resolvePrismaClientImportForDbProvider({ serverDir, provider }) {
|
|
92
|
+
const entry = resolveGeneratedClientEntrypoint({ serverDir, provider });
|
|
93
|
+
if (entry && existsSync(entry)) {
|
|
94
|
+
return pathToFileURL(entry).href;
|
|
95
|
+
}
|
|
96
|
+
// postgres + pglite share the default Prisma client.
|
|
97
|
+
return '@prisma/client';
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export function resolveServerDevScript({ serverComponentName, serverDir, prismaPush }) {
|
|
101
|
+
const scripts = readScripts(serverDir);
|
|
102
|
+
|
|
103
|
+
if (serverComponentName === 'happier-server') {
|
|
104
|
+
return 'start';
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (serverComponentName === 'happier-server-light') {
|
|
108
|
+
// Prefer the dedicated dev script that ensures migrations are applied before starting.
|
|
109
|
+
if (hasScript(scripts, 'dev:light')) {
|
|
110
|
+
return 'dev:light';
|
|
111
|
+
}
|
|
112
|
+
// Fallback: no dev script, run the light start script.
|
|
113
|
+
if (hasScript(scripts, 'start:light')) {
|
|
114
|
+
return 'start:light';
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Legacy behavior: prefer `dev` for older server-light checkouts.
|
|
118
|
+
if (prismaPush) {
|
|
119
|
+
return hasScript(scripts, 'dev') ? 'dev' : 'start';
|
|
120
|
+
}
|
|
121
|
+
return hasScript(scripts, 'start') ? 'start' : 'dev';
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Unknown: be conservative.
|
|
125
|
+
return 'start';
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export function resolveServerStartScript({ serverComponentName, serverDir }) {
|
|
129
|
+
const scripts = readScripts(serverDir);
|
|
130
|
+
|
|
131
|
+
if (serverComponentName === 'happier-server-light') {
|
|
132
|
+
if (hasScript(scripts, 'start:light')) {
|
|
133
|
+
return 'start:light';
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return 'start';
|
|
138
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { mkdtemp, mkdir, rm, writeFile } from 'node:fs/promises';
|
|
4
|
+
import { tmpdir } from 'node:os';
|
|
5
|
+
import { join } from 'node:path';
|
|
6
|
+
|
|
7
|
+
import {
|
|
8
|
+
resolvePrismaClientImportForServerComponent,
|
|
9
|
+
resolveServerDevScript,
|
|
10
|
+
resolveServerLightPrismaClientImport,
|
|
11
|
+
resolveServerLightPrismaMigrateDeployArgs,
|
|
12
|
+
resolveServerStartScript,
|
|
13
|
+
} from './flavor_scripts.mjs';
|
|
14
|
+
|
|
15
|
+
async function writeJson(path, obj) {
|
|
16
|
+
await writeFile(path, JSON.stringify(obj, null, 2) + '\n', 'utf-8');
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
async function withServerDir(run) {
|
|
20
|
+
const dir = await mkdtemp(join(tmpdir(), 'hs-flavor-scripts-'));
|
|
21
|
+
try {
|
|
22
|
+
await run(dir);
|
|
23
|
+
} finally {
|
|
24
|
+
await rm(dir, { recursive: true, force: true });
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async function writeServerScriptsPackageJson(dir, scripts) {
|
|
29
|
+
await writeJson(join(dir, 'package.json'), { scripts });
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
test('resolveServer*Script uses light scripts when unified light flavor is detected', async () => {
|
|
33
|
+
await withServerDir(async (dir) => {
|
|
34
|
+
await writeServerScriptsPackageJson(dir, {
|
|
35
|
+
'start:light': 'node x',
|
|
36
|
+
'dev:light': 'node y',
|
|
37
|
+
'migrate:light:deploy': 'node z',
|
|
38
|
+
});
|
|
39
|
+
assert.equal(resolveServerDevScript({ serverComponentName: 'happier-server-light', serverDir: dir, prismaPush: true }), 'dev:light');
|
|
40
|
+
assert.equal(resolveServerDevScript({ serverComponentName: 'happier-server-light', serverDir: dir, prismaPush: false }), 'dev:light');
|
|
41
|
+
assert.equal(resolveServerStartScript({ serverComponentName: 'happier-server-light', serverDir: dir }), 'start:light');
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
test('resolveServer*Script falls back to legacy scripts for non-unified happier-server-light', async () => {
|
|
46
|
+
await withServerDir(async (dir) => {
|
|
47
|
+
await writeServerScriptsPackageJson(dir, { start: 'node start', dev: 'node dev' });
|
|
48
|
+
assert.equal(resolveServerDevScript({ serverComponentName: 'happier-server-light', serverDir: dir, prismaPush: true }), 'dev');
|
|
49
|
+
assert.equal(resolveServerDevScript({ serverComponentName: 'happier-server-light', serverDir: dir, prismaPush: false }), 'start');
|
|
50
|
+
assert.equal(resolveServerStartScript({ serverComponentName: 'happier-server-light', serverDir: dir }), 'start');
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
test('resolveServer*Script returns start for happier-server', async () => {
|
|
55
|
+
await withServerDir(async (dir) => {
|
|
56
|
+
await writeServerScriptsPackageJson(dir, { start: 'node start', dev: 'node dev' });
|
|
57
|
+
assert.equal(resolveServerDevScript({ serverComponentName: 'happier-server', serverDir: dir, prismaPush: true }), 'start');
|
|
58
|
+
assert.equal(resolveServerDevScript({ serverComponentName: 'happier-server', serverDir: dir, prismaPush: false }), 'start');
|
|
59
|
+
assert.equal(resolveServerStartScript({ serverComponentName: 'happier-server', serverDir: dir }), 'start');
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
test('resolveServerLightPrismaMigrateDeployArgs adds --schema when unified light flavor is detected', async () => {
|
|
64
|
+
await withServerDir(async (dir) => {
|
|
65
|
+
await writeServerScriptsPackageJson(dir, { 'migrate:light:deploy': 'node z' });
|
|
66
|
+
assert.deepEqual(resolveServerLightPrismaMigrateDeployArgs({ serverDir: dir }), ['migrate', 'deploy', '--schema', 'prisma/schema.prisma']);
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
test('resolveServerLightPrismaMigrateDeployArgs supports legacy schema.sqlite.prisma', async () => {
|
|
71
|
+
await withServerDir(async (dir) => {
|
|
72
|
+
await mkdir(join(dir, 'prisma'), { recursive: true });
|
|
73
|
+
await writeFile(join(dir, 'prisma', 'schema.sqlite.prisma'), 'datasource db { provider = "sqlite" }\n', 'utf-8');
|
|
74
|
+
|
|
75
|
+
assert.deepEqual(resolveServerLightPrismaMigrateDeployArgs({ serverDir: dir }), ['migrate', 'deploy', '--schema', 'prisma/schema.sqlite.prisma']);
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
test('resolveServerLightPrismaClientImport returns @prisma/client for pglite light flavor', async () => {
|
|
80
|
+
await withServerDir(async (dir) => {
|
|
81
|
+
await writeServerScriptsPackageJson(dir, { 'migrate:light:deploy': 'node z' });
|
|
82
|
+
assert.equal(resolveServerLightPrismaClientImport({ serverDir: dir }), '@prisma/client');
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
test('resolveServerLightPrismaClientImport returns file URL for sqlite light flavor (legacy)', async () => {
|
|
87
|
+
await withServerDir(async (dir) => {
|
|
88
|
+
await mkdir(join(dir, 'prisma', 'sqlite'), { recursive: true });
|
|
89
|
+
await writeFile(join(dir, 'prisma', 'sqlite', 'schema.prisma'), 'datasource db { provider = "sqlite" }\n', 'utf-8');
|
|
90
|
+
const spec = resolveServerLightPrismaClientImport({ serverDir: dir });
|
|
91
|
+
assert.equal(typeof spec, 'string');
|
|
92
|
+
assert.ok(spec.startsWith('file:'), `expected file: URL import spec, got: ${spec}`);
|
|
93
|
+
assert.ok(spec.endsWith('/generated/sqlite-client/index.js'), `unexpected import spec: ${spec}`);
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
test('resolvePrismaClientImportForServerComponent returns @prisma/client for pglite server-light', async () => {
|
|
98
|
+
await withServerDir(async (dir) => {
|
|
99
|
+
await writeServerScriptsPackageJson(dir, { 'migrate:light:deploy': 'node z' });
|
|
100
|
+
assert.equal(resolvePrismaClientImportForServerComponent({ serverComponentName: 'happier-server-light', serverDir: dir }), '@prisma/client');
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
test('resolvePrismaClientImportForServerComponent accepts serverComponent alias (back-compat)', async () => {
|
|
105
|
+
await withServerDir(async (dir) => {
|
|
106
|
+
await writeServerScriptsPackageJson(dir, { 'migrate:light:deploy': 'node z' });
|
|
107
|
+
assert.equal(resolvePrismaClientImportForServerComponent({ serverComponent: 'happier-server-light', serverDir: dir }), '@prisma/client');
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
test('resolvePrismaClientImportForServerComponent returns @prisma/client for happier-server', async () => {
|
|
112
|
+
await withServerDir(async (dir) => {
|
|
113
|
+
assert.equal(resolvePrismaClientImportForServerComponent({ serverComponentName: 'happier-server', serverDir: dir }), '@prisma/client');
|
|
114
|
+
});
|
|
115
|
+
});
|